In the video, he inlines a very simple function and his game gets twice as fast for no apparent reason. It's instructive to watch him dive into the generated assembly to figure out why.
In the video, he inlines a very simple function and his game gets twice as fast for no apparent reason. It's instructive to watch him dive into the generated assembly to figure out why.
for(int I = 0; I < 4; ++I) {
real32 PixelPx = (real32)(XI + I);
real32 PixelPy = (real32)Y;
real32 dx = PixelPx - Origin.x;
real32 dy = PixelPy - Origin.y;
real32 U = dx*nXAxis.x + dy*nXAxis.y;
real32 V = dx*nYAxis.x + dy*nYAxis.y;
//rest of the loop
}
into something like real32 PixelPy = (real32)Y;
real32 dy = PixelPy - Origin.y;
real32 PixelPx = (real32)(XI);
real32 dx = PixelPx - Origin.x;
real32 U = dx*nXAxis.x + dy*nXAxis.y;
real32 V = dx*nYAxis.x + dy*nYAxis.y;
for(int I = 0; I < 4; ++I) {
U += nXAxis.x;
V += nYAxis.x;
//rest of the loop
}
PixelPy and dy are not affected by the counter in the loop which means they can safely moved outside the loop.This also results in the subexpression dynXAxis.y and dynYAxis.y being lifted outside the loop.
Now we've moved half of the code outside the loop but we aren't done yet.
The same can be done with PixelPx and dx, the trick is to then replace dxnXAxis.x with
(dx + I)*nXAxis.x
Expanding (dx + I)*nXAxis.x
yields dx*nXAxis.x + I*nXAxis.x
We can now lift the subexpression dx*nXAxis.x
out of the loop.The only thing that is now done in the loop is
I*nXAxis.x
which can be further simplified to U += nXAxis.x
The same happens with nYAxis.x.EDIT: Sorry for the bad formatting. The markdown parser ate my asterisks so I put things into code blocks which requires a new line each time.
It's another hurdle for the sufficiently smart compiler though. You need to know how the program will be run to know which is the better form. Once you get into making code-size Vs speed things get murky with instruction caches etc.
If a function is only called from a single place, consider inlining it.
You should consider inlining your function, not always do it. Recently, I made a mod for a game and I had to draw an UI by code, and there, it made sens to use one-time function because it made the code easier to read (super-expressive functions like DrawLeftPane() or DrawHeader(), and next to no ties between functions).Most of the time, code readability should be prioritized over performance.
I work in two performance sensitive projects, both C++, and this has yet to be a reason to inline code. Algorithm choice is optimization of choice first and so far finally.
Excepting environments where performance is critical (games comes to mind), shouldn't we bias toward improving the code for human readability?
For now, there are macros if an inline function does not work properly. Attributes to force inlining exist in some compilers, at least GCC and clang support those.
Additionally marking the function as pure if applicable can help optimisers as well.
(Security attributes and recursive calls that may remain recursive calls instead of stack utilising loops.)
GCC and clang variants do not have this issue.
MSVC is generally not really known for high performance of generated code, which is partly why newest versions support a clang backend.