哪个更快?++,+=或者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++;
}
哪一个是最高效(最快)的?
(特定于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循环产生更多的代码。