Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Interestingly, Casey Muratori accidentally demonstrates during one of his Handmade Hero sessions that the compiler won't always be able to optimize certain bits of code that are put in a function as opposed to being inline.

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.

https://www.youtube.com/watch?v=B2BFbs0DJzw



The compiler probably turned

  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.


Effectively this means the speedup comes from optimizations that assume the code in question is only ever run in that context. When the code is inline this is an easy call to make. For a function it's trickier. I would hope some compilers make a function to handle arbitrary contexts but try inlining on individual cases to see if significant gains such as this can be made.

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.


having const parameters in the function V2i might have helped the compiler I think


TIL, another reason to perform 'defactor inline-method' (with directed feedback of course).


I'm not sure it should be considered as a reason for anything. The most important point of the article, imo, is:

  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.


Totally agree, but in perf critical inner loops, it might be interesting to speculatively inline different functions and measure perf. Overly factored code inside an inner loop has been shown in the video to cause compiler confusion.


People write perf intensive inner loops so infrequently that this really ought to be discounted as an argument.

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.


Another example to set aside for the next "So you think you are smarter than the compiler" person.


Is that a helpful response to that person? Isn't that person (despite sounding like an ass) more or less right at any given time?

Excepting environments where performance is critical (games comes to mind), shouldn't we bias toward improving the code for human readability?


Technically, you could have some stronger keyword than inline in future C and C++ standards, akin to constexpr. For hard inlining always.

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.


You may find the Nim language to be interesting. The entire language is processed as an AST, so you can do a lot of magic stuff like write normal functions, functions with forced inlining, and AST-transforming macros all in the same syntax and all processed in one pass at compile time.

http://nim-lang.org


__forceinline in MSVC.



That usually works, with caveats as mentioned on page.

(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.


MSVC's latest versions support a clang frontend w/ microsoft's code generation as backend. Are you referring to something else?


constexpr functions exist, and they are also inline :)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: