我经常看到代码将整数转换为双精度,再将整数转换为双精度,然后再转换回来(有时是出于很好的原因,有时不是),我突然意识到这似乎是我的程序中的一个“隐藏”成本。让我们假设转换方法是截断。
那么,它到底有多贵呢?我敢肯定这取决于硬件,所以让我们假设一个新的英特尔处理器(哈斯韦尔,如果你喜欢,但我会采取任何东西)。一些我感兴趣的指标(尽管一个好的答案不需要所有的指标):
(生成的算术周期使用的相对成本与基本算术运算的比较)
我还假设,考虑到我们每秒可以执行的计算量与每秒实际到达CPU的数据量之间的差异,我们最能感受到缓慢转换的影响的方式应该是功耗,而不是执行速度。
发布于 2015-02-23 14:55:48
这是我自己能挖掘到的,对于x86-64用SSE2 (而不是传统的x87,因为改变C++的截断语义的舍入模式代价很高)执行FP数学:
int
to double
,它可以归结为一条指令:cvttsd2si
。从double
到int
,这就是cvtsi2sd
。(适用于32位操作数大小的cvtsi2sd
的cvtsi2sdl
AT&T语法。)
通过自动矢量化,我们得到了cvtdq2pd
。
所以我想问题变成了:这些
addsd
加上一个movq xmm, r64
(fp <-整数)或movq r64, xmm
(整数<- fp),因为它们在相同的端口上,在主流(沙桥/哈斯韦尔/斯莱克)英特尔CPU上解码为2个uop。Intel® 64 and IA-32 Architectures Optimization Reference Manual说cvttsd2si
指令成本是5延迟(见附录C-16)。根据您的架构不同,cvtsi2sd
的延迟从Silvermont上的1到其他几种架构上的7-16不等。
Agner Fog's instruction tables有更准确/合理的数字,如Silvermont上cvtsi2sd
的5周期延迟(每2个时钟吞吐量1个),或Haswell上的4c延迟,每个时钟吞吐量1个(如果您避免对目标寄存器的依赖与旧的上半部分合并,就像gcc通常对pxor xmm0,xmm0
所做的那样)。
SIMD packedfloat
to packedint
是很棒的;单uop。但转换为double
需要重新洗牌才能更改元素大小。SIMD float/double<->int64_t在AVX512之前不存在,但可以在有限范围内手动完成。
英特尔的手册将延迟定义为:“执行核心完成构成指令的所有μ操作的执行所需的时钟周期数”。但一个更有用的定义是从输入到输出准备就绪的时钟数。如果乱序执行有足够的并行性来完成它的工作,吞吐量比延迟更重要:What considerations go into predicting latency for operations on modern superscalar processors and how can I calculate them by hand?.
add
指令花费1延迟,整数imul
花费3(附录C-27)。在Skylake上,FP addsd
和mulsd
以每时钟2个的吞吐量运行,具有4个周期延迟。SIMD版本与FMA相同,具有128或256位矢量。在哈斯韦尔,addsd
/ addpd
每时钟吞吐量只有1个,但由于有一个专用的FP-add单元,所以有3个周期延迟。
因此,答案可以归结为:
1)它是硬件优化的,并且编译器利用了硬件机制。
2)在一个方向上的周期数和在另一个方向上的高度可变的数量(取决于您的体系结构)方面,它只比乘法多一点。它的成本既不是免费的,也不是荒谬的,但考虑到以一种不明显的方式编写导致成本的代码是多么容易,它可能需要更多的关注。
https://stackoverflow.com/questions/28668348
复制相似问题