Cop*_*pas 152 c++ optimization perl performance
长版......
一位同事在看到我while (1)在Perl脚本中使用for (;;)更快后断言了.我认为他们应该是一样的,希望翻译能够优化任何差异.我设置了一个脚本,它将运行1,000,000,000次循环迭代和相同数量的while循环并记录它们之间的时间.我找不到明显的区别.我的同事说,一位教授告诉他,这while (1)是在进行比较1 == 1而事实for (;;)并非如此.我们用100倍的C++迭代次数重复相同的测试,差异可以忽略不计.然而,它是一个图形示例,说明编译代码与脚本语言相比可以更快.
精简版...
没有任何理由,更喜欢一个while (1)比一个for (;;),如果你需要一个无限循环打出来的?
注意:如果问题不清楚.这在几个朋友之间纯粹是一次有趣的学术讨论.我知道这不是一个超级重要的概念,所有程序员都应该为之痛苦.感谢所有伟大的答案我(我相信其他人)从这次讨论中学到了一些东西.
更新:前面提到的同事在下面做出回应.
这里引用以防它被埋没.
它来自AMD汇编程序员.他说C程序员(人们)没有意识到他们的代码效率低下.他今天说,gcc编译器非常好,让像他这样的人破产.他举例称,并告诉我关于
while 1VSfor(;;).我现在出于习惯而使用它,但是gcc和特别是解释器在这些日子里都会做同样的操作(处理器跳转),因为它们已经过优化.
bdo*_*lan 216
在perl中,它们会产生相同的操作码:
$ perl -MO=Concise -e 'for(;;) { print "foo\n" }'
a <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 2 -e:1) v ->3
9 <2> leaveloop vK/2 ->a
3 <{> enterloop(next->8 last->9 redo->4) v ->4
- <@> lineseq vK ->9
4 <;> nextstate(main 1 -e:1) v ->5
7 <@> print vK ->8
5 <0> pushmark s ->6
6 <$> const[PV "foo\n"] s ->7
8 <0> unstack v ->4
-e syntax OK
$ perl -MO=Concise -e 'while(1) { print "foo\n" }'
a <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 2 -e:1) v ->3
9 <2> leaveloop vK/2 ->a
3 <{> enterloop(next->8 last->9 redo->4) v ->4
- <@> lineseq vK ->9
4 <;> nextstate(main 1 -e:1) v ->5
7 <@> print vK ->8
5 <0> pushmark s ->6
6 <$> const[PV "foo\n"] s ->7
8 <0> unstack v ->4
-e syntax OK
Run Code Online (Sandbox Code Playgroud)
同样在GCC中:
#include <stdio.h>
void t_while() {
while(1)
printf("foo\n");
}
void t_for() {
for(;;)
printf("foo\n");
}
.file "test.c"
.section .rodata
.LC0:
.string "foo"
.text
.globl t_while
.type t_while, @function
t_while:
.LFB2:
pushq %rbp
.LCFI0:
movq %rsp, %rbp
.LCFI1:
.L2:
movl $.LC0, %edi
call puts
jmp .L2
.LFE2:
.size t_while, .-t_while
.globl t_for
.type t_for, @function
t_for:
.LFB3:
pushq %rbp
.LCFI2:
movq %rsp, %rbp
.LCFI3:
.L5:
movl $.LC0, %edi
call puts
jmp .L5
.LFE3:
.size t_for, .-t_for
.section .eh_frame,"a",@progbits
.Lframe1:
.long .LECIE1-.LSCIE1
.LSCIE1:
.long 0x0
.byte 0x1
.string "zR"
.uleb128 0x1
.sleb128 -8
.byte 0x10
.uleb128 0x1
.byte 0x3
.byte 0xc
.uleb128 0x7
.uleb128 0x8
.byte 0x90
.uleb128 0x1
.align 8
.LECIE1:
.LSFDE1:
.long .LEFDE1-.LASFDE1
.LASFDE1:
.long .LASFDE1-.Lframe1
.long .LFB2
.long .LFE2-.LFB2
.uleb128 0x0
.byte 0x4
.long .LCFI0-.LFB2
.byte 0xe
.uleb128 0x10
.byte 0x86
.uleb128 0x2
.byte 0x4
.long .LCFI1-.LCFI0
.byte 0xd
.uleb128 0x6
.align 8
.LEFDE1:
.LSFDE3:
.long .LEFDE3-.LASFDE3
.LASFDE3:
.long .LASFDE3-.Lframe1
.long .LFB3
.long .LFE3-.LFB3
.uleb128 0x0
.byte 0x4
.long .LCFI2-.LFB3
.byte 0xe
.uleb128 0x10
.byte 0x86
.uleb128 0x2
.byte 0x4
.long .LCFI3-.LCFI2
.byte 0xd
.uleb128 0x6
.align 8
.LEFDE3:
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)
所以我猜答案是,它们在许多编译器中都是一样的.当然,对于其他一些编译器,情况可能不一定如此,但是循环内部的代码可能比循环本身要贵几千倍,所以谁在乎呢?
Mar*_*ote 55
使用GCC,它们似乎都编译为相同的汇编语言:
L2:
jmp L2
Run Code Online (Sandbox Code Playgroud)
Bil*_*ard 53
没有太多理由偏爱一个而不是另一个.我确实认为,while(1)尤其while(true)是更具可读性for(;;),但这只是我的偏好.
Ric*_*den 31
根据标准没有区别.6.5.3/1有:
for语句
for ( for-init-statement ; conditionopt ; expressionopt ) statement
Run Code Online (Sandbox Code Playgroud)
相当于
{
for-init-statement
while ( condition ) {
statement
expression ;
}
}
Run Code Online (Sandbox Code Playgroud)
6.5.3/2有:
可以省略条件和表达式中的任何一个或两者.缺失条件使隐含的while子句等同于while(true).
所以根据C++标准的代码:
for (;;);
Run Code Online (Sandbox Code Playgroud)
与以下内容完全相同:
{
while (true) {
;
;
}
}
Run Code Online (Sandbox Code Playgroud)
sea*_*n e 28
用于发出警告的Visual C++编译器
while (1)
Run Code Online (Sandbox Code Playgroud)
(常数表达式)但不是
for (;;)
Run Code Online (Sandbox Code Playgroud)
for (;;)由于这个原因,我继续选择优先考虑的做法,但我不知道这些天编译器是否仍然这样做.
Lut*_* L. 20
使用这些旧编译器的Turbo C会for(;;)产生更快的代码while(1).
今天gcc,Visual C(我认为几乎所有)编译器都很好地优化了,很少使用4.7 MHz的CPU.
在那些日子里,a for( i=10; i; i-- )比for( i=1; i <=10; i++ )由于比较i为0 更快,导致CPU-Zero-Flag条件跳转.并且使用最后的递减操作修改零标志( i-- ),不需要额外的cmp操作.
call __printf_chk
decl %ebx %ebx=iterator i
jnz .L2
movl -4(%ebp), %ebx
leave
Run Code Online (Sandbox Code Playgroud)
这里for(i=1; i<=10; i++)有额外的cmpl:
call __printf_chk
incl %ebx
cmpl $11, %ebx
jne .L2
movl -4(%ebp), %ebx
leave
Run Code Online (Sandbox Code Playgroud)
Ken*_*ric 13
对于所有人争论你不应该使用indefinte while循环,并建议像使用开放转到的愚蠢的东西(严重的,哎哟)
while (1) {
last if( condition1 );
code();
more_code();
last if( condition2 );
even_more_code();
}
Run Code Online (Sandbox Code Playgroud)
无法以任何其他方式真正有效地表达.不是没有创建退出变量并且做黑魔法以保持同步.
如果您喜欢更多goto-esque语法,请使用限制范围的合理内容.
flow: {
if ( condition ){
redo flow;
}
if ( othercondition ){
redo flow;
}
if ( earlyexit ){
last flow;
}
something(); # doesn't execute when earlyexit is true
}
Run Code Online (Sandbox Code Playgroud)
最终速度并不重要
考虑速度明智的不同循环结构是多么有效浪费时间.过早优化通过.我想不出任何我见过的情况,在我选择的循环结构中,分析代码找到了瓶颈.
一般来说其如何循环和什么样的循环.
您应该"优化"可读性和简洁性,并将最好的解释问题的内容写入找到代码的下一个可怜的傻瓜.
如果你使用有人提到的"goto LABEL"技巧,我必须使用你的代码,准备好一只眼睛打开睡觉,特别是如果你不止一次这样做,因为那种东西会产生可怕的意大利面条代码.
仅仅因为你可以创建意大利面条代码并不意味着你应该
来自Stroustrup,TC++ PL(第3版),§6.1.1:
好奇的符号
for (;;)是指定无限循环的标准方法; 你可以发音"永远".[...]while (true)是另一种选择.
我更喜欢for (;;).
如果编译器没有进行任何优化,for(;;)那么总是会比while(true).这是因为while语句每次都会计算条件,但for-statement是无条件跳转.但是如果编译器优化控制流,它可能会生成一些操作码.您可以非常轻松地阅读反汇编代码.
PS你可以像这样写一个无限循环:
#define EVER ;;
//...
for (EVER) {
//...
}
Run Code Online (Sandbox Code Playgroud)
我曾经听过这个.
它来自AMD汇编程序员.他表示,C程序员(人员)没有意识到他们的代码效率低下.他今天说,gcc编译器非常好,让像他这样的人破产.他举例称,并告诉我关于while 1VS for(;;).我现在出于习惯而使用它,但是gcc和特别是解释器在这些日子里都会做同样的操作(处理器跳转),因为它们已经过优化.
刚刚遇到这个线程(虽然晚了好几年)。
\n\n我想我找到了“for(;;)”比“while(1)”更好的真正原因。
\n\n根据“2018年巴尔编码标准”
\n\nKernighan & Ritchie long ago recommended for (;;) , which has the additional benefit\nof insuring against the visually-confusing defect of a while (l); referencing a variable \xe2\x80\x98l\xe2\x80\x99.\nRun Code Online (Sandbox Code Playgroud)\n\n基本上,这不是速度问题,而是可读性问题。根据代码的字体/打印,数字一(1)有时可能看起来像小写字母 l。
\n\n即1 vs l。(在某些字体中,它们看起来相同)。
\n\n所以 while(1) 可能看起来像一些依赖于变量字母 L 的 while 循环。
\n\nwhile(true) 也可能有效,但在某些较旧的 C 和嵌入式 C 情况下,除非包含 stdbool.h,否则尚未定义 true/false。
\n| 归档时间: |
|
| 查看次数: |
47349 次 |
| 最近记录: |