# Why do engines need to be optimized for new processors of the same architecture?

When a new processor generation is released, most websites report that game engines and programs need to be optimized for the new hardware. I do not quite understand why. A processor usually has an architecture that defines what kind of instruction set it uses. The one we all use nowadays is amd_x86_64 . Why would any program or compiler need to be updated if all processors use this same architecture? Surely there are features WITHIN the pipeline of the new processor that optimizes the execution of machine code, but why would the machine code itself need to be changed if the architecture did not?

• Comments are not for extended discussion; this conversation has been moved to chat.
– user1430
Dec 18 '17 at 18:15
• "Need" is a wrong wording, and more marketing than truth, much in the same way as e.g. Windows needs to support a certain new CPU generation (or doesn't, as in the case of Windows 7, which would in principle work perfectly fine with e.g. Ryzen, except for using 3-4% more power than necessary). This tuning is only about trying to squeeze a little more out of the CPU, getting closer to the maximum. Realistically, you may be able to gain an overall 1-2% in non-contrieved examples due to different scheduling and using a couple of new instructions. Dec 18 '17 at 18:59
• Just because two processors can perform the same operations that doesn't mean the operations have the same performance on both processors... Dec 19 '17 at 22:38
• See a related question of mine on Stack Overflow: How does mtune actually work? Dec 20 '17 at 3:55

Because different generations of the same architecture can have different instruction sets.

For example, Streaming SIMD Extensions are probably the best-known x86 instruction set, but yet, and despite there just being one x86 architecture, there exists SSE, SSE2, SSE3, and SSE4.

Each of these generations can include new instructions that provide faster ways of performing certain operations. An example that would be relevant to games might be dot product instructions.

So if a game engine is compiled for a previous generation of an architecture, it will not have support for these newer instructions. Similarly, it may be necessary to optimize the engine for newer instructions; SSE4, for example, has support for dot product instructions that work on array-of-structs data. An optimization that could take advantage of these newer instructions would be to change your data layout to array-of-structs.

• @Panzercrisis - thank you for the edit suggestion. To be clear: the original question was not about your own code, it was about the engine code, so "optimize your own code" is not a good edit suggestion. However, it did highlight that I needed to clarify that when I said "optimize" I meant "optimize the engine code", so I have edited to take that up. Dec 19 '17 at 16:18

Maximus's answer is correct, I just want to give another piece of the story:

The hardware itself changes in a way you need to change how you code, regardless of newly introduced instructions.

• Increased or decreased amounts of cache means you need to worry less or more about cache optimization/cache invalidation being issues. More cache means with small data you can focus less on making sure data is contiguous with out running into performance concerns. Less cache means this could be an issue, and very little cache means with some large data structures it won't matter any way.

• New levels of cache means you need to think more about how you organize even larger sets of data (L1, vs L2, vs L3 vs L4).

• More cores means you need to think about how you are going to multi thread applications better, and how your application scales in a multi process environment.

• Faster clocks means you need to start thinking about memory latency more than you need to think about CPU computation speed as a bottleneck of your system.

• The number of FPUs on a system may no longer match the number of integer ALUs per core any more (AMD had/has architectures like this).

• The number of clock cycles it takes to compute an operation my have decreased, or increased.

• The number of registers available changed.

All of these have very real performance impact on programs which made assumptions about underlying architecture in previous hardware with the same ISA, either positive or negative.

• "Increased or decreased levels of cache means you need to worry less about cache coherency. " - virtually any CPU is cache coherent. Do you mean false sharing? Even than virtually any CPU \$ line is almost always 64 B... Dec 18 '17 at 18:34
• Maciej was just taking your statement about cache coherency :) You probably meant "cache optimization" or something. Cache coherence is the ability of a system to keep a consistent view of memory transparently to the software even if in the presence of N independent caches. This is completely orthogonal to the size. TBH the statement is not really relevant but your answer (especially points 5 & 6) addresses the question better than the accepted one IMO :) Maybe stressing out the difference between architecture and u-architecture will make it stand out more. Dec 18 '17 at 20:59
• "like multiplication taking more time than addition, where as today on modern intel and amd CPUS it takes the same amount of time" That's not all true. In pipelined architectures you have to differentiate between latency (when the result is ready) and throughput (how many you can do per cycle). Int addition on modern Intel processors has a throughput of 4 and a latency of 1. Multiply has throughput 1 and latency 3 (or 4). These are the things that change with each architecture and need optimization. E.g. pdep takes 1 cycle on intel but 6 on Ryzen so might not want to use it on Ryzen. Dec 19 '17 at 7:47
• @Clearer I know we're talking about CPUs here, but you've never programmed for GPUs have you? The same code produces such wildly different results in performance that often you are actually forced to take in to consideration hardware capabilities in CUDA. That's where I came from with this, cache size (shared memory, managed L1 cache) actually needs to be taken into consideration in how you code for something in CUDA. Dec 19 '17 at 14:38
• @Christoph is correct. The benchmark you link is for a loop over an array c[i] = a[i] OP b[i] (i.e. 2 loads and 1 store per operation) so the times are dominated by memory bandwidth because of the very low computational intensity. The array size isn't shown so IDK if it fit in L1D. (gcc4.9 -Ofast very likely auto-vectorized those loops, so you're not even measuring the cost of normal scalar operations as part of complex integer code). The first line of that page is IMPORTANT: Useful feedback revealed that some of these measures are seriously flawed. A major update is on the way. Dec 20 '17 at 0:42

Even beyond gross changes like support for new instructions, microprocessor manufacturers are constantly modifying their designs to improve performance, and every new design can have different relative performance for each instruction or technique. Maybe you wrote some carefully optimized branchless code for the Model X, but the Model Y has an improved branch-predictor that reduces the misprediction penalty for the non-branchless version of the code (which also frees up a register to be used somewhere else). Maybe the Model Y supports greater parallelism of a certain high-latency instruction, so that now an unrolled loop of that instruction gets you better throughput, while on the Model X a shorter sequence was better.

Any problem can be solved in many ways, and every program is an interlocking collection of trade-offs and resource allocations, from the point of optimization. Even small changes in the availability of those resources or the cost of a given piece of code in terms of those resources, can have a cascade effect that gives a substantial performance advantage to one piece of code or another. Even if an upgraded chip has "more of everything", how much more of each thing can swing the balance.