Victoria 3

Victoria 3

Profiling-based Optimization (Performance Mod)
Brute  [developer] 26 Jun, 2024 @ 8:37am
A full list of changes
I decided to move the full changelog to the discussion board. After the release of the 1.7 version, all of the optimizations that I have made so far seem to have been incorporated into the base game, which means that they now serve only as a historical document lol. Anyway, I will keep looking into new things to optimize, and I will add new things here.

---------------------
(Initial changes)

1. Removed the constant checking of flag definitions for default countries:
Come on, while we all agree that dynamic country flags are brilliant, their performance cost is ridiculous. In essence, the game is looking at every country in every tick to see if they qualify for a different flag at that moment. This is the script item that contributed the most to early game lag and it continues to play a role throughout the game. Even though disabling this means that some countries might have funky flags (most notably colonial administrations), I would rather my game run smoother than having dynamic flags per se. I might change it to a more sophisticated method if there's a way to only refresh the flag when needed (but it seems like the engine doesn't permit that)

2. Changed the calculation for 'Red Scare' Journal Entry's trigger to use a less costly one:
Previously, the is_shown_when_inactive uses this trigger:
any_country = {
has_diplomatic_relevance = root
has_technology_researched = labor_movement
}
This is kind of stupid. What this means is that whenever the journal entry system refreshes, it will iterate over all countries to see if anyone of them has diplomatic_relevance and also researched the tech labor movement...just so that this journal entry can be DISPLAYED as inactive.
I changed it so that when the labor movement tech is researched, a global variable will be set indicating that someone has researched it. Since this trigger only affects whether this journal entry will be displayed at all, not whether it will actually become active, there's hardly any change to the gameplay.

---------------------

(4/8)Update: I have added two other optimizations to this mod, which were a bit more sophisticated yet will likely not produce as great of an effect as the initial two opimizations.

3. Change the AI calculation of desired army size bonus for small country.
Previously, AI desired army size will increase by 1 for every 100000 incorporated population, with the increase capped at max 10. I changed it so that if the country has more than a million incorporated population, the max directly applies without the need to calculate a division. Not going to be a big deal I suppose but still an improvement.

4. Rehauled the `Red Scare` Journal Entry's trigger altogether.
I am still not satisfied with the monstrous performance cost of this journal entry even after my initial fix, and I decided to rewrite the logic altogether.
Previously, whenever the journal entry system refreshes, anyone with inactive entry will iterate over every other country and see if there's one who has diplomatic relevance and council republic. This is very costly as journal entries are refreshed quite often.
The new code uses a yearly pulse event to keep log of every council republic in existence. In the `Red Scare` Journal Entry, the country will only iterate over the cached list of existing council republics and see if there's anyone that has diplomatic relevance (plus being more than a minor power, which is suggested by the tooltip but was not actually implemented by the vanilla code?) This results in drastically reduced frequency for iterating over all countries when checking for council republics, and should result in noticeable improvement once a few years have elapsed (at which point labor_movement will have been researched and the journal enabled). A quick test suggests that council republics should still correctly activate this journal entry, thus there will be no gameplay impact at all.

5. Change the trigger for 'Natural Borders of France' Journal Entry
Apparently Victoria 3 programmers forgot the fact that they offer this trigger called any_subject_or_below, which is much cheaper than iterating over every country and see if they are a subject of France. Fortunately only France got to enjoy this privilege so the performance impact is not as huge as say 'Red Scare', but still appreciable.
< >
Showing 1-2 of 2 comments
Brute  [developer] 29 Jun, 2024 @ 4:43am 
(6/29) Reorder the `potential` conditions for all character interactions to make short-circuiting more often.

In programming, short-circuiting a boolean evaluation means that no further checks will be made as soon as the result is obvious. Specifically in our case, when a list of conditions must all be satisfied, as soon as we encounter one condition that's not satisfied, we should not even bother with the following ones.

While there's no official documentation to prove that this behavior actually exists for Paradox's scripting, profiling observations seem to suggest that it does. Most important, because the conditions in `potential` must all be met for the interaction/decision/etc to even appear, and the conditions are not shown to the users, they serve as the best target for this optimization.

Why are we focusing character interactions? Well, the game is populated with hundreds of different characters and nations, and each nations will have different set of actions that can or cannot be done on any character (You can't mind control other countries' monarchs to abdicate, for example). To support this, the frequency for updating the interactions seems very high, such that around a million calls will be made to each character interactions each year. The condition checks themselves are rather low-cost, but the cumulative effect is still appreciable.

How exactly do I take advantage of short-circuiting? I did a one year test profiling session to see how effective each individual condition serves to rule out possibilities. Intuitively, we know that the likelihood for a character to be ruler or heir is much lower than the likelihood of them being a politician. This means that if we put the condition "is ruler or heir" first, there will be fewer cases where the following conditions must be checked. This is also where vanilla code is under-optimized: for example, conditions such as "not in exile" and "not in void" tend to be placed at the beginning, even though we know that pretty much most of the characters will pass these checks.

Some caveats must be noted: 1) I only did a one-year profiling test session for these individual conditions, and it's likely that their costs will continue to evolve as the game progresses 2) We are assuming that each condition is independent from one another, but there's not feasible course of action to assume otherwise 3) Some conditions have very context-dependent costs: checks over the possession of dlc, for example, and some conditions' cost will change significantly, for example whether the character's primary culture has researched nationalism.

With all these in mind, there should still be a small but significant cumulative effect from such a re-ordering, assuming that short-circuiting actually exists (please do otherwise this scripting language becomes a sea of insanity compounded with all those esoteric knowledge that you must master)
Brute  [developer] 2 Jul, 2024 @ 5:43am 
(7/2) Rationalize the calculation for pop qualification

The vanilla code is not very optimized in this area, as there are a lot of unnecessary and duplicate checks. First of all, peasants under serfdom is always checked twice: the first time alongside a literacy condition, the second time on its own. By simply checking it upfront and using else_if, we can remove the second check altogether, since reaching the second branch implies that the first branch is not passed

Furthermore, the code does not use if and else_if when checking favored pop types and peasants under serfdom. It's clear that if you belong to the favored pop type you cannot be a peasant under serfdom, thus there are some unnecessary checks.

Finally, each of the favored type's qualification multiplier is listed on its own, even when many of them have the same modifier value. By merging them and using OR, we can short-circuit and prevent unnecessary checks.
< >
Showing 1-2 of 2 comments
Per page: 1530 50