Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] benchmark::DoNotOptimize does not prevent a reference from being optimized away. #1773

Open
HFTrader opened this issue Mar 25, 2024 · 4 comments

Comments

@HFTrader
Copy link
Contributor

Describe the bug
benchmark::DoNotOptimize() does not prevent a reference from being optimized away.

System
OS: Ubuntu 20.04 LTS, Compiler explorer
Compiler: GCC 9.04, GCC trunk, Clang trunk

To reproduce
Compiler Explorer that works: https://godbolt.org/z/c3d5sc8s3
Compiler Explorer that does not: https://godbolt.org/z/3bMMvaecr

Alternatively, compile the following code

#include <vector>
#include <benchmark/benchmark.h>
static void bm_traverse(benchmark::State& state) {
    const size_t N = 500;
    std::vector<int> vec;
    for (size_t j = 0; j < N; ++j) vec.push_back(j);
    for (auto _ : state) {
        for (auto& value : vec) {
            asm("nop"); // just to locate asm point faster
            benchmark::DoNotOptimize(value);
        }
        benchmark::DoNotOptimize(vec);
    }
    state.SetComplexityN(N);
}
BENCHMARK(bm_traverse);
BENCHMARK_MAIN();

Expected behavior
DoNotOptimize should have preserved the loop read. I understand that the reference is basically a pointer but users typically would expect that the reference represents the actual lvalue.

Screenshots

With the "&" reference in the inner loop, a vec load is not generated.

.LBB0_31:                               #   Parent Loop BB0_6 Depth=1
        nop
        addq    $4, %rax
        cmpq    %rcx, %rax
        jne     .LBB0_31

If you remove the reference the vec load is generated.

.LBB0_31:                               #   Parent Loop BB0_6 Depth=1
        movl    (%rax), %edx   # <<<< HERE
        movl    %edx, 12(%rsp)
        nop
        addq    $4, %rax
        cmpq    %rcx, %rax
        jne     .LBB0_31

Additional context

I understand this is a matter of semantics that might have been discussed in the past. In this case, I'd just like confirmation of this behavior.

@HFTrader HFTrader changed the title [BUG] [BUG] benchmark::DoNotOptimize does not prevent a reference from being optimized away. Mar 25, 2024
@LebedevRI
Copy link
Collaborator

IMHO in this particular case the

Compiler Explorer that works: https://godbolt.org/z/c3d5sc8s3

... is the case that actually does not work.
There is zero reason for the compiler to produce that load-and-spill there.

@HFTrader
Copy link
Contributor Author

From my user point of view, DoNotOptimize() means: do something with this lvalue such that it is a noop but the compiler will still think we are using it. So if you are using the value, the DoNotOptimize() is doing its job.

@HFTrader
Copy link
Contributor Author

That all said, this is your library so the semantics of using it is what you say. I was surprised by it. If that's known and expected then it is what it is.

@LebedevRI
Copy link
Collaborator

Right, but still, i don't quite see it.
A reference is a pointer, while an auto is a result of dereferencing that pointer.
If you drop DoNotOptimize from either snippet, there's a lot more changes,
so DoNotOptimize did prevent the argument from being optimized away.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants