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 toNo 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 compilingNo 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.