最近一直在做性能优化方面的学习,思绪万千萦绕,所以想记录下来。只是简单聊一聊。
随着接触游戏编程的次数增多,对程序的性能优化也算有了一点正确的理解。回忆起来,我依旧记得之前为了提高程序性能,而做的那些实则无益的“优化”。
下面是一个简单的例子:
void swap(int *x, int *y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
void swap_by_xor(int *x, int *y)
{
*x ^= *y;
*y ^= *x;
*x ^= *y;
}
两个函数做的工作都是一样:交换两个整型变量的值。第一个函数使用常规方法(利用临时变量)。而第二个函数 swap_by_xor() 使用异或(xor),使得不需用临时变量便可完成交换(异或两次可得到原值),这样也就免去在内存中开辟空间。如果上面的例子改写成交换/翻转数组之类的结构,减少的内存读写次数便会相当可观。
然而,这样考虑并不正确,汇编后的结果是这样的(x86_64 gcc 7.2,O2 优化):
swap:
movl (%rdi), %eax
movl (%rsi), %edx
movl %edx, (%rdi)
movl %eax, (%rsi)
ret
swap_by_xor:
movl (%rdi), %eax
xorl (%rsi), %eax
movl %eax, (%rdi)
xorl (%rsi), %eax
movl %eax, (%rsi)
xorl %eax, (%rdi)
ret
swap() 中的临时变量实际是直接放入了寄存器,并不进入内存;而 swap_by_xor(),对比 swap() 反而多了几条 xor 指令。由此可见,实际用 xor “优化” 不能带来什么好处。虽然两个函数实际执行速度差不多,但就可读性而言,swap() 是完胜 swap_by_xor() 的。
现代编译器所能做的优化,远比上面的多得多。因此写出逻辑清晰的源代码,往往便能生成高效的汇编。只有在编译器生成的汇编不符合预期时才考虑去改进源代码。不要在做性能测试之前就尝试进行优化,这样可能会带来糟糕的结果。