Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is a C++ optimizer allowed to move statements across a function call?

Note: No multithreading at all here. Just optimized single-threaded code.

A function call introduces a sequence point. (Apparently.)

Does it follow that a compiler (if the optimizer inlines the function) is not allowed to move/intermingle any instructions prior/after with the function's instructions? (As long as it can "proove" no observable effects obviously.)


Explanatory background:

Now, there is a nice article wrt. a benchmarking class for C++, where the author stated:

The code we time won’t be rearranged by the optimizer and will always lie between those start / end calls to now(), so we can guarantee our timing will be valid.

to which I asked how he can be sure, and nick replied:

You can check the comment in this answer https://codereview.stackexchange.com/a/48884. I quote : “I would be careful about timing things that are not functions because of optimizations that the compiler is allowed to do. I am not sure about the sequencing requirements and the observable behavior understanding of such a program. With a function call the compiler is not allowed to move statements across the call point (they are sequenced before or after the call).”

What we do is basically abstract the callable (function, lambda, block of code surrounded by lambda) and have a signle call callable(factor) inside the measure structure that acts as a barrier (not the barrier in multithreading, I believe I convey the message).

I am quite unsure about this, especially the quote:

With a function call the compiler is not allowed to move statements across the call point (they are sequenced before or after the call).

Now, I was always under the impression that when an optimizer inlines some function (which may very well be the case in a (simple) benchmark scenario), it is free to rearrange whatever it likes as long as it does not affect observable behavior.

That is, as far as the language / the optimizer are concerned, these two snippets are exactly the same:

void f() {
  // do stuff / Multiple statements
}

auto start = ...;
f();
auto stop = ...;

vs.

auto start = ...;
  // do stuff / Multiple statements
auto stop = ...;
like image 704
Martin Ba Avatar asked Apr 12 '15 18:04

Martin Ba


People also ask

Can compiler reorder function calls?

If you mean that the difference can not be observed, then yes, the compiler (and even the CPU itself) is free to reorder the operations.

How does gcc optimization work?

A large variety of optimizations are provided by GCC. Most are categorized into one of three levels, but some are provided at multiple levels. Some optimizations reduce the size of the resulting machine code, while others try to create code that is faster, potentially increasing its size.

What is Ofast?

-Ofast enables all -O3 optimizations. It also enables optimizations that are not valid for all standard-compliant programs. It turns on -ffast-math , -fallow-store-data-races and the Fortran-specific -fstack-arrays , unless -fmax-stack-var-size is specified, and -fno-protect-parens .

How do I disable compiler optimization?

Use the command-line option -O0 (-[capital o][zero]) to disable optimization, and -S to get assembly file. Look here to see more gcc command-line options.


1 Answers

Now, I was always under the impression that when an optimizer inlines some function (which may very well be the case in a (simple) benchmark scenario), it is free to rearrange whatever it likes as long as it does not affect observable behavior.

It absolutely is. The optimizer doesn't even need to inline it for this to occur in theory.

However, timing functions are observable behaviour- specifically, they are I/O on the part of the system. The optimizer cannot know that that I/O will produce the same outcome (it obviously won't) if performed in a different order to other I/O calls, which can include non-obvious things like even memory allocation calls that can invoke syscalls to get their memory.

What this basically means is that by and large, for most function calls, the optimizer can't do a great deal of re-arranging because there's potentially a vast quantity of state involved that it can't reason about.

Furthermore, the optimizer can't really know that re-arranging your function calls will actually make the code run faster, and it will make debugging it harder, so they don't have a great deal of incentive to go screwing around with the program's stated order.

Basically, in theory the optimizer can do this, but in reality it won't because doing so would be a massive undertaking for not a lot of benefit.

You'll only encounter conditions like this if your benchmark is fairly trivial or consists virtually entirely of primitive operations like integer addition- in which case you'll want to check the assembly anyway.

like image 125
Puppy Avatar answered Nov 10 '22 11:11

Puppy