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認識])で試してもらったらこうなったらしいです。 これまた、不思議な結果だなあ。
無作為研究所トップページに戻る