MSVC C++ compiler giving inconsistent performance on different builds of the same code when using [[unlikely/likely]] attributes

111 views Asked by At

I am simply experimenting with the [[unlikely/likely]] branch hints available when compiling with /std:c++latest.

To do this I am using a modified version of the code provided by http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0479r0.html at the bottom under Appendix A

#include <array>
#include <cstdint>
#include <random>
#include <chrono>

std::uint16_t clamp(int i) {
    if (i < 0) [[unlikely]] {
        return 0;
    }
    else if (i > 0xFFFF) {
        return 0xFFFFu;
    }

    return i;
}

int main() {
    std::mt19937 gen(42);
    std::bernoulli_distribution d(.001), up_down(.5);

    std::vector<int> data;
    for (std::size_t i = 0; i != 1000000; ++i) {
        if (d(gen)) {
            data.push_back(up_down(gen) ? -1 : 0xFFFFF);
        }
        else {
            data.push_back(1);
        }
    }

    auto Prev = std::chrono::high_resolution_clock::now();

    std::uint32_t result = 0;
    for (int i = 0; i != 10000; ++i) {
        for (auto val : data) {
            result += clamp(val);
        }
    }

    auto After = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> Elapsed = After - Prev;
    std::chrono::milliseconds Time = std::chrono::duration_cast<std::chrono::milliseconds>(Elapsed);
    std::cout << (Time.count()) << '\n';

    return result > 5 ? 1 : 2;
}

Note all compilation was done in x64 release mode with /O2 on the latest version of Visual Studio 2019 (16.10.3) and on a Intel(R) Core(TM) i7-7700HQ 2.8Ghz CPU.

I then tested how using [[unlikely]], [[likely]] and no attribute would effect the time taken.

Testing [[unlikely]]] first I found that on my system it took ~6750ms to run. I then tested [[likely]], which also took ~6750ms to run. At first I thought this was probably just because the attributes are hints and thus the compiler can just ignore them. I then tested no attribute (simply not using [[unlikely/likely]]) and the code ran in ~9000ms. If the compiler was ignoring the attributes previously then it should give the same time of 6750ms, right?

I then went back to test [[likely]] and found that it now ran in ~9000ms??? At this point I was really confused so went back to check [[unlikely]] and found that it ran in ~6750ms. Checking [[likely]] again it now ran at ~6750ms.

This was super strange and after some digging looking through the generated assemblies I found that sometimes the generated assemblies would be 6000+ lines and sometimes under 2000 lines with subsequent compilations??? This is the same for subsequent compilations of the same code. After some more googling I found a peculiar comment:

Why godbolt generate different asm output than my actual asm code in Visual Studio?

And now I am even more confused, why would MSVC generate different code?

Going back to my experiment, I found these results based on compilation order:

  • No attribute => [[unlikely]] => [[likely]]

    ~9000ms => ~6750ms => ~6750ms and continues ~6750ms for subsequent [[likely]] compilations and switching back to [[unlikely]] gets the same result whilst switching back to No attribute gets ~9000ms.

  • No attribute => [[likely]] => [[unlikely]]

    ~9000ms => ~9000ms => ~9000ms and subsequent [[unlikely]] compilations gets the same ~9000ms result, this is the same when switching to [[likely]] and only exhibits the behaviour shown in the first sequence when compiled after compiling No attribute.

To make sure this wasn't some random quirk I ran these test multiple times without compilation at each step (simply by running the .exe file generated from File Explorer) and got consistent results. I even saved each different .exe file from each different sequence of compilations and ran them again getting the same bizarre results.

I am yet to compare the .exe bytes, but I want to avoid doing so as I am not very well versed in that area "^-^

Am I missing something, is this a bug? It all seems so strange how inconsistent everything is.

0

There are 0 answers