c++microbenchmarkgoogle-benchmark

Google Benchmark BaseClass Initialization happens before static variable assignment causes problem


I want to use google benchmark to run my codes, which contains a static global variable. In the benchmark's base class, I need to read this variable to perform some setup. But when the benchmark base class is initialized, that static variable is not initialized yet and cause problems.

The Minimal Reproducing Example below contains three files: test.h, test.cc and benchmark_main.cc. The benchmark_main.cc contains a benchmark base class Init. Its constructor reads the data variable. But at this time, the data variable is still empty.

Question: is there a way to make the data variable initialization happens before the baseclass initialization? Any other workaround is also welcome.

test.h

#include <array>
#include <vector>

using namespace std;

const vector<int>& func(int);

test.cc

#include "test.h"

const array<vector<int>,5> data = {
        vector<int>({1}),
        {2,3},
        {4,5},
        {6,7},
        {8}
};

const vector<int>& func(int index) {
        return data[index];
}

benchmark_main.cc

#include <benchmark/benchmark.h>
#include <iostream>
#include "test.h"

class Init: public benchmark::Fixture {
protected:
public:
    Init() {
        std::cout << func(1)[0] << '\n';
    }
};

BENCHMARK_F(Init, A)(benchmark::State &state) {
  for (auto _ : state)
    std::cout << func(1)[0] << '\n';
}

BENCHMARK_MAIN();

Solution

  • Your problem here is that global variables in C++ have an undefined initialization order between different translation units (i.e. .cc files).

    I see a few options to resolve it:

    1. Move the code from test.cc to the top of benchmark_main.cc, as globals are initialized in declaration order within a single file. This is trivial in this example, but not so easy in a large project.
    2. Make the data global variable a static variable inside func() instead. This may affect the performance to some extent as it will check to see if the static is initialized on every call to the function.
    3. There are compiler specific tweaks you can do to modify the initialization order of global variables. For example: https://learn.microsoft.com/en-us/cpp/preprocessor/init-seg?view=msvc-160
    4. Instead of using the BENCHMARK_F macro, you could use the API more directly to avoid using a global variable there. Google benchmark custom main might help with that.