为什么编译器在编译的汇编代码中生成额外的sqrts
我正在尝试使用以下简单的C代码来分析计算sqrt所需的时间,其中readTSC()是一个读取CPU循环计数器的函数。
double sum = 0.0; int i; tm = readTSC(); for ( i = 0; i < n; i++ ) sum += sqrt((double) i); tm = readTSC() - tm; printf("%lld clocks in total\n",tm); printf("%15.6e\n",sum);
但是,当我使用打印出汇编代码时
gcc -S timing.c -o timing.s
在英特尔机器上,结果(如下所示)令人惊讶?
为什么汇编代码中有两个sqrts,一个使用sqrtsd
指令而另一个使用函数调用? 它是否与循环展开和尝试在一次迭代中执行两个sqrts相关?
以及如何理解这条线
ucomisd %xmm0, %xmm0
为什么将%xmm0
与自身进行比较?
//----------------start of for loop---------------- call readTSC movq %rax, -32(%rbp) movl $0, -4(%rbp) jmp .L4 .L6: cvtsi2sd -4(%rbp), %xmm1 // 1. use sqrtsd instruction sqrtsd %xmm1, %xmm0 ucomisd %xmm0, %xmm0 jp .L8 je .L5 .L8: movapd %xmm1, %xmm0 // 2. use C funciton call call sqrt .L5: movsd -16(%rbp), %xmm1 addsd %xmm1, %xmm0 movsd %xmm0, -16(%rbp) addl $1, -4(%rbp) .L4: movl -4(%rbp), %eax cmpl -36(%rbp), %eax jl .L6 //----------------end of for loop---------------- call readTSC
它使用库sqrt
函数进行error handling。 请参阅glibc的文档: 20.5.4按数学函数报告错误 :数学函数设置errno
以与没有IEEE754exception标志的系统兼容。 相关:glibc的math_error(7)
手册页。
作为优化,它首先尝试通过内联sqrtsd
指令执行平方根,然后使用ucomisd
指令检查结果,该指令设置标志如下:
CASE (RESULT) OF UNORDERED: ZF,PF,CF 111; GREATER_THAN: ZF,PF,CF 000; LESS_THAN: ZF,PF,CF 001; EQUAL: ZF,PF,CF 100; ESAC;
特别是,将QNaN
UNORDERED
自身进行比较将返回UNORDERED
,如果您尝试取负数的平方根,则会得到UNORDERED
。 这由jp
分支涵盖。 je
检查只是偏执狂,检查确切的平等。
另请注意,gcc有一个-fno-math-errno
选项 ,它会牺牲此error handling速度。 此选项是-ffast-math
一部分,但可以单独使用而不启用任何结果更改优化。
sqrtsd
本身正确地为负和NaN输入生成NaN,并设置IEEE754无效标志。 检查和分支只是为了保留大多数代码不依赖的errno
设置语义。
-fno-math-errno
是Darwin(OS X)的默认值,其中数学库从不设置errno
,因此可以在没有此检查的情况下内联函数。