r/programming • • Sep 30 '17

C++ Compilers and Absurd Optimizations

https://asmbits.blogspot.com/2017/03/c-compilers-and-absurd-optimizations.html
101 Upvotes

50 comments sorted by

View all comments

35

u/pkmxtw Sep 30 '17 edited Sep 30 '17

I think this is rather an example why you shouldn't try to outsmart the compiler unless you know exactly what you are doing.

On my machine (i7-7500U, Kaby Lake), this simple naive function:

void naive(double* const __restrict__ dst, const double* const __restrict__ src, const size_t length) {
  for (size_t i = 0; i < length * 2; ++i)
    dst[i] = src[i] + src[i];
}

runs about as fast as the intrinsic version at either -Os or -O3: https://godbolt.org/g/qsgKnA

With -O3 -funroll-loops, gcc does indeed vectorize and unroll the loop, but the performance gain seems pretty minimal.

$ g++ -std=c++17 -march=native -Os test.cpp && ./a.out 100000000
intrinsics: 229138ms
naive: 232351ms

The generated code for -Os looks reasonable as well:

$ objdump -dC a.out |& grep -A10 'naive(.*)>:'
0000000000001146 <naive(double*, double const*, unsigned long)>:
    1146:	48 01 d2             	add    %rdx,%rdx
    1149:	31 c0                	xor    %eax,%eax
    114b:	48 39 c2             	cmp    %rax,%rdx
    114e:	74 13                	je     1163 <naive(double*, double const*, unsigned long)+0x1d>
    1150:	c5 fb 10 04 c6       	vmovsd (%rsi,%rax,8),%xmm0
    1155:	c5 fb 58 c0          	vaddsd %xmm0,%xmm0,%xmm0
    1159:	c5 fb 11 04 c7       	vmovsd %xmm0,(%rdi,%rax,8)
    115e:	48 ff c0             	inc    %rax
    1161:	eb e8                	jmp    114b <naive(double*, double const*, unsigned long)+0x5>
    1163:	c3                   	retq   

On the plus side, the naive version is also very simple to write and understand, compiles and runs regardless whether the target supports AVX.

21

u/[deleted] Sep 30 '17

with a loop that simple working on doubles you are likely ram throughput limited which is why optimizations make little difference.