Following https://meowni.ca/posts/static-initializers/, I am trying to reduce static initializers in our shared libraries to improve startup time of our program. One of the suggestions in the article is to avoid objects with non-trivial constructors as static objects. I'm trying to measure if the total static initializers came down after implementing the same.
Consider the following program:
#include <iostream>
static const char foo[] = "1234567890";
static const std::string bar = "abcdefghij";
int main() {
std::cout << foo << std::endl;
std::cout << bar << std::endl;
}
When I compile and inspect the ELF, I see function pointers for both foo
and bar
in the .init_array section which supposedly holds static initializers
readelf -s .init_array ./a.out -W | egrep "foo|bar"
11: 0000000000403010 11 OBJECT LOCAL DEFAULT 15 _ZL3foo
12: 0000000000405200 32 OBJECT LOCAL DEFAULT 25 _ZL3bar
Since foo is compile-time constant, it shouldn't require a static initializer right?
Whether the type is const
, trivial or POD is irrelevant.
Whether or not dynamic initialization at runtime may happen is determined by whether or not the initialization is a constant expression.
foo
's initialization in your example is guaranteed to be a constant expression.
bar
's initialization in your example is guaranteed not to be a constant expression before C++20 and may or may not be one in C++20 (depending on how exactly std::string
is implemented, in particular whether SSO is being used for the initialization).
The output of readelf
is not giving you the dynamic initializers, but just the symbols for the global objects themselves. You can see this in the mangled name of the symbol or that it says OBJECT
instead of FUNC
. It has no relevance to your problem.
Since C++20 you can enforce that no dynamic initialization will happen by declaring the variable with constinit
. Then either you are guaranteed that there is no dynamic initialization (because the initializer is a constant expression) or the compiler will fail to compile the program, letting you know why it isn't a constant expression.
Also static
probably doesn't do what you think it does in your example. static
here determines linkage, not storage duration, so it just means that foo
and bar
shall have internal instead of external linkage, meaning they will not be the same variable if declared like this in multiple translation units. They have static storage duration regardless of whether or not you use static
, because they are declared at namespace scope.