我正在尝试优化一些应该从内存中读取单精度浮点数并以双精度对它们执行算术的代码。这正在成为一个重要的性能瓶颈,因为将数据作为单精度存储在内存中的代码比将数据作为双精度存储在内存中的等效代码要慢得多。下面是一个玩具 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]
实际上更慢。