我有一个C / C ++程序,它涉及强烈的 32位浮点矩阵数学计算,如加法,减法,乘法,除法等。
我可以通过将 32位浮点数字转换为 16位定点数字来加速我的程序吗?我可以获得多少速度增益?
目前我正在使用Intel I5 CPU。我使用Openblas来执行矩阵计算。我应该如何重新实现Openblas函数,例如 cblas_dgemm 来执行定点计算?
我知道SSE(简单SIMD扩展)一次运行4x32 = 8x16 = 128位数据,即4个32位浮点类型或8个16位定点类型。我想在从32位浮点转换为16位定点后,我的程序会快两倍。
答案 0 :(得分:6)
总结:即使每个矢量的元素数量是原来的两倍,现代FPU硬件很难被定点击败。
现代BLAS库通常可以很好地调整缓存性能(使用缓存阻塞/循环平铺)以及指令吞吐量。这使他们非常难以击败。特别是DGEMM有很多空间进行这种优化,因为它对O(N ^ 2)数据进行O(N ^ 3)处理,因此值得转换一个输入的缓存大小的块,并且像这样的东西。
可能的帮助是通过以16位半浮点格式存储浮点数来减少内存瓶颈。没有硬件支持以这种格式对它们进行数学运算,只是在加载/存储时,只需要在格式和普通32位元素浮点向量之间进行转换的几条指令:VCVTPH2PS (__m256 _mm256_cvtph_ps(__m128i)
)和VCVTPS2PH (__m128i _mm256_cvtps_ph(__m256 m1, const int imm8_rounding_control)
。这两条指令包括F16C extension,首先由AMD Bulldozer和Intel IvyBridge支持。
IDK,如果任何BLAS库支持该格式。
SSE / AVX没有任何整数除法指令。如果你只是除以常数,你可能不需要真正的div指令。这是固定点的一个主要绊脚石。
定点的另一个重大缺点是在乘法之后转移以校正十进制(二进制?)点的位置的额外成本。对于每个矢量具有16位定点的两倍元素,可以获得任何增益。
SSE / AVX实际上有很多选择的打包16位乘法(比任何其他元素大小更好)。那些打包的乘法产生低半部分,高半部分(有符号或无符号),甚至一个从顶部下面的2比特取16位,带有舍入(PMULHRSW.html)。 Skylake每时钟运行两次,具有5个周期延迟。还有整数乘加指令,但它们在乘法结果对之间进行水平加法。 (请参阅Agner Fog's insn tables,以及x86标记维基以获取性能链接。)Haswell和之前不具有尽可能多的整数向量加法和乘法执行单元。通常是总uop吞吐量的代码瓶颈,而不是特定的执行端口。 (但是一个好的BLAS库甚至可以手动调整asm。)
如果您的输入和输出是整数,那么使用整数向量通常会更快,而不是转换为浮点数。 (例如,请参阅Scaling byte pixel values (y=ax+b) with SSE2 (as floats)?上的答案,其中我使用16位定点来处理8位整数。)
但是,如果您真的使用浮点数,并且需要进行大量的乘法和除法,那么只需使用硬件FPU即可。它们在现代CPU中具有惊人的强大功能,并且已经使定点在大多数任务中都过时了。正如@Iwill所指出的那样,FMA指令是FP吞吐量(有时甚至延迟)的另一大推动力。
整数加/减/比较指令(但不是乘法)的延迟也低于它们的FP对应物。