“靠汇编获得高效率”纯属谬论
gigix 2003-04-11 10:15:05 Solstice:
关于汇编、优化、效率等议题,再聊两句:
Computer Organization & Design 2/e by Hennessy & Patterson 一书中,186页:
Fallacy: Write in assembly language to obtain the highest performance.
Fallacy 是“谬论”的意思。
This battle between compilers and assembly language coders is one situation in which humans are losing ground. 在编译器与汇编语言编码员的抗衡中,人类的领地正在缩小。一个汇编程序员要想与编译器竞争,至少深入了解流水线、内存架构(即cache)。
现在芯片技术发展太快了,以至于以前的“常识”变成了认识的误区:整数运算比浮点运算快,加法比乘法快,单精度浮点数比双精度快等等。
而具我实测的结果,整数的加法、减法、乘法几乎一样快;浮点数(无论单双精度)的加法、减法、乘法几乎一样快;而且,浮点数的乘法和整数加法几乎一样快。这一现象,使得某些“优化行为”(用数次加法取代一次乘法)实际上变成“劣化”。
目前看来,除法和整数的取模运算要慢得多,但或许过不了几年,这种认识又将成为历史。
cache对程序性能的影响,我举一个亲身遇到的例子(我的CPU的L2 Cache = 256k):
FFT算法的运算量是N log N,N 是2的整数幂。
如果用单精度浮点数(float)执行运算,当 N <= 32768 时,运算时间确实是符合 N log N 的规律,但如果 N >= 65536,速度立刻下降 7~9 倍。
如果用双精度浮点数(double)执行运算,当 N <= 16384 时,运算时间确实是符合 N log N 的规律,但如果 N >= 32768,速度下降 4~10 倍。
用多个编译器进行测试,结果相近。为什么会这样?
float的情况 sizeof(float) == 4:32768 * 4 * 2 = 256k (乘以2是因为一个复数的实部和虚部各占一个float)
sizeof(double) == 8 : 16384 * 8 * 2 = 256k。
可以看出,速度的转折点处,数据量正好等于CPU L2 Cache的大小。
我又在一台 CPU L2 Cache = 512k 的机器上进行测试,这次 float 的阈值是65536,double 的阈值是32768。又正好和 Cache 大小匹配。有多少人写程序(尤其在“优化”程序时)考虑到了CPU Cache的影响?至少,密集操作的数据要放在一起,
字节的对齐,有时也会成问题。x86中,存放double类型的数据的地址,最好是8的倍数,否则影响速度。比如VC和GCC的malloc返回的指针是按8字节对齐,而BCC5.5是按4字节对齐。结果使得BCC版的FFT程序,如果使用double类型,那么有时(当malloc返回的地址是4的奇数倍的时候)会出现执行速度慢一倍的情况。当然,这个问题解决起来比较容易。
指令的调度(scheduling)也是一个大问题。例如有的 CPU 执行一次加法只要1个周期,但是在接下来的两个周期内,不能立即取用答数。如果在这种机器上写汇编程序,在执行加法之后,立即取用答数,会空等(浪费)两个周期。指令调度还包括合理利用 Pentium的U V两条流水线,使相邻指令能并行执行繁杂议题等等。而Pentium是1993年上市的,距今已有10年,10年来,CPU又复杂了许多。
我要说的是,不要想当然地认为汇编就会比较快,指令级的优化非常复杂、琐碎,这种优化绝大多数情况应该交给编译器去做。只有编译器(尤其是CPU厂商自己的编译器、实力数一数二的大公司的编译器或是广泛使用的经过众多一流黑客高手调校的免费编译器)才能同时兼顾许许多多的优化细则。70 年代以来,主流的操作系统(Unix系列从早期的Version 5、7到SVR4、BSD4.4,Windows系列等等)都主要采用 C 来编写(辅以少量的汇编代码,一般 5%以下)也可算是一个佐证。
Kernighan 对他的学生们说(见他的课程主页):
Don't worry about performance if it doesn't
matter (and it often doesn't).
When it does,
- get the algorithm right
- use the compiler's optimization
- code tune, as a last resort
Kernighan 这里甚至都没有提到 assembly language 。