简而言之(tl; dr):
我对OP的代码中的注释的解释有些不同,我认为他们声称观察到的“更好的代码”是由于将实际工作移入了“条件”循环。但是,我完全同意,这是非常特定于编译器的,并且它们进行的比较虽然能够生成稍有不同的代码,但大部分都是毫无意义的,而且可能已过时,如下所示。
细节:
很难说原始作者对此do {} while
代码产生更好的代码的评论是什么意思,但是我想推测的方向与此处提出的方向不同-我们认为do {} while
and while {}
循环之间的差异很小(少了一个分支,神秘主义者说),但是这段代码中甚至有一些“ funnier”,这使所有工作都处于这种疯狂的条件下,而内部却空着(do {}
)。
我在gcc 4.8.1(-O3)上尝试了以下代码,它给出了一个有趣的区别-
#include "stdio.h"
int main (){
char buf[10];
char *str = "hello";
char *src = str, *dst = buf;
char res;
do { // loop 1
res = (*dst++ = *src++);
} while (res);
printf ("%s\n", buf);
src = str;
dst = buf;
do { // loop 2
} while (*dst++ = *src++);
printf ("%s\n", buf);
return 0;
}
编译后-
00000000004003f0 <main>:
...
; loop 1
400400: 48 89 ce mov %rcx,%rsi
400403: 48 83 c0 01 add $0x1,%rax
400407: 0f b6 50 ff movzbl 0xffffffffffffffff(%rax),%edx
40040b: 48 8d 4e 01 lea 0x1(%rsi),%rcx
40040f: 84 d2 test %dl,%dl
400411: 88 16 mov %dl,(%rsi)
400413: 75 eb jne 400400 <main+0x10>
...
;loop 2
400430: 48 83 c0 01 add $0x1,%rax
400434: 0f b6 48 ff movzbl 0xffffffffffffffff(%rax),%ecx
400438: 48 83 c2 01 add $0x1,%rdx
40043c: 84 c9 test %cl,%cl
40043e: 88 4a ff mov %cl,0xffffffffffffffff(%rdx)
400441: 75 ed jne 400430 <main+0x40>
...
因此,第一个循环执行7条指令,而第二个循环执行6条指令,即使它们应该执行相同的工作。现在,我真的无法确定这背后是否有一些编译器的智能,可能不是,这只是巧合,但我还没有检查它如何与该项目可能使用的其他编译器选项交互。
另一方面,在clang 3.3(-O3)上,两个循环都生成以下5条指令代码:
400520: 8a 88 a0 06 40 00 mov 0x4006a0(%rax),%cl
400526: 88 4c 04 10 mov %cl,0x10(%rsp,%rax,1)
40052a: 48 ff c0 inc %rax
40052d: 48 83 f8 05 cmp $0x5,%rax
400531: 75 ed jne 400520 <main+0x20>
这只是表明编译器是完全不同的,并且以比几年前某些程序员所预期的快得多的速度前进。这也意味着该评论是毫无意义的,并且可能在那里,因为没有人检查过它是否仍然有意义。
底线-如果您想优化为可能的最佳代码(并且知道它的外观),请直接在汇编中进行操作,并从等式中删除“中间人”(编译器),但要考虑到较新的代码编译器和较新的硬件可能会使此优化过时。在大多数情况下,最好让编译器为您完成这一级别的工作,并专注于优化大工作。
应该指出的另一点-指令计数(假设这是原始OP的代码所遵循的),绝不是衡量代码效率的好方法。并非所有指令都是相同的,并且其中一些指令(例如简单的reg-to-reg移动)确实很便宜,因为它们已由CPU优化。其他优化实际上可能会损害CPU内部优化,因此最终只能进行适当的基准测试。