【发布时间】:2014-03-10 03:58:04
【问题描述】:
高,谁能帮我理解为什么调用数学库函数比编写内联汇编代码来执行相同的操作更有效?我写了这个简单的测试:
#include <stdio.h>
#define __USE_GNU
#include <math.h>
void main( void ){
float ang;
int i;
for( i = 0; i< 1000000; i++){
ang = M_PI_2 * i/2000000;
/*__asm__ ( "fld %0;"
"fptan;"
"fxch;"
"fstp %0;" : "=m" (ang) : "m" (ang)
) ;*/
ang = tanf(ang);
}
printf("Tan(ang): %f\n", ang);
}
该代码以两种不同的方式计算角度的正切,一种调用动态链接库 libm.a 中的 tanf 函数,另一种使用内联汇编代码。请注意,我会交替注释部分代码。 代码多次执行该操作以使用命令 time y linux 终端获得有意义的结果。
使用数学库的版本大约需要 0.040 秒。 使用汇编代码的版本大约需要 0.440 秒;十倍以上。
这些是反汇编的结果。两者都使用 -O3 选项编译。
LIBM
4005ad: b8 db 0f c9 3f mov $0x3fc90fdb,%eax
4005b2: 89 45 f8 mov %eax,-0x8(%rbp)
4005b5: f3 0f 10 45 f8 movss -0x8(%rbp),%xmm0
4005ba: e8 e1 fe ff ff callq 4004a0 <tanf@plt>
4005bf: f3 0f 11 45 f8 movss %xmm0,-0x8(%rbp)
4005c4: 83 45 fc 01 addl $0x1,-0x4(%rbp)
4005c8: 83 7d fc 00 cmpl $0x0,-0x4(%rbp)
4005cc: 7e df jle 4005ad <main+0x19>
ASM
40050d: b8 db 0f c9 3f mov $0x3fc90fdb,%eax
400512: 89 45 f8 mov %eax,-0x8(%rbp)
400515: d9 45 f8 flds -0x8(%rbp)
400518: d9 f2 fptan
40051a: d9 c9 fxch %st(1)
40051c: d9 5d f8 fstps -0x8(%rbp)
40051f: 83 45 fc 01 addl $0x1,-0x4(%rbp)
400523: 83 7d fc 00 cmpl $0x0,-0x4(%rbp)
400527: 7e e4 jle 40050d <main+0x19>
有什么想法吗?谢谢。
我想我有个主意。浏览 glibc 代码我发现 tanf 函数是通过多项式逼近和使用 sse 扩展来实现的。我想这会比 fptan 指令的微码更快。
【问题讨论】:
-
你拆机看看有没有优化?
-
也许它不能优化混合汇编和C。但是可以自己优化C并且忽略除了最后一个循环之外的所有内容。
-
不需要优化编译器来计算您从未使用过的结果,因此无法保证除了最后一次之外正在执行该操作。但说到预期的问题,数学库的作者可能比你更擅长编写高效的汇编代码。
-
我使用完全优化 (-O3) 编译了两个版本。我反汇编了这两个目标文件,我看到两个都存在循环。我在原始问题中附上了部分反汇编清单。
-
请帮我理解汇编代码:根据 i,-0x4(%rbp) 计算的 ang,-0x8(%rbp) 的当前值究竟在哪里?还是优化编译器检测到 i/2000000 作为整数除法的结果始终为零,因此 ang=0 被加载为预先计算的常量?最好使用 M_PI/2000000*i。
标签: math gcc optimization assembly