高速なatoiとatof作ってみた。

必ず数字が引数に渡されるという限定した状況に限り使用可能な、atoiとatof(数字文字列を数値に変換するC言語関数)を作ってみました。
作ってみたっていうか、こちらの丸パクリして、ほんのちょっとさらに高速にしてみただけですけど。

fast_atof32は小数点以下9桁までしか扱えませんが、整数演算なのでちょっと早いはず。
桁数が少ない場合に使えるかなと。あと浮動小数演算が苦手なCPU向け。

//スペースを取って、符号を返す
int _space_sign(const char *s, const char **endptr)
{
	while (*s == 0x20)
		++s;
	int sign = 0;
	switch (*s)
	{
		case '-':
			sign = -1;
			// fall through
		case '+':
			++s;
			break;
	}
	*endptr = s;
	return sign;
}

//高速版atoi
int fast_atoi(const char *s)
{
	int sign = _space_sign(s, &s);
	int result = 0;
	while(true)
	{
		if(*s > '9' || *s < '0')break;
		result = result * 10 + *s - '0';
		s++;
	}

	if (sign != 0)
		result = -result;
	return result;
}
//高速版atof
float fast_atof(const char *s)
{
	int sign = _space_sign(s, &s);
	float result = 0;
	float cnm = 1;
	while(true)
	{
		if(*s == '.')
		{
			s++;
			break;
		}else if(*s > '9' || *s < '0')break;

		result = result * 10 + *s - '0';

		s++;
	}
	while(true)
	{
		if(*s > '9' || *s < '0')break;
		result = result * 10 + *s - '0';
		cnm*=0.1;
		s++;
	}

	if (sign != 0)
		result = -result;

	return result * cnm;
}

//高速版atof(桁が少ない)
float fast_atof32(const char *s)
{
	int sign = _space_sign(s, &s);
	int result = 0;
	int cnm = 1;
	while(true)
	{
		if(*s == '.')
		{
			s++;
			break;
		}else if(*s > '9' || *s < '0')break;

		result = result * 10 + *s - '0';
		s++;
	}
	while(true)
	{
		if(*s > '9' || *s < '0')break;
		result = result * 10 + *s - '0';
		cnm*=10;
		s++;
	}

	if (sign != 0)
		result = -result;

	return (float)result / cnm;
}

パクリ元との違いはisdigitやisspaceを単純に手書きしただけなんだけど、関数呼び出しを止めるだけでほんのちょっとだけ早くなる。
空白の無い、正の数しかない数字文字列に限定すれば、_space_signも要らなくなるからさらにちょっと早くなる。

ベンチマークを取ってみた。
atoi用データは”123456″
atof用データは””0.01111111″
10,000 * 10,000のループで測定。
VC++2008でビルド。
CPUはCore2Duo E6600 2.4GHz

最適化 有無 標準atoi strtol fast_atoi 標準atof strtod fast_atof fast_atof32
なし 5,732 5,281 4,091 55,496 55,300 8,698 6,620
あり 5,521 5,074 1,237 55,746 57,190 5,287 3,192

数値の単位はミリ秒。

最適化ありのほうはCPUによって違ってくるだろうけど、それにしたって標準関数に比べて早すぎな気がするなぁ。
最適化によってループが省略されたとか?
特にfalst_atofは標準atofに比べて10倍。fast_atof32だと17倍早い。劇的過ぎる。
でもfast_atof32とfast_atofの差は微妙だなぁ。1.6倍か。・・・fast_atof32要らないかも。使いづらいし。

strtolは機能が多いのにatoiより速いんだね。atoi良いとこなし。涙目。
逆にstrtodは微妙にatofに負けてるけど、その差は小さい。つか最適化掛けた方が遅いってどゆこと。

数値変換にsscanfを使う人がいるから、sscanfでも同じ条件で計ってみたんだけど、時間掛かりすぎてたから途中で止めた。
1,000 * 1,000のループにしたら1,000ms位だったから、単純に100,000ms掛かるわけだね。標準atofの2倍。
sscanfは使いやすいけど、一つ変換したいだけなら、素直にatoi/atof使った方がいいっぽいね。

移植性とか安全性とか考えてないので、使い場面は限られますね。
ゲームみたいに、異常データが来ないことが決まっていて、速度が求められるようなケースには結構有効なんじゃないかな。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

認証のために問題を解いて下さい * Time limit is exhausted. Please reload CAPTCHA.