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

时间:2011-06-25 13:16:21

标签: c# .net performance operators

我正在使用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++;
}

哪种效率最高(也最快)?

5 个答案:

答案 0 :(得分:90)

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

1和2相同。

3肯定会慢一些。

话虽如此,每秒只做300次,你就不会注意到任何差异。您是否知道计算机在一秒钟内可以在原始CPU +内存方面做多少?通常,您应该将 clarity 的代码编写为最重要的事情。无论如何都要担心性能 - 但只有当你有办法测量它时,才能a)告诉你是否需要担心,以及b)是否有任何改变实际上提高了性能。

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

答案 1 :(得分:31)

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

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

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

答案 2 :(得分:21)

编译器为1和2生成相同的程序集,可能在选项3中展开循环。遇到这样的问题时,你可以使用一个有用的工具用于经验测试正在进行的是查看编译器生成的程序集。在g ++中,可以使用-S开关来实现。

例如,使用命令g++ -S inc.cpp编译时,选项1和2都会生成此汇编程序(使用g ++ 4.5.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

g ++为选项3产生效率显着降低的汇编程序:


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 instruction在单个指令中进行添加,而不是使用mov进行操作。

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

(看起来你也应该能够inspect the assembly produced by C#,虽然我从来没有尝试过)

答案 3 :(得分:3)

他们是一样的:

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

答案 4 :(得分:2)

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