For anyone who wants to see what black magic compilers can do nowadays, some time ago we made a set of tests with a friend: different, often quite convoluted, implementations of some simple operations. This code below is the only one in the set that has failed to produce optimal output. Coincidently it calls memmove:void swap(unsigned char* v) {
unsigned char tmp;
memmove(&tmp, v, 1);
memmove(v, v + 3, 1);
memmove(v + 3, &tmp, 1);
memmove(&tmp, v + 1, 1);
memmove(v + 1, v + 2, 1);
memmove(v + 2, &tmp, 1);
}
But it wasn’t far from the optimum (2 instructions) and — considering what most people would exect — it does it pretty well. gcc on x86_64 produces: 0: 0f b6 07 movzbl (%rdi),%eax
3: 0f b6 57 03 movzbl 0x3(%rdi),%edx
7: 66 c1 47 01 08 rolw $0x8,0x1(%rdi)
c: 88 17 mov %dl,(%rdi)
e: 88 47 03 mov %al,0x3(%rdi)
11: c3 retq The optimum is, of course, uing the bswap instruction, avoiding two extra moves.
All implementations of swap, including this one, if used on a contant and being static, are completely eliminated and replaced by constants.
This is why doing optimizations before actually detecting a performance problem is seen a bad practice. Modern tools produce output that may be far from what you expect it to be: to the point of replacing whole portions of code with constants. This is not always true and sometimes doing early optimizations may be beneficial, but this is usually if you already know that you did something 20 times and you know that the compiler generated poor code 20 times for that particular solution.