Short Answer:
Your asd array is declared as this:
int *asd=new int[16];
Therefore, use int as the return type rather than bool.
Alternatively, change the array type to bool.
In any case, make the return type of the test function match the type of the array.
Skip to bottom for more details.
Long Answer:
In the manually inlined version, the “core” of one iteration looks like this:
xor eax,eax
mov edx,ecx
and edx,0Fh
mov dword ptr [ebp+edx*4],eax
mov eax,dword ptr [esp+1Ch]
movss xmm0,dword ptr [eax]
movss xmm1,dword ptr [edi]
cvtps2pd xmm0,xmm0
cvtps2pd xmm1,xmm1
comisd xmm1,xmm0
The compiler inlined version is completely identical except for the first instruction.
Where instead of:
xor eax,eax
it has:
xor eax,eax
movzx edx,al
Okay, so it’s one extra instruction. They both do the same – zeroing a register. This is the only difference that I see…
The movzx instruction has a single-cycle latency and 0.33 cycle reciprocal throughput on all the newer architectures. So I can’t imagine how this could make a 10% difference.
In both cases, the result of the zeroing is used only 3 instructions later. So it’s very possible that this could be on the critical path of execution.
While I’m not an Intel engineer, here’s my guess:
Most modern processors deal with zeroing operations (such as xor eax,eax) via register renaming to a bank of zero registers. It completely bypasses the execution units. However, it’s possible that this special handling could cause a pipeline bubble when the (partial) register is accessed via movzx edi,al.
Furthermore, there’s also a false dependency on eax in the compiler inlined version:
movzx edx,al
mov eax,ecx // False dependency on "eax".
Whether or not the out-of-order execution is able to resolve this is beyond me.
Okay, this is basically turning into a question of reverse-engineering the MSVC compiler…
Here I’ll to explain why that extra movzx is generated as well as why it stays.
The key here is the bool return value. Apparently, bool datatypes are probably as stored 8-bit values inside the MSVC internal-representation.
Therefore when you implicitly convert from bool to int here:
asd[j%16] = a.test(b);
^^^^^^^^^ ^^^^^^^^^
type int type bool
there is an 8-bit -> 32-bit integer promotion. This is the reason why MSVC generates the movzx instruction.
When the inlining is done manually, the compiler has enough information to optimize out this conversion and keeps everything as a 32-bit datatype IR.
However, when the code is put into it’s own function with a bool return value, the compiler is not able to optimize out the 8-bit intermediate datatype. Therefore, the movzx stays.
When you make both datatypes the same (either int or bool), no conversion is needed. Hence the problem is avoided altogether.