Get Global Variables Always Inlined ?

Started by
11 comments, last by King Mir 8 years, 3 months ago

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

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.

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.

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?

void hurrrrrrrr() {__asm sub [ebp+4],5;}

There are ten kinds of people in this world: those who understand binary and those who don't.

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.

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.

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

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.

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.

This topic is closed to new replies.

Advertisement