8 个不稳定版本 (3 个破坏性更新)
0.4.0 | 2019年4月26日 |
---|---|
0.3.1 | 2019年4月23日 |
0.2.2 | 2019年4月19日 |
0.1.1 | 2019年4月14日 |
#1145 in 硬件支持
用于 hwt
62KB
842 行
swar
寄存器内SIMD操作支持库
更多信息请见 https://en.wikipedia.org/wiki/SWAR。
请查看文档以查看实际的代码示例。
请注意,在const generics加入nightly之后,此库将进行全面重构。该库的后续版本将依赖于nightly,直到const generics稳定。
此库尝试收集寄存器内SIMD (SWAR) 操作并以类型安全的方式呈现,以便程序员不会对数字的布局出错。SWAR操作利用了某些操作可以在单个寄存器中并行计算的事实,而无需对主机处理器进行任何SIMD扩展。例如,SWAR的一个常见用途是在没有专用 popcnt
指令的目标上使用rust的 count_ones
指令。
可以屏蔽数字中的每个偶数位
X = ABCD & 1010 = A0C0
Y = ABCD & 0101 = 0B0D
然后我们可以将数字右移1位并求和位
S1 = (X>> 1) +Y= EEFF
上面标记的 EE
和 FF
现在是相应2位的汉明重量(hamming weights),即 00
,01
或 10
。
现在我们可以将它们加在一起了
S2 = ((S1 & 1100) >> 2) + (S1 & 0011) =0GGG
现在,三个位 GGG
的范围可以在 0..=4
之间变化,因为位的和(汉明重量)最大可以是四。
这种模式可以继续,并且你可以计算 count_ones
/popcnt
,以在 N
位中计算汉明重量,所需时间为 O(log2(N))
。
计算两个二进制特征向量之间的汉明距离也非常重要。我们可以通过将每个位分配给一个特征来在内存中以紧凑的方式表示一个二进制特征向量。然后,为了计算汉明距离,只需要使用 XOR
操作。由于 XOR
设置了原始输入寄存器中不同的每个位,输出寄存器中有它们之间不同的位被设置。现在,这个输出可以计算其汉明重量,这就可以计算汉明距离了!
由于现代系统流水线化,这实际上可以几乎与实际工作负载中的本地 popcnt
指令一样快,但对此有巨大的免责声明,因为本地指令在简单基准测试中肯定比现代处理器上的并行位计数解决方案要快(见这里)。此外,程序大小差异也会影响缓存压力,所以不要忘记进行基准测试!显然,如果可用,请使用 popcnt
!如果不可用,还有其他好的备选方案。还可以考虑使用参考文献中使用的并行位计数的一个SIMD变体,但使用SIMD来推算更多的位数。
你可能希望使用并行位计数的这个版本的主要原因是,如果你实际上需要中间位计数!这实际上是 hwt
crate 实现中非常重要的,这启发我制作了这个库。
我们还可以并行执行其他操作。例如,我们可以在寄存器中添加一个,通过添加单个填充零来存储进位位(添加两个 n
位数字生成一个 n+1
位数字)
OUT =0A0B+0C0D
OUT & 0011 =B+D
OUT & 1100 = (A+C) << 2
我们还可以通过填充 1
借位来在寄存器中减去
OUT =1A1B-1C1D
OUT & 0011 =B-D+ 10
OUT & 1100 = (A-C+ 10) << 2
我们甚至可以在寄存器内并行执行乘法
OUT =0AB000CD*0EF000GH
OUT & 0000_0000_0000_1111 = CD * GH
OUT & 0000_0011_1110_0000 = (AB * GH + CD * EF) << 5
OUT & 0011_1100_0000_0000 = (AB * EF) << 10
注意,实际上从这个乘法中得到了三个输出!由于我们无法很好地打包数字,乘法并不比其他方法更高效,但在某些情况下是可能的。
我不知道是否可以使用我所知道的方法实现SWAR除法,但如果你有其他SWAR除法算法,请随时打开一个问题或PR!
致谢
Sean Eron Anderson的斯坦福位翻转技巧页面是本代码中一些位翻转算法的来源和灵感。
依赖项
~1MB
~24K SLoC