0

Given a (inline void-returning nullary) function f that doesn't have side effects:

inline void f() { /*...*/ }

Is it possible to write an inline function g that calls f but won't be optimized out by the implementation:

inline void g() { [[dont_optimize_this_away ???]] f(); }

Is there some way to tell the compiler "call f and inline the assembly, but don't optimize those instructions away"?

I don't think there is a way to do this in standard C++20. (or is there?)

...but is there a platform-specific way to do it on x86-64 with gcc, clang and/or msvc ? Some kind of instrinsic / compile builtin / attribute?

Andrew Tomazos
  • 66,139
  • 40
  • 186
  • 319
  • 6
    What instructions would you expect the compiler to emit, for an inline function that doesn’t do anything? – Sneftel Jun 28 '22 at 08:22
  • @Sneftel: For example, let's say it was `for (int i = 0; i < 100; i++);` I would expect it to emit `mov eax=100; loop: dec eax; jnz loop;`. Or let's say I put those instructions in an inline assembly block explicitly. – Andrew Tomazos Jun 28 '22 at 08:24
  • I suppose part of the confusion could be caused by the assumption that assembly is generated before inlining. That's not how modern compilers work; it loses valuable optimization opportunities. A symbolic representation is inlined, which in this case is "nothing". – MSalters Jun 28 '22 at 08:25
  • If you did that loop in the middle of a function, rather than by calling a separate function that did that, wouldn’t you expect the optimiser to remove the loop entirely? It has no visible effects, after all. – Sneftel Jun 28 '22 at 08:25
  • @MSalters: Ok lets suppose I wrote an inline assembly block - is there a way to tell the compiler to really emit those instructions and not optimize them away. – Andrew Tomazos Jun 28 '22 at 08:26
  • 2
    In the case of that `for` loop, you could add the `volatile` qualifier to `int i`. – Adrian Mole Jun 28 '22 at 08:26
  • @AdrianMole: That would introduce a side effect, and the question specifically says there aren't. – MSalters Jun 28 '22 at 08:28
  • @MSalters Not sure what that side effect would be, as `i` no longer exists after the loop ends. – Adrian Mole Jun 28 '22 at 08:30
  • @MSalters: Then I suspect maybe what I'm really after is a way to turn off optimization for a specific function. – Andrew Tomazos Jun 28 '22 at 08:34
  • @AdrianMole: The observable effect would be 100 writes to a `volatile`. In C++, that is formally an observable side effect. That's precisely why `volatile` stops the optimizer. – MSalters Jun 28 '22 at 08:41
  • 1
    One idea is to pass the return value somewhere. `dont_discard(f());` and then define `dont_discard` in another file where the optimizer can't see it. Obviously not guaranteed to work (especially with LTO) – user253751 Jun 28 '22 at 08:51
  • In general, the answer to this question as asked is "no". The C++ standard only requires that observable side effects (e.g. output, changes of a `volatile` variable) are produced. Timing is not an observable side effect (at least, in C++ and quite a few other languages - the definition does not extend to what (say) a system task monitor outside the running program can measure) which is why functions (or loops, or whatever) that are deemed to have no observable effects are often optimised out entirely. – Peter Jun 28 '22 at 08:51
  • @BoP: this question forgot to mention that this is a followup to [Best approximation for nanosecond-accurate sleep function with compile-time duration?](https://stackoverflow.com/q/72781467) . That doesn't work well for tiny sleeps on the order of the cost of making a single system call, and they don't actually want to delay for time, they want to delay for execution cost to try to mock up a test of genetic algorithms that take a function pointer as an arg. – Peter Cordes Jun 28 '22 at 09:50
  • 1
    Note that the optimizer will optimize `f` into the empty function. So even if you make the optimizer call `f` in `g` it will just be `f: ret`. What you need is to add something to `f` that has an observable effect for the compiler but does nothing, like `volatile asm("")` or `volatile asm("":::memory);`. – Goswin von Brederlow Jun 28 '22 at 13:11

3 Answers3

2

According to this post, you can use __attribute__((optimize("O0"))) like:

inline void __attribute__((optimize("O0"))) g() { [[dont_optimize_this_away ???]] f(); }
Dehghannn
  • 66
  • 6
1

Maybe this? Looking at the disassembly of release looks like it's still in there. (Windows pragma: https://learn.microsoft.com/en-us/cpp/preprocessor/optimize?view=msvc-170)

inline void f() {
    //std::cout << "I do a thing";
}

#pragma optimize( "", off ) 
inline void g() {
    f();
}
#pragma optimize( "", on )

int main()
{
    g();
}
Natio2
  • 235
  • 1
  • 9
1

There is a way. For example Google benchmark has benchmark::DoNotOptimize.

Just copy its implementation to your code and apply it for index variable for this dummy loop of yours for (int i = 0; i < 100; i++); form comment under a question.

Here is technical explanation how it works for gcc (note it is old source so name of function is different).

Marek R
  • 32,568
  • 6
  • 55
  • 140
  • Certainly worth mentioning, but `DoNotOptimize` operates on a reference (essentially just casting it to volatile), whereas the question asks for a way to apply it to the function `g` (without modifying `f`). The `for` example is just one possible body of `f`. – Andrew Tomazos Jun 28 '22 at 11:32