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

本文关键字:或者 x+1 | 更新日期: 2023-09-27 18:00:27

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

哪一个是最高效(最快)的?

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

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

1和2是等效的。

3肯定会更慢。

话虽如此,每秒只做300次,你不会注意到任何区别。你知道一台计算机在一秒钟内可以完成多少CPU+内存的工作吗?一般来说,您应该编写代码以确保的清晰度是最重要的。无论如何都要担心性能,但只有当你有办法衡量它时,才能a)判断你是否需要担心,b)任何更改是否真的提高了性能。

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

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

认为CCD_ 2比CCD_ 3甚至CCD_。所有优秀的编译器都会将这三条指令转换为相同的代码。

对于加法这样一个琐碎的操作,编写最清晰的代码,让编译器担心它会变快。

编译器应该为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

g++ produces significantly less efficient assembler for option 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

But with optimisation on (even -O1) g++ produces this for all 3 options:


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循环产生更多的代码。

相关文章: