为什么编译器会在编译的汇编代码中生成额外的 sqrts
Why does compiler generate additional sqrts in the compiled assembly code
我正在尝试使用以下简单的 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
在 Intel 机器上,结果(如下所示)令人惊讶?
为什么汇编代码中有两个sqrt,一个使用sqrtsd
指令,一个使用函数调用?是否与循环展开和尝试有关在一次迭代中执行两个 sqrts?
以及如何理解行
ucomisd %xmm0, %xmm0
为什么它会将 %xmm0
与自身进行比较?
//----------------start of for loop----------------
call readTSC
movq %rax, -32(%rbp)
movl [=13=], -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 , -4(%rbp)
.L4:
movl -4(%rbp), %eax
cmpl -36(%rbp), %eax
jl .L6
//----------------end of for loop----------------
call readTSC
它正在使用库 sqrt
函数进行错误处理。请参阅 glibc 的文档:20.5.4 Error Reporting by Mathematical Functions: math functions set errno
for compatibility with systems that don't have IEEE754 exception flags. Related: glibc's 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
与自身进行比较将 return UNORDERED
,这就是您尝试对负数求平方根时将得到的结果。这包含在 jp
分支中。 je
检查只是偏执狂,检查是否完全相等。
另请注意,gcc 有一个 -fno-math-errno
option,它将牺牲此错误处理以提高速度。此选项是 -ffast-math
的一部分,但可以在不启用任何结果更改优化的情况下单独使用。
sqrtsd
自身正确地为负输入和 NaN 输入生成 NaN,并设置 IEEE754 无效标志。检查和分支仅 以保留大多数代码不依赖的 errno
设置语义。
-fno-math-errno
是 Darwin (OS X) 上的默认值,其中数学库从不设置 errno
,因此可以在没有此检查的情况下内联函数。
我正在尝试使用以下简单的 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
在 Intel 机器上,结果(如下所示)令人惊讶?
为什么汇编代码中有两个sqrt,一个使用sqrtsd
指令,一个使用函数调用?是否与循环展开和尝试有关在一次迭代中执行两个 sqrts?
以及如何理解行
ucomisd %xmm0, %xmm0
为什么它会将 %xmm0
与自身进行比较?
//----------------start of for loop----------------
call readTSC
movq %rax, -32(%rbp)
movl [=13=], -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 , -4(%rbp)
.L4:
movl -4(%rbp), %eax
cmpl -36(%rbp), %eax
jl .L6
//----------------end of for loop----------------
call readTSC
它正在使用库 sqrt
函数进行错误处理。请参阅 glibc 的文档:20.5.4 Error Reporting by Mathematical Functions: math functions set errno
for compatibility with systems that don't have IEEE754 exception flags. Related: glibc's 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
与自身进行比较将 return UNORDERED
,这就是您尝试对负数求平方根时将得到的结果。这包含在 jp
分支中。 je
检查只是偏执狂,检查是否完全相等。
另请注意,gcc 有一个 -fno-math-errno
option,它将牺牲此错误处理以提高速度。此选项是 -ffast-math
的一部分,但可以在不启用任何结果更改优化的情况下单独使用。
sqrtsd
自身正确地为负输入和 NaN 输入生成 NaN,并设置 IEEE754 无效标志。检查和分支仅 以保留大多数代码不依赖的 errno
设置语义。
-fno-math-errno
是 Darwin (OS X) 上的默认值,其中数学库从不设置 errno
,因此可以在没有此检查的情况下内联函数。