【发布时间】:2013-05-11 22:41:42
【问题描述】:
我正在尝试优化一些应该从内存中读取单精度浮点数并以双精度对它们执行算术运算的代码。这正成为一个重要的性能瓶颈,因为以单精度在内存中存储数据的代码比以双精度在内存中存储数据的等效代码要慢。下面是一个玩具 C++ 程序,它抓住了我的问题的本质:
#include <cstdio>
// noinline to force main() to actually read the value from memory.
__attributes__ ((noinline)) float* GetFloat() {
float* f = new float;
*f = 3.14;
return f;
}
int main() {
float* f = GetFloat();
double d = *f;
printf("%f\n", d); // Use the value so it isn't optimized out of existence.
}
GCC 和 Clang 都将 *f 的加载和转换为双精度作为两个单独的指令执行,即使 cvtss2sd 指令支持内存作为源参数。根据Agner Fog,cvtss2sd r, m 在大多数架构上的执行速度与movss r, m 一样快,并且避免了需要在后面执行cvtss2sd r, r。尽管如此,Clang 为main() 生成了以下代码:
main PROC
push rbp ;
mov rbp, rsp ;
call _Z8GetFloatv ;
movss xmm0, dword ptr [rax] ;
cvtss2sd xmm0, xmm0 ;
mov edi, offset ?_001 ;
mov al, 1 ;
call printf ;
xor eax, eax ;
pop rbp ;
ret ;
main ENDP
GCC 生成同样低效的代码。为什么这些编译器都不简单地生成类似cvtss2sd xmm0, dword ptr [rax] 的东西?
编辑:很好的答案,斯蒂芬佳能!我将 Clang 的汇编语言输出用于我的实际用例,将其作为内联 ASM 粘贴到源文件中,对其进行基准测试,然后进行此处讨论的更改并再次对其进行基准测试。我不敢相信cvtss2sd [memory] 居然更慢。
【问题讨论】:
标签: performance assembly x86-64 sse