关于C#:大型缓冲区的位popcount,带有Core 2 CPU(SSSE3)

Bit popcount for large buffer, with Core 2 CPU (SSSE3)

我正在寻找在512字节或更多字节的大缓冲区上进行popcount的最快方法。我可以保证任何所需的对齐,并且缓冲区大小总是2的幂。缓冲区与块分配相对应,因此通常情况下,位要么全部设置,要么全部不设置,要么大部分设置为偏向缓冲区的"左",偶尔会有孔。

我考虑过的一些解决方案是:

  • GCC的__builtin_popcount
  • 位片popcount_24words
  • 数位集,布莱恩·克尼根的方法

我对最快的解决方案感兴趣,它必须在32位x86芯片组上工作,属于core2或更高版本。SSE和SIMD非常感兴趣。我将在以下四核CPU上进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
matt@stanley:~/anacrolix/public/stackoverflow$ cat /proc/cpuinfo
processor       : 0
vendor_id       : GenuineIntel
cpu family      : 6
model           : 15
model name      : Intel(R) Core(TM)2 Quad CPU    Q6600  @ 2.40GHz
stepping        : 11
cpu MHz         : 1600.000
cache size      : 4096 KB
physical id     : 0
siblings        : 4
core id         : 0
cpu cores       : 4
apicid          : 0
initial apicid  : 0
fdiv_bug        : no
hlt_bug         : no
f00f_bug        : no
coma_bug        : no
fpu             : yes
fpu_exception   : yes
cpuid level     : 10
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm lahf_lm tpr_shadow vnmi flexpriority
bogomips        : 4800.21
clflush size    : 64
cache_alignment : 64
address sizes   : 36 bits physical, 48 bits virtual
power management:


参见AMD软件优化指南第195页中的32位版本以了解一个实现。这将直接为x86提供程序集代码。

看看斯坦福比特旋转木马的变种。斯坦福大学的版本对我来说是最好的。它看起来很容易编码为x86 ASM。

这些都不使用分支指令。

这些可以概括为64位版本。

对于32位或64位版本,您可以考虑执行SIMD版本。SSE2将执行4个双字或两个四字(双向128位)马上。您要做的是实现32的popcount或2个或4个可用寄存器中的64位。最后在xmm寄存器中有2到4组popcount完成后,最后一步是存储和添加PopCounts一起得到最终答案。猜测,我希望你做得更好,4平行32位PopCounts而不是2个并行的64位PopCounts,因为后者可能会接受1或2个附加指令在每次迭代中,它很容易将4、32位的值加在一起结束。


我概述了我为下面的大型缓冲区的人口计数/汉明重量找到的最佳C/assembly函数。

最快的装配功能是ssse3_popcount3,这里描述。它需要SSSE3,可在英特尔酷睿2及更高版本上使用,AMD芯片组将于2011年推出。它使用SIMD指令以16字节块的形式进行popcount,并一次展开4个循环迭代。

最快的C函数是popcount_24words,在这里描述。它使用位切片算法。值得注意的是,我发现clang实际上可以生成适当的向量装配指令,从而显著提高了性能。除此之外,算法仍然非常快速。


如果你有popcnt:

http://kent-vandervelden.blogspot.com/2009/10/counting-bits-population-count-and.html

http://software.intel.com/sites/products/documentation/studio/composer/en-us/2011/compiler_c/intref_cls/common/intref_sse42_ata.htm


我建议从Hacker'sDelight中实现一个优化的32位popcnt例程,但是在SSE向量中为4 x 32位整数元素实现它。然后,您可以在每次迭代中处理128位,与优化的32位标量例程相比,这将给您大约4倍的吞吐量。