アセンブラ vs C言語のテーブル引き 速度対決!

かずさんの提案で、
ビットの立ってる1を数える関数をアセンブラで書いた場合と、
8ビットでテーブルもっといて一発で引いた場合(ソースはうさ親さん提供のれさぴょんから)を比較してみました。


ソースは最後にあります。


アリーナに鍛え抜かれた強者たちが入ってきました。
筋肉と筋肉が雌雄を決するアスリート同士の頂上がちんこ対決。
筋肉番付、今スタートです!


さて注目の結果は?

C:\misaki>bench
0xaaaaaaaa PopCnt32 =1440000016  2.7s
0xaaaaaaaa PopCnt32b=1440000016  1.4s
0xa00aa00a PopCnt32 =720000008  1.3s
0xa00aa00a PopCnt32b=720000008  1.4s
0xa000a000 PopCnt32 =360000004  0.8s
0xa000a000 PopCnt32b=360000004  1.4s
0xffffffff PopCnt32 =-1414967264  4.6s
0xffffffff PopCnt32b=-1414967264  1.3s

ビットが全部立ってると断然テーブル引き(アセンブラ超遅い!)
ビットが16(半分)立っててもテーブル引きの勝ち
ビットが8個以下になってくるとアセンブラが速い
さすがにテーブル引きは、時間が一定なので安定してますね(^^;


実際、利きテーブルどのくらいビットが立ってるか?を考えると
上位16ビットは飛び利きなので、盤面の大部分で立ってませんので、
総合で考えるとアセンブラも盛り返すと思います。
ひとマスに利きが8個も乗ることなんかないですからね。
多くても5個ぐらいじゃないですか? 大部分は3個以下でしょう。
(先手と後手で利きテーブル別々ですから)


実際の所は連続自己対戦で決着ですか?
どうなんだろ? アセンブラ不利かも( ´Д`)
ビットボードでも使ってるんで、全面テーブル引きに切り替えるか?( ´Д`)


この勝負微妙。


審判委員の高砂部屋親方が土俵に入ってきました。
なにやら協議中のようです
勝負は、千秋楽の優勝決定戦に持ち越されました。

#include 
#include  
#include 
#include  
#include 
#include 
#include 

__forceinline int PopCnt32(unsigned int a)
{
  __asm {
                   mov ecx, dword ptr a
                   xor eax, eax
                   test ecx, ecx
                   jz l1
         l0:       lea edx, [ecx - 1]
                   inc eax
                   and ecx, edx
                   jnz l0
         l1:
         }
}


const int Tbl[256]={
	0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
	1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
	1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
	1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
	3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
	1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
	3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
	2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
	3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
	3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
	4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
};

// xは、何ビット1が立っているか
__forceinline int PopCnt32b(unsigned int x)
{
	return Tbl[((unsigned char*)&x)[0]]+Tbl[((unsigned char*)&x)[1]]+Tbl[((unsigned char*)&x)[2]]+Tbl[((unsigned char*)&x)[3]];
}

clock_t t_end;
clock_t t_start;

void main()
{
	long count=90000000;

	t_start = clock();
	int sum=0;
	for(long i=0;i<=count;i++)
	{
		sum+=PopCnt32(0xaaaaaaaa);
	}
	t_end = clock();
	float thinktime=(float)(t_end - t_start) / CLOCKS_PER_SEC;
	printf("0xaaaaaaaa PopCnt32 =%d %4.1fs \n",sum,thinktime );

	t_start = clock();
	sum=0;
	for(long i=0;i<=count;i++)
	{
		sum+=PopCnt32b(0xaaaaaaaa);
	}
	t_end = clock();
	thinktime=(float)(t_end - t_start) / CLOCKS_PER_SEC;
	printf("0xaaaaaaaa PopCnt32b=%d %4.1fs \n",sum,thinktime );

	t_start = clock();
	sum=0;
	for(long i=0;i<=count;i++)
	{
		sum+=PopCnt32(0xa000a000);
	}
	t_end = clock();
	thinktime=(float)(t_end - t_start) / CLOCKS_PER_SEC;
	printf("0xa000a000 PopCnt32 =%d %4.1fs \n",sum,thinktime );

	t_start = clock();
	sum=0;
	for(long i=0;i<=count;i++)
	{
		sum+=PopCnt32b(0xa000a000);
	}
	t_end = clock();
	thinktime=(float)(t_end - t_start) / CLOCKS_PER_SEC;
	printf("0xa000a000 PopCnt32b=%d %4.1fs \n",sum,thinktime );


	t_start = clock();
	sum=0;
	for(long i=0;i<=count;i++)
	{
		sum+=PopCnt32(0xffffffff);
	}
	t_end = clock();
	thinktime=(float)(t_end - t_start) / CLOCKS_PER_SEC;
	printf("0xffffffff PopCnt32 =%d %4.1fs \n",sum,thinktime );

	t_start = clock();
	sum=0;
	for(long i=0;i<=count;i++)
	{
		sum+=PopCnt32b(0xffffffff);
	}
	t_end = clock();
	thinktime=(float)(t_end - t_start) / CLOCKS_PER_SEC;
	printf("0xffffffff PopCnt32b=%d %4.1fs \n",sum,thinktime );
}