views:

193

answers:

3

I'm looking for the fastest way to popcount on large buffer of 512 or more bytes. I can guarantee any required alignment, and the buffer size is always a power of 2. The buffer corresponds to block allocations, so typically the bits are either all set, none set, or mostly set favoring the "left" of the buffer, with occasional holes.

Some solutions I've considered are:

I'm interested in the fastest solution, it must work on 32bit x86 chipset belonging to core2 or more recent. SSE and SIMD are of great interest. I'll be testing on the following quad core CPU:

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:
A: 

I would suggest implementing one of the optimised 32 bit popcnt routines from Hacker's Delight, but do it for 4 x 32 bit integer elements in an SSE vector. You can then process 128 bits per iteration, which should give you around 4x throughput compared to an optimised 32 bit scalar routine.

Paul R
I don't know how it's implemented in the routines you're referring to, but certainly the SSE4.1 POPCNT instruction only works on general-purpose, and not on XMM registers.
PhiS
@PhiS: indeed - I'm not suggesting using the SSE 4.1 POPCNT - there are some efficient routines in Hacker's Delight for a 32 bit popcnt in C, which could be used as the basis for an SSE 4 x 32 bit popcnt implementation. You would then iterate through your block of data, 128 bits a a time, generating 4 x 32 bit partial population counts, which could then be summed at the end to get the total population count.
Paul R
+1  A: 

See a 32 bit version in the AMD Software Optimization guide, page 195 for one implementation. This gives you assembly code for an x86 directly.

See a variant at Stanford bit-twiddling hacks The Stanford version looks like the best one to me. It looks very easy to code as x86 asm.

Neither of these use branch instructions.

These can be generalized to 64 bit versions.

With the 32 or 64 bit versions, you might consider doing a SIMD version. SSE2 will do 4 double-words or two quadwords (either way 128 bits) at once. What you want to do is implement the popcount for 32 or 64 bits in each of the 2 or 4 registers available. You'll end up with 2 or 4 sets of popcounts in the XMM registers when you are done; final step is to store and add those popcounts together to get the final answer. Guessing, I'd expect you do so slightly better doing 4 parallel 32 bit popcounts rather than 2 parallel 64 bit popcounts, as the latter is likely to take 1 or 2 additional instructions in each iteration, and its easy to add 4, 32 bit values together the end.

Ira Baxter
Can you provide links for the AMD guide, and the SIMD instructions you'd use?
Matt Joiner
@Matt: Oops, I bungled the AMD Optimiziation guide link, fixed.
Ira Baxter
@Matt: Regarding the SIMD instructions: the coded examples consist mostly of LOAD, AND, SHIFT, ADD scalar machine instructions and no branches. The x86 SIMD instructions have easily-found equivalents; the only real issue is making sure that the operands are aligned on 128 or 256 bit boundaries depending on which SIMD set you are using. You said that wasn't a problem for you :-}
Ira Baxter
@Matt: You got me playing with this... the solutions all listed here compute the popcnt for a single word. Obviously, you can unroll a loop to do more than one word, and you can go SIMD. What might not be so obvious is that one can compute popcnt for several words at a time at something like half the instructions it takes to do one, amortized (I've sketched an existence proof in the margin of SO :-} but its too bulky to fit in this comment. That would work nicely with your 512-byte buffer to process. How badly do you want the fastest answer?
Ira Baxter
@Ira Baxter: I'm glad someone understands there's a difference. I'm keen to see an answers that can beat what I currently have, or appealingly trade performance for simplicity and required chipset features.
Matt Joiner
@Matt: You aren't going to get simplicity if you want high performance; you'll get a convoluted piece of code that takes advantage of lots of obscure facts. Want to exhibit what you have so far?
Ira Baxter