哪个更快? ++,+ =或x + 1?

我正在使用C#(这个问题也适用于像C ++这样的类似语言),我试图找出最快,最有效的增量方法。 在我的游戏中,它不仅仅是一个或两个增量,而是每秒300个增量。 就像屏幕上每个精灵的帧都在递增,我的RPG角色的速度和位置,相机的偏移等等。所以我在想,最有效的方法是什么? 例如,对于我可以做的每个动作增加5 y_pos

1。

 Player.YPos += 5; 

2。

 Player.YPos = Player.YPos + 5; 

3。

 for (int i = 0; i < 5; i++) { Player.YPos++; } 

哪个是最有效的(也是最快的)?

(特定于C#作为C ++的答案可能会有很大差异。)

1和2是等价的。

3肯定会慢一些。

话虽如此,每秒只做300次,你不会发现任何差异。 您是否知道计算机在一秒内可以在原始CPU +内存方面做多少? 一般来说,为了清晰起见 ,您应该编写代码作为最重要的事情。 无论如何都要担心性能 – 但只有当你有办法测量它时,才能a)告诉你是否需要担心,以及b)是否有任何改变实际上改善了性能。

在这种情况下,我会说选项1是最清晰的,所以这就是我使用的。

选项1和2将导致编译器生成相同的代码。 选项3将慢得多。

这是一个谬论, i++i += 1甚至i = i + 1更快。 所有体面的编译器都会将这三个指令转换为相同的代码。

对于像添加这样的简单操作,编写最清晰的代码并让编译器担心使其快速。

编译器应该为1和2生成相同的程序集,它可以在选项3中展开循环。当遇到这样的问题时,您可以使用一个有用的工具来实际测试正在发生的事情是查看编译器生成的程序集。 在g ++中,这可以使用-S开关来实现。

例如,当使用命令g++ -S inc.cpp (使用g ++ 4.5.2)编译时,选项1和2都生成此汇编程序

 main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 movq %rsp, %rbp .cfi_offset 6, -16 .cfi_def_cfa_register 6 addl $5, -4(%rbp) movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc 

对于选项3,g ++生成效率明显较低的汇编程序:

 main: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 movq %rsp, %rbp .cfi_offset 6, -16 .cfi_def_cfa_register 6 movl $0, -8(%rbp) jmp .L2 .L3: addl $1, -4(%rbp) addl $1, -8(%rbp) .L2: cmpl $4, -8(%rbp) setle %al testb %al, %al jne .L3 movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc 

但是通过优化(偶数-O1),g ++可以为所有3个选项生成:

 main: .LFB0: .cfi_startproc leal 5(%rdi), %eax ret .cfi_endproc 

g ++不仅在选项3中展开循环,而且它还使用lea指令在单个指令中进行添加,而不是使用mov

因此g ++将始终为选项1和2生成相同的程序集。只有在明确打开优化(这是您可能期望的行为)时,g ++才会为所有3个选项生成相同的程序集。

(看起来你应该能够检查C#生成的程序集 ,虽然我从来没有尝试过)

他们是一样的:

 static void Main(string[] args) { int a = 0; a++; a +=1; a = a+1; } 

ILSpy中的上述代码是:

 private static void Main(string[] args) { int a = 0; a++; a++; a++; } 

此外,所有这些的IL也是相同的(在发布模式下):

 .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 15 (0xf) .maxstack 2 .locals init ([0] int32 a) IL_0000: ldc.i4.0 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: ldc.i4.1 IL_0004: add IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldc.i4.1 IL_0008: add IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: ldc.i4.1 IL_000c: add IL_000d: stloc.0 IL_000e: ret } // end of method Program::Main 

选项1和2将在编译后生成相同的代码。 选项3会慢得多,因为它会导致涉及for循环的更多代码。