我仍在使用C ++中任意长整数的例程。到目前为止,我已经为64位Intel CPU实现了加/减和乘法。
一切正常,但我想知道我是否可以通过使用SSE来加快速度。我浏览了SSE文档和处理器指令列表,但我找不到任何我认为可以使用的内容,这就是原因:
SSE有一些整数指令,但大多数指令处理浮点数。看起来它不是设计用于整数(例如,是否有较小的整数比较?)
SSE的想法是SIMD(相同指令,多个数据),因此它提供了2或4个独立操作的指令。另一方面,我希望有一个像128位整数加(128位输入和输出)的东西。这似乎不存在。 (然而?在AVX2中可能?)
整数加法和减法既不处理输入也不处理输出。因此,手动操作非常麻烦(因而也很慢)。
我的问题是:我的评估是正确的还是我忽略了什么?长整数例程可以从SSE中受益吗?特别是,他们可以帮我写一个更快的添加,分或更常规吗?
答案 0 :(得分:25)
在过去,这个问题的答案是坚实的,“不”。但截至2017年,情况正在发生变化。
但在我继续之前,需要一些背景术语:
全字算术:
这是标准表示,其中数字使用32位或64位整数数组存储在基数2 32 或2 64 中。 许多bignum库和应用程序(包括GMP)都使用此表示形式。
在全字表示中,每个整数都有唯一的表示。比较等操作很简单。但是由于需要进行传播,因此像添加这样的东西会更加困难。
正是这种进位传播使得bignum算术几乎不可能进行矢量化。
部分字算术
这是一种较少使用的表示,其中数字使用小于硬件字大小的基数。例如,在每个64位字中只放置60位。或者使用带有32位字大小的基数1,000,000,000
进行十进制算术。
GMP的作者称之为“钉子”,其中“钉子”是该词的未使用部分。
过去,部分字算法的使用主要限于在非二进制基础上工作的应用程序。但是现在,它变得越来越重要,因为它允许延迟传输传播。
全字算术问题:
传统的全字算术历来是一个失败的原因:
* AVX512-DQ增加了一个下半部64x64位乘法。但是仍然没有上半部分指令。
此外,x86 / x64有很多专门用于bignums的标量指令:
adc
,adcx
,adox
。mul
和mulx
。对于AVX2,SIMD几乎与标量bignum-multiply竞争,如果你重新排列数据,以便在4个中的每一个中实现相同长度的4个不同(和独立)乘法的“垂直矢量化” SIMD车道。
AVX512会在假设垂直矢量化的情况下再次使用SIMD。
但是在大多数情况下,bignums的“水平矢量化”在很大程度上仍然是一个失败的原因,除非你有很多(相同大小)并且可以承担转置它们以使它们“垂直”的成本。
部分字算术的矢量化
使用部分字算法,额外的“钉子”位使您能够延迟进位传播。
因此,只要你没有溢出这个词,SIMD add / sub就可以直接完成。在许多实现中,部分字表示使用有符号整数来允许单词变为负数。
因为(通常)不需要执行进位,所以可以在垂直和水平矢量化的bignums上同等有效地完成部分单词的SIMD加/减。
在水平矢量化的bignums上进行仍然很便宜,因为你只需将指甲移到下一个车道上。除非你需要对两个几乎相同的数字进行比较,否则通常没有必要完全清除指甲位并获得独特的表示。
由于需要处理指甲位,因此乘法对于部分字算法更为复杂。但是与add / sub一样,它仍然可以在水平矢量化的bignums上有效地进行。
AVX512-IFMA(与Cannonlake处理器配合使用)将提供指令,提供52 x 52位乘法的全部104位(可能使用FPU硬件)。对于每个字使用52位的部分字表示,这将非常有效。
使用FFT的大型乘法
对于非常大的bignums,使用Fast-Fourier Transforms (FFTs)最有效地完成乘法。
FFT完全可以矢量化,因为它们可以在独立的double
上工作。这是可能的,因为从根本上说,FFT使用的表示是
部分单词表示。
总而言之,可以进行bignum算术的矢量化。但必须做出牺牲。
如果您希望SSE / AVX能够在不对表示和/或数据布局进行根本性更改的情况下加速某些现有的bignum代码,则不太可能发生这种情况。
但是,bignum算术可以进行矢量化。
披露:
我是y-cruncher的作者,它有很多大量的arihmetic。