PopCntの速度再び2011

3年ほど前、popcntの速度を調べたことがありますが
http://d.hatena.ne.jp/mkomiya/20070905/p1


bitを数える
http://vivio.blog.shinobi.jp/Entry/137/


ふと思い出したので、またちょっと調べてみました。


以前は、ビット数少ないとアセンブラ速いけど
表引きC言語は安定してるよね
的な幕引きでしたが、
アセンブラでマジックで計算するコードをサイボウズラボの人が書いていたので、
http://developer.cybozu.co.jp/takesako/2006/11/binary_hacks.html
それを加えてみます。
あと、団子の人のSSEコードもあったのでそれも追加。


LS3600さんが、SSE4.2でPopCntの使い方も紹介されてましたが、
今使ってるマシン(core2duo E7500)がSSE4.1なので、SSE4.2はビルドはできるけど
実行するとエラーになりました(残念)
http://d.hatena.ne.jp/LS3600/20091218/p1
っていうか、phenomII x6もSSE4.1らしいよ。えー。


出場選手
アセンブラでループ
C言語でマジック
アセンブラでマジック
・SSE命令でマジック

0x00000001 PopCnt=1 asm(loop)=0.30 asm(magic)=0.55 c++=1.25 SSE=0.45
0x00000103 PopCnt=3 asm(loop)=0.41 asm(magic)=0.53 c++=1.27 SSE=0.44
0x0000050b PopCnt=5 asm(loop)=0.47 asm(magic)=0.53 c++=1.27 SSE=0.44
0x0000152b PopCnt=7 asm(loop)=0.67 asm(magic)=0.53 c++=1.25 SSE=0.45
0x000055ab PopCnt=9 asm(loop)=0.78 asm(magic)=0.55 c++=1.25 SSE=0.44
0x000157ab PopCnt=11 asm(loop)=0.84 asm(magic)=0.53 c++=1.27 SSE=0.44
0x00055fab PopCnt=13 asm(loop)=0.97 asm(magic)=0.53 c++=1.27 SSE=0.44
0x00157fab PopCnt=15 asm(loop)=1.08 asm(magic)=0.55 c++=1.25 SSE=0.45
0x0055ffab PopCnt=17 asm(loop)=1.20 asm(magic)=0.53 c++=1.26 SSE=0.44
0x0157ffab PopCnt=19 asm(loop)=1.33 asm(magic)=0.55 c++=1.25 SSE=0.45
0x055fffab PopCnt=21 asm(loop)=1.45 asm(magic)=0.53 c++=1.25 SSE=0.45

※それぞれ9000万回実行


マジックのアセンブラ激速! SSEにしても大差なし?
SSE版はメモリアクセスしてところが遅くなってそう?


マジックなアセンブラのソースと、SSEでマジックなソース

unsigned int PopCnt32c(unsigned int v) 
{ 
  unsigned int retVal; 
  __asm { 
  MOV EAX, [v]         ;v 
  MOV EDX, EAX         ;v 
  SHR EAX, 1           ;v >> 1 
  AND EAX, 055555555h  ;(v >> 1) & 0x55555555 
  SUB EDX, EAX         ;w = v - ((v >> 1) & 0x55555555) 
  MOV EAX, EDX         ;w 
  SHR EDX, 2           ;w >> 2 
  AND EAX, 033333333h  ;w & 0x33333333 
  AND EDX, 033333333h  ;(w >> 2) & 0x33333333 
  ADD EAX, EDX         ;x = (w & 0x33333333) + ((w >> 2) & 0x33333333) 
  MOV EDX, EAX         ;x 
  SHR EAX, 4           ;x >> 4 
  ADD EAX, EDX         ;x + (x >> 4) 
  AND EAX, 00F0F0F0Fh  ;y = (x + (x >> 4) & 0x0F0F0F0F) 
  IMUL EAX, 001010101h ;y * 0x01010101 
  SHR EAX, 24          ;population count = (y * 0x01010101) >> 24 
  MOV retVal, EAX      ;store result 
  } 
  return (retVal); 
} 

//・∀・)っ-○◎● ◆DanGorION6
unsigned int  PopCnt32_SSE(unsigned int  src)
{ 
	unsigned int dest;
	static const __m128i table = { 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4 }; 
	__asm { 
		movd xmm1, dword ptr [src] 
		movdqa xmm0, xmm1 
		psrlq xmm1, 4 
		punpckldq xmm0, xmm1 
		pshufb xmm0, xmmword ptr [table] 
		pxor xmm1, xmm1 
		psadbw xmm0, xmm1 
		movd dword ptr [dest], xmm0 
	} 
	return dest; 
} 


SSE4.2なCPUだと、
_mm_popcnt_u32(a)
で組み込みPopCntが可能

#include "intrin.h"

VC++コンパイルできます。