Factorio - Klonan
Hello, it seems the summer heat has finally subsided, and we have not had to run our AC units the whole week. We mentioned earlier we have had Dominik in for a testing week, and we are happy to say that he is quite qualified for a position here, so will be remaining with us. Tom has also joined us, moving here from the Republic of Ireland, and has been getting settled in working through a lot of the small unassigned tasks. It also seems most of us are back from our vacations, so the pace is picking up again.

Most of the work this week has been on the unentertaining sepctrum, with a lot of internal reworking and refactoring going on. A lot commits related to fixing our compilation after the move to C++17. Many of the GUI and input action functions were broken (such as rebinding keys, switch map editor tabs, setting combinator filters), so its been a team effort to fix these as they are found. Hopefully not many will slip through the cracks and into release.

Lua noise specification
As mentioned in an earlier FFF,one of the planned features for 0.16 is to allow composition of noise functionsfrom Lua code, so that we (and modders) can have more control over how the map is generated.Over the past several weeks I've been getting that workingand playing around with it trying to make a world that has a lot of variationas you explore far away, but is not too crazy near the starting area.

Sometimes it can be hard to visualise what effect changing the noise will have on the resulting map. To give a somewhat more intuitive feel to how the 'elevation' of the noise is affected, I added height shading to the preview. While only used in the game to place water, showing it in this way really helps to see the underlying structure of the noise.



One of the problems I've been tackling is how to allow large bodies of waterwhile ensuring that the player never starts on a tiny island.One way to do this is to add in a web-like structure near the starting area.This can be done by taking some medium-frequency noise with a positive bias(to ensure that there are land bridges to everywhere)but folded back down above a certain elevation to add lakes,subtracting distance from the starting area from that,and then taking the maximum of that and the general world elevation noise.

As a simple example, the Lua code to do create such a noise function looks like:
data:extend{ { type = "noise-expression", name = "starting-area-webbing", expression = noise.define_noise_function( function(x,y,tile,map) reset_seed(map.seed) local continents = new_basis_noise(x,y,32,1/512) local webbing = noise.ridge(new_basis_noise(x,y,16,1/64), -math.huge, 8) return noise.max(continents, webbing - tile.tier) end) } }
The result looks like this in the previewer:



Mod portal 2.0 progress
Over the summer, we have had a French student Lucien working with us in the office. As a part of his internship, he was helping us develop the new version of the mod portal. It took a long time, as I (Martin) had many other things to work on, and couldn't fully dedicate myself to finishing this long-overdue project, but we are nearing the end. We're hoping to release the new version this month.

Apart from speed and performance improvements, there will probably not be many more changes on release, but we'll start working on new features right away. This new version gives us a much more solid foundation to work from. You can let us know on our Mod portal discussion sub-forum which features/improvements are the most important to you, and there will proobably make a poll later on to see which we should focus on.

As always, let us know what you think on our forum.
Factorio - Klonan
Hello, it seems the summer heat has finally subsided, and we have not had to run our AC units the whole week. We mentioned earlier we have had Dominik in for a testing week, and we are happy to say that he is quite qualified for a position here, so will be remaining with us. Tom has also joined us, moving here from the Republic of Ireland, and has been getting settled in working through a lot of the small unassigned tasks. It also seems most of us are back from our vacations, so the pace is picking up again.

Most of the work this week has been on the unentertaining sepctrum, with a lot of internal reworking and refactoring going on. A lot commits related to fixing our compilation after the move to C++17. Many of the GUI and input action functions were broken (such as rebinding keys, switch map editor tabs, setting combinator filters), so its been a team effort to fix these as they are found. Hopefully not many will slip through the cracks and into release.

Lua noise specification
As mentioned in an earlier FFF,one of the planned features for 0.16 is to allow composition of noise functionsfrom Lua code, so that we (and modders) can have more control over how the map is generated.Over the past several weeks I've been getting that workingand playing around with it trying to make a world that has a lot of variationas you explore far away, but is not too crazy near the starting area.

Sometimes it can be hard to visualise what effect changing the noise will have on the resulting map. To give a somewhat more intuitive feel to how the 'elevation' of the noise is affected, I added height shading to the preview. While only used in the game to place water, showing it in this way really helps to see the underlying structure of the noise.



One of the problems I've been tackling is how to allow large bodies of waterwhile ensuring that the player never starts on a tiny island.One way to do this is to add in a web-like structure near the starting area.This can be done by taking some medium-frequency noise with a positive bias(to ensure that there are land bridges to everywhere)but folded back down above a certain elevation to add lakes,subtracting distance from the starting area from that,and then taking the maximum of that and the general world elevation noise.

As a simple example, the Lua code to do create such a noise function looks like:
data:extend{ { type = "noise-expression", name = "starting-area-webbing", expression = noise.define_noise_function( function(x,y,tile,map) reset_seed(map.seed) local continents = new_basis_noise(x,y,32,1/512) local webbing = noise.ridge(new_basis_noise(x,y,16,1/64), -math.huge, 8) return noise.max(continents, webbing - tile.tier) end) } }
The result looks like this in the previewer:



Mod portal 2.0 progress
Over the summer, we have had a French student Lucien working with us in the office. As a part of his internship, he was helping us develop the new version of the mod portal. It took a long time, as I (Martin) had many other things to work on, and couldn't fully dedicate myself to finishing this long-overdue project, but we are nearing the end. We're hoping to release the new version this month.

Apart from speed and performance improvements, there will probably not be many more changes on release, but we'll start working on new features right away. This new version gives us a much more solid foundation to work from. You can let us know on our Mod portal discussion sub-forum which features/improvements are the most important to you, and there will proobably make a poll later on to see which we should focus on.

As always, let us know what you think on our forum.
Factorio - Klonan
Hello Factorio players, lets start our developer to player interaction right now! :)

Belt fast replace improvements
Dominik is our new programmer in for a testing period. He was given the task to add support for fast-replacing splitters and belts in a reasonable way, and he ended up extending the functionality a little.

Adding a belt junction:



Removing it:



Adding underground part:



Removing it:



It is something I wanted in the game for a long time!

Compile time optimisations - Technical
Rseding91 explained in the FFF-201 how important it is for us to not only optimize the game for you the players, but also for us, so we can speed up our change->compile->run/test cycle.

Step 1 - Hardware

When I returned from my holiday this Friday, I noticed that the recompile time of Factorio on debug mode took almost 5 minutes, which is more than I was used to. We checked the CPU temperatures and memory speeds, and they are okay, but the compile time was still double of what others have on similar hardware. It turned out that it is most probably caused by slow SSD speed, which could be caused by writing a huge amount of data when working. From what I found on the internet, the typical SSD write capacity is something around 1TB (EDIT: I meant 1000TB) of data, which is not so hard to approach if one recompilation cycle of Factorio generates 5GB of data. It is quite possible, that the drive just gets slower as it approaches its limit. Actually, the lifespan my specific SSD is typically a year or less. Luckily, our new computers with sweet i9-7900X CPU were ready to be assembled at this time, and I'm planning to get enough memory to compile on ramdisk to prevent this problem in the future.

Step 2 - Hardware updated

So, after half a day of fiddling, I installed everything on a brand new computer so I could test the compilation speed. Discovering that it still took 2 minutes was not really satisfying.

Step 3 - Getting rid of boost

Boost is a special kind of demon. It lures you in by giving you all these cool and simple to use features, and then it beats your soul from you by increasing compilation times absurdly. There are two main problems. Problem one is that they don't care much about compile times and two, they want to have everything nice and generic ad absurdum, and they even defend it as the correct style. The result is, that changing boost::mpl::vector66 to std::variant can improve the compile time from 1:44 to 1:20 and getting rid of templates completely by using unions can decrease the compile time to 0:53. I'm talking about changing 2 headers of 2 classes in a project with 3390 files, 410k lines of code and 15Mb of source code. Everything that was compiled to Factorio, GUI, graphics library, networking, entity logic, scripting, modding, logistic system... all these things together took the same time to compile as two instances of boost::mpl::vector. Our current goal is to get rid of the boost library completely.

The conclusion is that moving from 5 minutes compilation times to sub 1 minute in one week feels good, and it is worth the trouble to improve it from time to time (Until a better language for our purpose is invented, which Jai could be someday).

Test runtime optimisations
Another thing that was done to improve the speed of our change->compile->run/test cycle was the improvement of the automated test run speed. Rseding91 made a great feature that runs the tests in parallel, which is especially useful with our new 20 thread systems.

Debug mode Standard: 270.23 seconds. Fork: 45.64 seconds. Difference: 5.92 times faster Release mode Standard: 58.94 seconds. Fork: 17.56 seconds. Difference: 3.35 times faster (limited by slowest suite) Heavy mode Standard: 7456.27 seconds Fork: 877.82 seconds Difference: 8.49 times faster (limited by slowest suite)

High res pipe entities
As pipes and pumps are high resolution already, it only makes sense to upgrade the entities that tile with that, such as storage tanks and pumpjacks.



As always, let us know your thoughts, ideas and feedback on our forum.
Factorio - Klonan
Hello Factorio players, lets start our developer to player interaction right now! :)

Belt fast replace improvements
Dominik is our new programmer in for a testing period. He was given the task to add support for fast-replacing splitters and belts in a reasonable way, and he ended up extending the functionality a little.

Adding a belt junction:



Removing it:



Adding underground part:



Removing it:



It is something I wanted in the game for a long time!

Compile time optimisations - Technical
Rseding91 explained in the FFF-201 how important it is for us to not only optimize the game for you the players, but also for us, so we can speed up our change->compile->run/test cycle.

Step 1 - Hardware

When I returned from my holiday this Friday, I noticed that the recompile time of Factorio on debug mode took almost 5 minutes, which is more than I was used to. We checked the CPU temperatures and memory speeds, and they are okay, but the compile time was still double of what others have on similar hardware. It turned out that it is most probably caused by slow SSD speed, which could be caused by writing a huge amount of data when working. From what I found on the internet, the typical SSD write capacity is something around 1TB (EDIT: I meant 1000TB) of data, which is not so hard to approach if one recompilation cycle of Factorio generates 5GB of data. It is quite possible, that the drive just gets slower as it approaches its limit. Actually, the lifespan my specific SSD is typically a year or less. Luckily, our new computers with sweet i9-7900X CPU were ready to be assembled at this time, and I'm planning to get enough memory to compile on ramdisk to prevent this problem in the future.

Step 2 - Hardware updated

So, after half a day of fiddling, I installed everything on a brand new computer so I could test the compilation speed. Discovering that it still took 2 minutes was not really satisfying.

Step 3 - Getting rid of boost

Boost is a special kind of demon. It lures you in by giving you all these cool and simple to use features, and then it beats your soul from you by increasing compilation times absurdly. There are two main problems. Problem one is that they don't care much about compile times and two, they want to have everything nice and generic ad absurdum, and they even defend it as the correct style. The result is, that changing boost::mpl::vector66 to std::variant can improve the compile time from 1:44 to 1:20 and getting rid of templates completely by using unions can decrease the compile time to 0:53. I'm talking about changing 2 headers of 2 classes in a project with 3390 files, 410k lines of code and 15Mb of source code. Everything that was compiled to Factorio, GUI, graphics library, networking, entity logic, scripting, modding, logistic system... all these things together took the same time to compile as two instances of boost::mpl::vector. Our current goal is to get rid of the boost library completely.

The conclusion is that moving from 5 minutes compilation times to sub 1 minute in one week feels good, and it is worth the trouble to improve it from time to time (Until a better language for our purpose is invented, which Jai could be someday).

Test runtime optimisations
Another thing that was done to improve the speed of our change->compile->run/test cycle was the improvement of the automated test run speed. Rseding91 made a great feature that runs the tests in parallel, which is especially useful with our new 20 thread systems.

Debug mode Standard: 270.23 seconds. Fork: 45.64 seconds. Difference: 5.92 times faster Release mode Standard: 58.94 seconds. Fork: 17.56 seconds. Difference: 3.35 times faster (limited by slowest suite) Heavy mode Standard: 7456.27 seconds Fork: 877.82 seconds Difference: 8.49 times faster (limited by slowest suite)

High res pipe entities
As pipes and pumps are high resolution already, it only makes sense to upgrade the entities that tile with that, such as storage tanks and pumpjacks.



As always, let us know your thoughts, ideas and feedback on our forum.
Factorio - Klonan
Hello, it's vacation season here in the office, with a lot of the team taking some time off. We just released what will probably be the last version of 0.15, so now is the best time for everyone to take a breather.

Armor & equipment
I've been working on the next set of mini-tutorial in preparation for 0.16. I recently finished the last logistic bot tutorial, when I pondered what needs to be explained next. My thinking is that the complexities of the game lay in the 'high concept' areas, train systems, circuit network, logistics etc. Everything below this is just so simple and obvious, we can just assume the player will learn it on their own.

So with my brainstorming not turning up any good candidates, I asked Jitka (our office administrator) what aspects of the game she struggled with. One thing she mentioned was the whole equipment section of the crafting menu. She said "I don't know what it is, so I have never touched it". It gave me the realisation that to an untrained eye, equipment modules are just random items put in your crafting section.

With this new perspective, I started to see the lack of information related to this topic. The most glaring omission is in the item tooltip itself. It doesn't say "I am equipment, put me in armor" or "I am armor which can accept equipment modules". The issue of mods adding equipment grids to cars and trains somewhat complicated the issue, but in a short time Rseding had a working version of the feature running smoothly.



With this information more clearly available to the player, and the mini-tutorial explaining how all of it works, I am confident that no player will ever see these items and say "I don't know what they do".

Mod dependencies


This is another one that is just 'obvious' to us. A dependency with a question mark in orange is optional. What does optional mean? Well its obvious right? Optional means it isn't required to run the mod.

Once again, the problem is that we don't communicate any of this to the player, and it isn't so hard to explain:



Its been quite interesting to look through the game with the lens of "Do we ever explain this?", and it will certainly help me as I write more mini-tutorials on all different aspects of the game. If you have had any of these experiences in the game or know of any of these 'unexplained' areas, please let us know.

HR radar
Another dose of high resolution anticipation.



As always, let us know your thoughts, ideas and feedback on our forum.
Factorio - Klonan
Hello, it's vacation season here in the office, with a lot of the team taking some time off. We just released what will probably be the last version of 0.15, so now is the best time for everyone to take a breather.

Armor & equipment
I've been working on the next set of mini-tutorial in preparation for 0.16. I recently finished the last logistic bot tutorial, when I pondered what needs to be explained next. My thinking is that the complexities of the game lay in the 'high concept' areas, train systems, circuit network, logistics etc. Everything below this is just so simple and obvious, we can just assume the player will learn it on their own.

So with my brainstorming not turning up any good candidates, I asked Jitka (our office administrator) what aspects of the game she struggled with. One thing she mentioned was the whole equipment section of the crafting menu. She said "I don't know what it is, so I have never touched it". It gave me the realisation that to an untrained eye, equipment modules are just random items put in your crafting section.

With this new perspective, I started to see the lack of information related to this topic. The most glaring omission is in the item tooltip itself. It doesn't say "I am equipment, put me in armor" or "I am armor which can accept equipment modules". The issue of mods adding equipment grids to cars and trains somewhat complicated the issue, but in a short time Rseding had a working version of the feature running smoothly.



With this information more clearly available to the player, and the mini-tutorial explaining how all of it works, I am confident that no player will ever see these items and say "I don't know what they do".

Mod dependencies


This is another one that is just 'obvious' to us. A dependency with a question mark in orange is optional. What does optional mean? Well its obvious right? Optional means it isn't required to run the mod.

Once again, the problem is that we don't communicate any of this to the player, and it isn't so hard to explain:



Its been quite interesting to look through the game with the lens of "Do we ever explain this?", and it will certainly help me as I write more mini-tutorials on all different aspects of the game. If you have had any of these experiences in the game or know of any of these 'unexplained' areas, please let us know.

HR radar
Another dose of high resolution anticipation.



As always, let us know your thoughts, ideas and feedback on our forum.
Factorio - HanziQ
Bugfixes
  • Fixed that after a player reconnected after a desync, their blueprints would no longer upload. more
  • Fixed that it was possible to modify other players' blueprint libraries. more
  • Fixed a crash when loading a save that was transferring blueprints to a now offline player. more
  • Fixed that the blueprint library would remove duplicate blueprints even though they were in different books. more
  • Fixed game freezing when clicking the decrease replay speed button. more
  • Disabled possibility to open invalid save/replay by enter key or double click.
  • Fixed rare crash when being disconnected from multiplayer. more
  • Fixed creating map from scenario would copy also system and hidden files from scenario folder. more
  • Fixed threading issue causing random server crashes. more
  • Fixed that if the server was launched with --start-server-load-scenario, the /save command with no name would cause the server to hang. more
  • Fixed --start-server-load-scenario would ignore --map-gen-settings, --map-settings and --preset options. more
  • Fixed disabling shaders would cause crashes. more


You can get experimental releases by selecting the 'experimental' beta branch under Factorio's properties in Steam.
Factorio - HanziQ
Bugfixes
  • Fixed that after a player reconnected after a desync, their blueprints would no longer upload. more
  • Fixed that it was possible to modify other players' blueprint libraries. more
  • Fixed a crash when loading a save that was transferring blueprints to a now offline player. more
  • Fixed that the blueprint library would remove duplicate blueprints even though they were in different books. more
  • Fixed game freezing when clicking the decrease replay speed button. more
  • Disabled possibility to open invalid save/replay by enter key or double click.
  • Fixed rare crash when being disconnected from multiplayer. more
  • Fixed creating map from scenario would copy also system and hidden files from scenario folder. more
  • Fixed threading issue causing random server crashes. more
  • Fixed that if the server was launched with --start-server-load-scenario, the /save command with no name would cause the server to hang. more
  • Fixed --start-server-load-scenario would ignore --map-gen-settings, --map-settings and --preset options. more
  • Fixed disabling shaders would cause crashes. more


You can get experimental releases by selecting the 'experimental' beta branch under Factorio's properties in Steam.
Factorio - Klonan
Hello,as the team is getting slowly bigger and we still don't have any dedicated project manager, we had to start looking for tools to help us manage the team. We are testing software that allows our team members to track time spent on individual tasks, so right now my timer on "Friday facts related work" is running. I hope it to give me better insight into what kind of tasks our time goes to, where are we losing most of it, or what were the people doing when I was not here. People tend to not like these kind of changes, but we just have to admit that we are not the 4 people punk development team working from our living room and we need to invest more time into working efficiently.

Prefetching (Technical)
Kovarex already presented a concise summary of the prefetching patch, here is some more background and dirty technical details.

I started to look into Factorio performance improvements a while back, more specifically UPS (updates per second) improvements for large bases. It is widely recognized that the UPS are mostly limited by memory performance (more). That is normal - even highly optimized scientific simulation codes are rarely limited by arithmetic instructions.

At first, I looked into ways to reduce the size of Entities. Common entity sizes like Inserter (536 bytes) or AssemblingMachine (648 bytes) seem surprisingly large at first. I tried some changes, e.g. moving less frequently accessed data out of the actual entity in a separate object in memory. These changes had significant impact to the code in many files, but just saving a few bytes didn't make a measurable impact to performance.

Back to a bit of theory - there are two different ways in which memory can become a bottleneck: bandwidth (the amount of data supplied over time, e.g. 50 GB/s) and latency (the time until a requested piece of data is available, e.g. 50 ns). Comparing the results for different RAM timing settings (CAS latency) shows, that latency has a significant impact. It is important to note, that Factorio is not a homogeneous workload - some parts are still limited by memory bandwidth, others by CPU.

Modern CPUs are extremely good at mitigating memory bottlenecks by using caches, speculative execution and prefetchers. However, all active entities are read at every tick of the game. In large factories, this is too much data for caches. Also a virtual function call - such as the update of an entity - cannot be executed speculatively. Prefetchers are a part of the CPU that predicts what memory is going to be accessed soon and transfers it even before it is explicitly loaded. But since the entity update loop iterates over a linked list - the address of the next entity is stored within the entity itself - it is difficult to predict (not impossible).

This is where software prefetching comes in - the programmer gives a hint to the CPU what memory is accessed soon. That is what we now do in Factorio: Before an entity is updated, the next entity is already requested so that it can be loaded in the background. The principle also applies to a few other loops over linked lists. The nice thing about this, is that it is an extremely simple and isolated change in the code. The downside is, that you are entering the realm of architecture-specific micro-optimization. If you aren't careful, it can even be bad for performance.

A good rule is to never guess about performance - always verify. So I did some tests with different maps and the results were promising. Entities are larger than a single cache line and the pointers point into the middle of the object due to multiple inheritance. Many experiments later, the optimal range showed to be -128 byte to +384 byte (8 cache lines). This coincides with the sizes of typical entities. The prefetching instruction has another parameter determining the cache level used - which again was determined experimentally.



To get a bit more diversity, the measurements for this chart were done on a different CPU (i7-6700K vs i7-4790K previously), and include some more maps. It showed that the new belt-heavy map got less speedup (+5%) from software prefetching than the others. As a remedy, this map gets a huge boost from the belt optimization before. Other saves got a nice 9-13% speedup. All measurements are averages update times over 3600 ticks, the boxplots show 20 repeated runs.

Overall software prefetching is a nice effective micro-optimization with very little code changes, but many measurements to find the right configuration and verify.

Crafting machine animation optimisation
The issue is, that crafting machines can have arbitrary count of secondary animations tied to it (rotating fan, liquid in the chemical plants etc.). As each of the animations can have different speed and frame count, we kept positions of all of these animations in dynamically allocated vector and just updated each of these independently whenever the crafting machine was producing. But now, we just have one number representing the overall offset of the animations. We move it depending on the speed of the crafting machine and all the animations calculate their cyclic position depending on the modulo of this value only when we need to actually draw the machine.

This means, that this complicated code:
void CraftingMachine::setupWorkingVisualisationFrames(double performance)
{
const CraftingMachinePrototype& prototype = *this->getPrototype();
this->frame.move(performance, prototype.animation.getAnimation(this->direction));
if (this->workingVisualisationFrames.empty())
{
this->workingVisualisationFrames.resize(prototype.workingVisualisations.size());
for (size_t i = 0; i < this->workingVisualisationFrames.size(); ++i)
this->workingVisualisationFrames[i].randomize(prototype.workingVisualisations[i].getAnimation(this->direction),
this->getMap().getRandomGenerator());
}
for (size_t i = 0; i < this->workingVisualisationFrames.size(); ++i)
this->workingVisualisationFrames[i].move(prototype.workingVisualisations[i].getAnimation(this->direction));
Becomes this simple:
void CraftingMachine::setupWorkingVisualisationFrames(double performance) { this->frameReference += performance; this->showWorkingVisualisations = true; }
The memory size of crafting machine is decreased and the overall performance of game is improved by additional 2%.
Another day, another optimisation :)

HR Lab
The weekly dose of update high resolution graphics:


Related to HR entities, It turned out that our zooming system never showed an exact zoom of 2.0, which would be the 'pixel perfect' zoom level for the HR entities. By changing the zoom rate from 1.1, to the 7th root of 2 (1.104089...), the zoom now increments perfectly from 1.0 to 2.0 in 7 steps.

As always, let us know any thoughts or feedback over on our forum.
Factorio - Klonan
Hello,as the team is getting slowly bigger and we still don't have any dedicated project manager, we had to start looking for tools to help us manage the team. We are testing software that allows our team members to track time spent on individual tasks, so right now my timer on "Friday facts related work" is running. I hope it to give me better insight into what kind of tasks our time goes to, where are we losing most of it, or what were the people doing when I was not here. People tend to not like these kind of changes, but we just have to admit that we are not the 4 people punk development team working from our living room and we need to invest more time into working efficiently.

Prefetching (Technical)
Kovarex already presented a concise summary of the prefetching patch, here is some more background and dirty technical details.

I started to look into Factorio performance improvements a while back, more specifically UPS (updates per second) improvements for large bases. It is widely recognized that the UPS are mostly limited by memory performance (more). That is normal - even highly optimized scientific simulation codes are rarely limited by arithmetic instructions.

At first, I looked into ways to reduce the size of Entities. Common entity sizes like Inserter (536 bytes) or AssemblingMachine (648 bytes) seem surprisingly large at first. I tried some changes, e.g. moving less frequently accessed data out of the actual entity in a separate object in memory. These changes had significant impact to the code in many files, but just saving a few bytes didn't make a measurable impact to performance.

Back to a bit of theory - there are two different ways in which memory can become a bottleneck: bandwidth (the amount of data supplied over time, e.g. 50 GB/s) and latency (the time until a requested piece of data is available, e.g. 50 ns). Comparing the results for different RAM timing settings (CAS latency) shows, that latency has a significant impact. It is important to note, that Factorio is not a homogeneous workload - some parts are still limited by memory bandwidth, others by CPU.

Modern CPUs are extremely good at mitigating memory bottlenecks by using caches, speculative execution and prefetchers. However, all active entities are read at every tick of the game. In large factories, this is too much data for caches. Also a virtual function call - such as the update of an entity - cannot be executed speculatively. Prefetchers are a part of the CPU that predicts what memory is going to be accessed soon and transfers it even before it is explicitly loaded. But since the entity update loop iterates over a linked list - the address of the next entity is stored within the entity itself - it is difficult to predict (not impossible).

This is where software prefetching comes in - the programmer gives a hint to the CPU what memory is accessed soon. That is what we now do in Factorio: Before an entity is updated, the next entity is already requested so that it can be loaded in the background. The principle also applies to a few other loops over linked lists. The nice thing about this, is that it is an extremely simple and isolated change in the code. The downside is, that you are entering the realm of architecture-specific micro-optimization. If you aren't careful, it can even be bad for performance.

A good rule is to never guess about performance - always verify. So I did some tests with different maps and the results were promising. Entities are larger than a single cache line and the pointers point into the middle of the object due to multiple inheritance. Many experiments later, the optimal range showed to be -128 byte to +384 byte (8 cache lines). This coincides with the sizes of typical entities. The prefetching instruction has another parameter determining the cache level used - which again was determined experimentally.



To get a bit more diversity, the measurements for this chart were done on a different CPU (i7-6700K vs i7-4790K previously), and include some more maps. It showed that the new belt-heavy map got less speedup (+5%) from software prefetching than the others. As a remedy, this map gets a huge boost from the belt optimization before. Other saves got a nice 9-13% speedup. All measurements are averages update times over 3600 ticks, the boxplots show 20 repeated runs.

Overall software prefetching is a nice effective micro-optimization with very little code changes, but many measurements to find the right configuration and verify.

Crafting machine animation optimisation
The issue is, that crafting machines can have arbitrary count of secondary animations tied to it (rotating fan, liquid in the chemical plants etc.). As each of the animations can have different speed and frame count, we kept positions of all of these animations in dynamically allocated vector and just updated each of these independently whenever the crafting machine was producing. But now, we just have one number representing the overall offset of the animations. We move it depending on the speed of the crafting machine and all the animations calculate their cyclic position depending on the modulo of this value only when we need to actually draw the machine.

This means, that this complicated code:
void CraftingMachine::setupWorkingVisualisationFrames(double performance)
{
const CraftingMachinePrototype& prototype = *this->getPrototype();
this->frame.move(performance, prototype.animation.getAnimation(this->direction));
if (this->workingVisualisationFrames.empty())
{
this->workingVisualisationFrames.resize(prototype.workingVisualisations.size());
for (size_t i = 0; i < this->workingVisualisationFrames.size(); ++i)
this->workingVisualisationFrames[i].randomize(prototype.workingVisualisations[i].getAnimation(this->direction),
this->getMap().getRandomGenerator());
}
for (size_t i = 0; i < this->workingVisualisationFrames.size(); ++i)
this->workingVisualisationFrames[i].move(prototype.workingVisualisations[i].getAnimation(this->direction));
Becomes this simple:
void CraftingMachine::setupWorkingVisualisationFrames(double performance) { this->frameReference += performance; this->showWorkingVisualisations = true; }
The memory size of crafting machine is decreased and the overall performance of game is improved by additional 2%.
Another day, another optimisation :)

HR Lab
The weekly dose of update high resolution graphics:


Related to HR entities, It turned out that our zooming system never showed an exact zoom of 2.0, which would be the 'pixel perfect' zoom level for the HR entities. By changing the zoom rate from 1.1, to the 7th root of 2 (1.104089...), the zoom now increments perfectly from 1.0 to 2.0 in 7 steps.

As always, let us know any thoughts or feedback over on our forum.
...