How does the use of `static` affect the speed of my code?

Viewed 318

I was solving an exercise online, and at one point i needed to delete the "" from a the beginning and end of a string. This was my code:

void static inline process_value(std::string &value) {
    if (value.back() !='>') {
        value = value.substr(1, value.size()-2);
    }
}

Called from this benchmark loop:

static void UsingStatic(benchmark::State& state) {
  // Code inside this loop is measured repeatedly
  for (auto _ : state) {
      std::string valor("\"Hola\"");
      process_valueS(valor);
    // Make sure the variable is not optimized away by compiler
    benchmark::DoNotOptimize(valor);
  }
}

Just because of curiosity I did a benchmark.

  • Compiler: Clang-9.0
  • std: c++20
  • optim: O3
  • STL: libstdc++(GNU)

While I was at it I decided to remove static from process_value, making void inline process_value that was otherwise the same. To my surprise it was slower.

I thought that static only meant that the function was just for a file. But here it says that " 'static' means that the function should be inlined by the compiler if possible". But in that case when i removed static I think that the result should not have changed. Now I'm am confused, what other things does static do other than delimiting the function to a single .cpp, how does that affect performance?

The disassembly on QuickBench shows that the NoUsingStatic loop actually calls process_value instead of inlining it, despite the inline keyword making it legal for the compiler to do so. But UsingStatic does inline the call to process_valueS. That difference in compiler decision-making presumably explains the difference in performance, but why would clang choose not to inline a simple function declared void inline process_value(std::string &value){ ... }?


EDIT: Beacuse the question was closed because it was not clear enough, i deleted parts that where not related to the question. But if im missing some information please tell me in the comments

2 Answers

Clang uses a cost based decision whether a function will be inlined or not. This cost is affected by a lot of things. It is affected by static.

Fortunately, clang has an output, where we can observe this. Check out this godbolt link:

void call();

inline void a() {
    call();
}

static inline void b() {
    call();
}

void foo() {
    a();
    b();
}

In this little example, a() and b() are the same, the only exception is that b() is static.

If you move the mouse over the calls a() or b() on godbolt (in OptViewer window), you can read:

a(): cost=0, threshold=487

b(): cost=-15000, threshold=487

(clang will inline a call, if the cost is less than the threshold.)

clang gave b() a much lower cost, because it is static. It seems that clang will only give this -15000 cost reduction for a static function only once. If b() is called several times, the cost of all b()s will be zero, except one.

Here are the numbers for your case, link:

process_value(): cost=400, threshold=325 -> it is just above the threshold, won't be inlined

process_valueS():: cost=-14600, threshold=325 -> OK to inline

So, apparently, static can have a lot of impact, if it is only called once. Which makes sense, because inlining a static function once doesn't increase code size.

Tip: if you want to force clang to inline a function, use __attribute__((always_inline)) on it.

inline is just an advise to the compiler, which may or may not really inline that your particular code.

Regarding to the static keyword, if it's applied to a global variable, then it has the file-scope (as you've mentioned) if you compile your code as a separate compilation-unit. So it's even possible to have your static global variables accessible from other files if you compile them as a single compilation unit. This means that in reality, the scope of the global static variables is not the file but the compilation unit (which may or may not be one single file).

But, since you have a global static function, not a variable, it is accessible from everywhere as a global static function.

EDIT: As suggested by @Peter Cordes in comments below, it may be a real mess with inline and static at the same time, so the official doc ( https://en.cppreference.com/w/cpp/language/inline ) says that the redefinition of inline functions (and variables since C++17) must be non-static.

Related