Почему мы должны спекулировать? Мы можем попробовать и выяснить. Я скомпилировал код с gcc -O3 -g
(на x86) и разобрал результат. Произошло больше изменений, чем я ожидал, поэтому я сосредоточусь на кусочках посередине, где мы ожидаем, что большая часть различий между ними будет.
Ядро цикла в первом случае:
0x00000030 <foo+48>: mov %dl,(%edi,%esi,1)
0x00000033 <foo+51>: movzbl 0x1(%ecx),%edx
0x00000037 <foo+55>: inc %eax
0x00000038 <foo+56>: inc %ecx
0x00000039 <foo+57>: mov %eax,%esi
0x0000003b <foo+59>: test %dl,%dl
0x0000003d <foo+61>: jne 0x30 <foo+48>
Ядро цикла во втором случае:
0x00000080 <foo2+48>: mov %dl,(%eax)
0x00000082 <foo2+50>: movzbl 0x1(%ecx),%edx
0x00000086 <foo2+54>: inc %eax
0x00000087 <foo2+55>: inc %ecx
0x00000088 <foo2+56>: test %dl,%dl
0x0000008a <foo2+58>: jne 0x80 <foo2+48>
Исходя из этого, второе, возможно, немного быстрее. Но на самом деле, это не будет иметь большого значения на практике. Кэш L1 прекрасно держит оба цикла, а целевая память не кэшируется, поэтому различия будут спорными. Удачи в измерении разницы между ними.