1 2 3 4 5 6 7 8 9 10 11 12 | int getSum(int len, int* arr) { int sum = 0; for (int i=0; i<len; i++) sum += arr[i]; return sum; } int sum100(int* arr) { int s = sum(100, arr); return s/5; } |
1 2 3 4 5 6 7 8 9 10 | int sum100_inlined(int* arr) { int s; { int sum = 0; for (int i=0; i<100; i++) sum += arr[i]; s = sum; } return s/5; } |
There is something that I still don't quite understand about inlining functions:
Not always. Inlining large functions increases code size much and that may be bad for code cache. Compilers decide on inlining based on function size, optimization levels & other things.
Compilers try as hard as they can to remove virtual functions. It is called "devirtualization" and is important optimization in C++ compilers. Java and C# and similar languages too. Here's example where virtual function disappears, because compiler figures out which concrete function is used as virtual function: https://godbolt.org/z/1EnfrsYbz
Here are two presentations from clang & llvm how they do devirtualization optimizations:
Depends on function definition. If it has external linkage, it will still produce actual function code in object file, because maybe some other TU is using it. If it has internal linkage, then no - it won't produce function body. If you always want the latter, then use static inline
vs just inline
It can if compiler can inline / figure out exactly which function is called through function pointer. Example: https://godbolt.org/z/nnoqnh96o
Function pointers do not point to inlined places, they point to actual function. Even if other places get inlined. Example: https://godbolt.org/z/KWPMe5adG You can see number()
function has big()
function inlined, but number_runtime()
function has function pointer to original big()
function.
Thanks for the answers!
Compilers try as hard as they can to remove virtual functions. It is called "devirtualization" and is important optimization in C++ compilers. Java and C# and similar languages too. Here's example where virtual function disappears, because compiler figures out which concrete function is used as virtual function: https://godbolt.org/z/1EnfrsYbz Here are two presentations from clang & llvm how they do devirtualization optimizations:
Haven't watched the 2 clips yet, but does "devirtualization" here mean just don't use the virtual pointer or literally remove the virtual pointer from the base class and all the derived ones (and reduce the size of all the instances of those classes) when it knows it can always inline the function.
Function pointers do not point to inlined places, they point to actual function. Even if other places get inlined.
Why is it? Why couldn't it point to the inlined one? Doing that will certainly reduce the overall code size, right? Especially in your example when the function just returns an int, so pointing it to the inlined one is very easy.
devirtualization means change virtual function calls (which are indirect) to direct calls - as that allows possibility of inlining and more optimizations removing whole call.
Virtual table pointer will still be there. Afaik that never gets removed.
Function pointers do not point to inlined places, because that's not a real function anymore. Inlined code gets mixed with code in wherever it was inlined - but when you call inlined function you don't want to execute that other code, only function's one.
Also the C standard requires two pointers be equal only when they point to same thing. So having &number == &big
expression be true would violate C standard. They must be separate things.
My previous example is too simple to show how inlined function code changes. Yes, produced code looks identical - but that can be de-duplicated by linker (it will keep only one copy of identical functions).
Here's a better example: https://godbolt.org/z/jse649h8o
While big()
function is inlined into foo()
function, the bar()
function refers to big() as function pointer. Because it cannot point anywhere inside foo() function to return that 100000
number, foo() function performs different calculation.
Yes, produced code looks identical - but that can be de-duplicated by the linker (it will keep only one copy of identical functions).
What did you mean here? What would the linker do?
If compiled functions (or global variables) contains identical bytes, the linker can put only one copy in final binary and adjust all references to point to same location.
On Windows this is done if you use /Gy cl.exe argument (which is used by default in /O1 or /O2) and /OPT:ICF link.exe argument (which is used by default if no /DEBUG is specified).
&ptr1 == &ptr2
expression equal to true.