• Advertisement
Sign in to follow this  

Get Global Variables Always Inlined ?

This topic is 743 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi,

I did a simple test to get a global variable from a struct pointer and from the global namespace directly.

The two cases result to an inlined get in release when you look at the assembly view from MSVC 2013.

I did the test for an executable build, is it always the case for executable, static and shared lib ?

To be clear, the variable is not declared in the header, only in the .cpp.

Thanks

Share this post


Link to post
Share on other sites
Advertisement
Depends on details.

The exact machine code the compiler outputs depends on various settings. It also depends on how much code you are processing, and details of the compiler.

The biggest thing here is the location of the accessor function. You can help the compiler here by keeping the accessor function in the header file rather than the cpp file. If you put it in the cpp file then the compiler will only inline the function if it has loaded the cpp file at the same time. If the function is in the header file then the function will already be loaded in memory so it is much more certain to be inlined.

Usually debug builds will not inline methods like that when it can in favor of enabling more direct debugging, but sometimes they will depending on compiler settings. As you turn up optimization settings the compiler gets more aggressive about what gets inlined, at the expense of readability. As there is less debug information the more likely it becomes that stepping in the debugger will jump to seemingly-random locations, or stepping will stay on the same line multiple times.

If you have placed the accessor function in the header file and turned up optimizations to a high level, the compiler will make it inline if possible, replacing the function with a memory address or similar fully-inlined call. Unless for some reason the compiler cannot do it for some technical reason that might apply.

Share this post


Link to post
Share on other sites

Lot of cases here, to be clear the case studied here is : variables in the .cpp (or all variables in one struct and a pointer of this struct in the .cpp) and accessor in header.

The goal of this operation is to hide the global variable and force to use Init, Shutdown and multiple Get.

Edited by Alundra

Share this post


Link to post
Share on other sites

Anonymous namespaces place things the global scope but restrict them to the translation unit.

namespace {
  int globalVar;
}

You can use them in .cpp files for a "private" global, similar to static globals in C. Is this what you mean?

Edited by Khatharr

Share this post


Link to post
Share on other sites

Global.h :

#pragma once

namespace Global
{
  inline int GetGlobalInt();

} // Global

Global.cpp :

namespace
{
  int IntValue = 150;
}

namespace Global
{
  int GetGlobalInt()
  {
    return IntValue;
  }
}

It's an example but the real use case is more to use a struct containing multiple variables and have a pointer with Init and Shutdown to call new and delete.

Share this post


Link to post
Share on other sites
As mentioned, because it is in the cpp file it will only be inlined if the compiler has pulled in the cpp file. Some compilers and compiler options will span multiple cpp files, others will only consider the included header files.

For best results, mark the function as inline and put it in a header file.

Share this post


Link to post
Share on other sites

As mentioned, because it is in the cpp file it will only be inlined if the compiler has pulled in the cpp file. Some compilers and compiler options will span multiple cpp files, others will only consider the included header files.

For best results, mark the function as inline and put it in a header file.


If you have "Link-time code generation" turned on for MSVC compilers (there are equivalent settings for GCC and Clang) then the linker will be able to inline functions in different cpp files without issue.

Marking a function as inline has little to no effect on whether a function gets inlined. And as compilers get smarter, putting them in the header file also doesn't matter that much, other than slowing down compilation.

No matter what you do (short of a force-inline compiler-specific intrinsic), the compiler may choose not to inline it if it's analysis of the situation lead it to believe that inlining the function call would result in a decrease in performance (very unlikely for a simple accessor like this, but possible for other functions where things inlining might increase the size of the calling function to the point where the instruction cache is blown out). Heck, because of these reasons, tagging a function as inline is the wrong place to do it anyway, since inlining the function at one call site may cause performance degredation, while inlining it at a second call-site would increase performance. It would make more sense to tag each call site as inlined or not (though C++ does not give you a way to do this).

In general - do not worry about inlining. With the proper compiler optimization settings the compiler will do it anyway where appropriate, and avoid it where appropriate.

Only focus on this kind of micro-optimization if you have profiled your code and proven that forcing a function to be inlined actually improves performance. Edited by SmkViper

Share this post


Link to post
Share on other sites
Only focus on this kind of micro-optimization if you have profiled your code and proven that forcing a function to be inlined actually improves performance.

In my case, these functions are used to get the render system, logger, managers, factory .... so, it's important.

Edited by Alundra

Share this post


Link to post
Share on other sites

In my case, these functions are used to get the render system, logger, managers, factory .... so, it's important.
"important" is not a substitute for "profiling your code to check it makes any difference".

 

I don't know what your render system does, but my $random guess is that it walks over the world, collects things to draw, sends stuff to the GPU, and perhaps does a few calculations on  the way. Even if you do a fraction of that, it is a whole lot more than a few instructions to get an address. Also, the render system is likely called less than 100 times / second, which is eons between the calls in CPU time.

Share this post


Link to post
Share on other sites
In general - do not worry about inlining. With the proper compiler optimization settings the compiler will do it anyway where appropriate, and avoid it where appropriate.

 

Sounds like "inline" has gone the way of register variables. Present, but completely ignorable by the compiler.

Share this post


Link to post
Share on other sites

Sounds like "inline" has gone the way of register variables. Present, but completely ignorable by the compiler.

Partially, but not completely.

It has always been a strong recommendation to the compiler, not a requirement. Putting functions in the header triggers the same thing implicitly. Compilers needed to handle this from the beginning as implicit optimisation steps.

Major compilers (MSVC, GCC, Clang, etc) still take the inline keyword into account in addition to their own heuristics. Command line options similarly contribute to what the compiler does with them.

Share this post


Link to post
Share on other sites


If you have "Link-time code generation" turned on for MSVC compilers (there are equivalent settings for GCC and Clang) then the linker will be able to inline functions in different cpp files without issue.
 

Link time optimization is still a second, less aggressive, optimization pass than compile time optimisation. It can inline, but it may miss some things that the first pass doesn't. In fact if you want the most aggressive optimization, one proven strategy is to #include all your cpp files into one massive translation unit. This will allow the first, more aggressive compiler pass to optimize all of your code together... at the cost of build time.

 

Marking functions as inline and putting them in a header, does not significantly effect the heuristics for when to inline, but it allows the function to be inlined at compile time.  In C89, which does not have the inline keyword, doing the same but making the function static would have a similar effect.

 

For global variables, they can be inlined when they are compile time constants, so you can mark them as constexpr to ensure that they are, and to give an error if they can't be. This works like static, to give each compilation unit its own copy.

Edited by King Mir

Share this post


Link to post
Share on other sites

 

In general - do not worry about inlining. With the proper compiler optimization settings the compiler will do it anyway where appropriate, and avoid it where appropriate.

 

Sounds like "inline" has gone the way of register variables. Present, but completely ignorable by the compiler.

 

Register is going away in C++17 and was formally deprecated in C++11. So it won't be present for long.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement