atoi()関数の高速化

 エラー処理がないとかイロイロ嫌われているatoi()関数ではありますが、いまだによく使います。 また、呼び出し回数が多いこともあり、昔から高速化を行っていました。 まあ、最近のCPUでは、何千万回とかやらないと差がわかりにくくなっていますが、少しでも高速なことは良いことです。


プログラムテスト仕様

 atoi()関数に限らず、高速チューンを施す場合、まずテスト用のプログラムを作成します。 今回作ったのは、windows用で、以下のようなモノです。

//
//	atoi() を極める
//	copyright(c)2011 by 無作為研究所 http://www.faicha.com/
//

#include "windows.h"
#include "stdlib.h"

char*	srcbuf;
int*	dstbuf;
int*	ansbuf;

#define N	1000000
#define M	999999

//
//	データエリアの初期化
//
void init_data(void )
	{
	int i;

	srcbuf = malloc(N * 8);		// 8バイトづつ文字列を入れる
	dstbuf = malloc(N * sizeof(int));	// 整数型
	ansbuf = malloc(N * sizeof(int));	// 整数型

	for (i = 0;i < N;i++)
		{
		// 0からM−1の数文字列をいれておく
		sprintf(srcbuf+i*8,"%d",((rand()*rand()+rand())% M)-M/2);
		// 正解を求めておく
		ansbuf[i] = atoi(srcbuf+i*8);
		}
	return ;
	}


//
//	解答エリアのクリア
//
void clear_data(void )
	{
	int i;

	for (i = 0;i < N;i++)
		{
		dstbuf[i] = 0;
		}
	return ;
	}


//
//	答え合わせ
//
void check_data(void )
	{
	int i;

	for (i = 0;i < N;i++)
		{
		if (dstbuf[i] != ansbuf[i])
			{
			printf("Wrong answer: string=%s answer=%s bad answer=%d (position=%d)\n",
				srcbuf+i*8,ansbuf[i],dstbuf[i],i);
			exit(1);
			}
		}
	return ;
	}

extern int atoi_01(char* p);

//
//	atoiによる処理
//
void atoi_00_loop(void)
	{
	int i;

	for (i = 0;i < N;i++)
		{
		dstbuf[i] = atoi(srcbuf+i*8);
		}
	return ;
	}
void atoi_01_loop(void)
	{
	int i;

	for (i = 0;i < N;i++)
		{
		dstbuf[i] = atoi_01(srcbuf+i*8);
		}
	return ;
	}


//
//	計測ルーチン
//
int check_speed(void(*callback)(void))
	{
	DWORD t1,t2;

	while(1)
		{
		clear_data();
		t1 = GetTickCount();
		callback();
		t2 = GetTickCount();
		check_data();
		if (t1 <= t2) break;
		}
	return t2 - t1;
	}


//
//	メインエントリー
//
int main(int argc,char ** argv)
	{
	int i,j,spd1,spd2;
	init_data();

	spd1 = 0;
	for (i = 0;i < 10;i++)
		{
		spd1 += check_speed(atoi_00_loop);
		}
	spd1 /= 10;
	printf("atoi_00(標準ライブラリ)での処理時間:%d[mS]\n",spd1);

	spd2 = 0;
	for (i = 0;i < 10;i++)
		{
		spd2 += check_speed(atoi_01_loop);
		}
	spd2 /= 10;
	printf("atoi_01(高速ライブラリ)での処理時間:%d[mS]\n",spd2);

	printf("標準ライブラリからの高速化 %G%%\n",100.0*((1.0/(double)spd2) / (1.0/(double)spd1)));

	return 0;
	}

check_speedという関数に、時間計測したい関数のポインタを渡して、これを10回呼び出し、平均値を計測しています。 計測対称は、atoi_00_loopと、atoi_01_loopという2つの関数で、それぞれ100万回、atoi関数と、atoi互換自作関数を呼び出します。atoi_01_loop関数内で呼び出されるatoi_01というのが、今回の研究対象である、atoiと互換性があって、速度をチューニングした関数です。

atoi_00(標準ライブラリ)での処理時間:101[mS]
atoi_01(高速ライブラリ)での処理時間:49[mS]
標準ライブラリからの高速化 206.122%

 上のプログラムをコンパイルして実行した結果がこちらです。
数回実行してみると、数mSは動作時間がバラつきますが、概ね200%程度の高速化を達成しているようです。 コンパイルはVisualC++で、オプティマイズオプションは /Ox(最大)で行っています。 プログラムソースとEXEをここに置きました。 ただ、高速atoi関数はobjのみ収録しておきましたので、どなたか、これより高速なヤツを作ってみてください。 また、当方の実行環境はWindowsVista(涙) Intel Centrino,4G Memですんで、環境によって、結果が違うなどの情報も頂けるとありがたいです。
 しかし、ソースださなくっても、atoi1.obj解析したら、すぐに手口わかるなあ、こりゃ。

atoi_00(標準ライブラリ)での処理時間:54[mS]
atoi_01(高速ライブラリ)での処理時間:24[mS]
標準ライブラリからの高速化 225%

知り合いのマシン(XP SP3 / Core 2 Quad @3.0GHz / MEM 4G)で試してもらったらこうなったらしいです。 みんないいマシン使っているんですね。

atoi_00(標準ライブラリ)での処理時間:70[mS]
atoi_01(高速ライブラリ)での処理時間:23[mS]
標準ライブラリからの高速化 304.348%

さらに、知り合いのマシン(Win7-32bit / Athlon IIx4 630 / MEM 4G[3G認識])で試してもらったらこうなったらしいです。 これまた、不思議な結果だなあ。


無作為研究所トップページに戻る

copyright(c)2007 by MUSAKUI-LABO