最近一直在做性能优化方面的学习,思绪万千萦绕,所以想记录下来。只是简单聊一聊。

随着接触游戏编程的次数增多,对程序的性能优化也算有了一点正确的理解。回忆起来,我依旧记得之前为了提高程序性能,而做的那些实则无益的“优化”。

下面是一个简单的例子:

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() 的。

现代编译器所能做的优化,远比上面的多得多。因此写出逻辑清晰的源代码,往往便能生成高效的汇编。只有在编译器生成的汇编不符合预期时才考虑去改进源代码。不要在做性能测试之前就尝试进行优化,这样可能会带来糟糕的结果。