読者です 読者をやめる 読者になる 読者になる

奇特なブログ

「殊勝に値する行いや心掛け」を意味する、奇特な人になる為のブログです

パフォーマンスが上がるfor文の書き方

以下の様な2つの書き方、皆さんしていませんか?

1.for(i = 0; i < strlen(文字列変数); i++)
2.for(i = 0; i < count(配列変数); i++)
strlen(マニュアル)は文字列のバイト数(文字数ではない)をint型で返す関数で、

count(マニュアル)は配列の要素数などをint型で返す関数です。

詳細はマニュアルを参照して下さい。

で、実は上記の書き方。

ループを終了するかどうか判断する際に、「必ず」関数が実行されます。

なので、その分多くの処理が実行され、
結果としてコストがかかるわけです。

関数内でバイト数や要素数を求めている以上。

よって、上記の文字列変数や配列変数の中身を、

ループ内で編集したり「しない」ならば、

必ず実行するのは無意味というか無駄なわけです。

バイト数や要素数がループ内で増減するのであれば、

上記の書き方で良いのですが。

そこで、strlenなどを「for文の1行上に書く」という方法を提案します。

以下は、コストをマイクロミリ秒単位で計測したサンプルプログラムです。

環境はOSはFedora12で、PHPのバージョンは5.3.0です。

1.for文の中にstrlen関数を書いた場合

--------------------------------------------------------------------------------



<?php

function microtime_float() {
list($usec, $sec) = explode(' ', microtime());
return ((float)$usec + (float)$sec);
}

$time_start = microtime_float();

for($i = 0; $i < strlen('11'); $i++) {
}

$time_end = microtime_float();
$time = $time_end - $time_start;

echo $time . ' seconds' . "\n";

?>

結果例:

[root@www ~]# for i in 1 2 3 4 5 6 7 8 9 10; do php for_test_in.php; done

6.6995620727539E-5 seconds

6.6041946411133E-5 seconds

6.6041946411133E-5 seconds

6.4849853515625E-5 seconds

7.3909759521484E-5 seconds

6.4849853515625E-5 seconds

6.4849853515625E-5 seconds

7.5101852416992E-5 seconds

6.6041946411133E-5 seconds

7.1048736572266E-5 seconds

[root@www ~]#

--------------------------------------------------------------------------------

2.for文の外にstrlen関数を書いた場合

--------------------------------------------------------------------------------



<?php

function microtime_float() {
list($usec, $sec) = explode(' ', microtime());
return ((float)$usec + (float)$sec);
}

$time_start = microtime_float();

$length = strlen('11');

for($i = 0; $i < $length; $i++) {
}

$time_end = microtime_float();
$time = $time_end - $time_start;

echo $time . ' seconds' . "\n";

?>

結果例:

[root@www ~]# for i in 1 2 3 4 5 6 7 8 9 10; do php for_test_out.php; done

6.6041946411133E-5 seconds

6.6041946411133E-5 seconds

0.00010585784912109 seconds

0.0001070499420166 seconds

6.5088272094727E-5 seconds

6.5088272094727E-5 seconds

7.3909759521484E-5 seconds

7.6055526733398E-5 seconds

6.6041946411133E-5 seconds

8.2969665527344E-5 seconds

[root@www ~]#

--------------------------------------------------------------------------------

3.for文の中にcount関数を書いた場合

--------------------------------------------------------------------------------



<?php

function microtime_float() {
list($usec, $sec) = explode(' ', microtime());
return ((float)$usec + (float)$sec);
}

$array = array();

for($j = 0; $j < 2; $j++) {
$k = strval($j);
$array[$k] = $k;
}

$time_start = microtime_float();

for($i = 0; $i < count($array); $i++) {
}

$time_end = microtime_float();
$time = $time_end - $time_start;

echo $time . ' seconds' . "\n";

?>

結果例:

[root@www ~]# for i in 1 2 3 4 5 6 7 8 9 10; do php for_test_array_in.php; done

4.4107437133789E-5 seconds

4.4822692871094E-5 seconds

5.5074691772461E-5 seconds

7.1048736572266E-5 seconds

4.3869018554688E-5 seconds

5.9127807617188E-5 seconds

4.3869018554688E-5 seconds

4.4107437133789E-5 seconds

5.1975250244141E-5 seconds

4.3869018554688E-5 seconds

[root@www ~]#

--------------------------------------------------------------------------------

4.for文の外にcount関数を書いた場合

--------------------------------------------------------------------------------



<?php

function microtime_float() {
list($usec, $sec) = explode(' ', microtime());
return ((float)$usec + (float)$sec);
}

$array = array();

for($j = 0; $j < 2; $j++) {
$k = strval($j);
$array[$k] = $k;
}

$time_start = microtime_float();

$length = count($array);

for($i = 0; $i < $length; $i++) {
}

$time_end = microtime_float();
$time = $time_end - $time_start;

echo $time . ' seconds' . "\n";

?>

結果例:

[root@www ~]# for i in 1 2 3 4 5 6 7 8 9 10; do php for_test_array_out.php; done

4.1961669921875E-5 seconds

4.3869018554688E-5 seconds

4.5061111450195E-5 seconds

5.2213668823242E-5 seconds

6.8187713623047E-5 seconds

7.2002410888672E-5 seconds

4.2200088500977E-5 seconds

4.1961669921875E-5 seconds

4.1961669921875E-5 seconds

4.1961669921875E-5 seconds

[root@www ~]#

--------------------------------------------------------------------------------

で、上記4つのプログラムをそれぞれ100回実行した時の、

平均速度が以下です。


プログラムのパターン 100回実行時の平均速度
1.for文の中にstrlen関数を書いた場合 0.0000736秒
2.for文の外にstrlen関数を書いた場合 0.0000746369秒
3.for文の中にcount関数を書いた場合 0.0000503135秒
4.for文の外にcount関数を書いた場合 0.0000476432秒


微妙な結果です。

では、上記では「2」だったバイト数と要素数を、

それぞれ「100」に変更して実行した結果が以下です。


プログラムのパターン 100回実行時の平均速度
1.for文の中にstrlen関数を書いた場合 0.000111005秒
2.for文の外にstrlen関数を書いた場合 0.0000818014秒
3.for文の中にcount関数を書いた場合 0.00007671秒
4.for文の外にcount関数を書いた場合 0.000055秒


予想通り、for文の外に書いた方がコストがかからないという結果が出ました。

結果はほんの僅かの差ですが、

システムやアプリの性能改善で、

「色々チューニングしてみたけど、要望まであとちょっと足りない!」などの時に、

やってみるのは如何でしょうか。

特にcountの方は要素数100以上などはザラにあるでしょうし、

for文自体、頻繁に使うものですし。

塵も積もれば山となるとも言いますしね。

とはいえ、デグレが恐いのも確かです。

なので、プログラムを書く際に「習慣的」にやっておけば、

「少なくても、for文の部分に関してはチューニングの余地無し」となり、

精神的な心配からは開放されるのではないかと思います(笑)

他言語は現時点では不明です。

JavaScriptだと、文字列or配列のlengthプロパティなど。

Javaだと、Stringクラスのlengthメソッド、配列のlength属性、

Collectionインターフェースのsizeメソッドなどが対象でしょうか。

取り上げるかどうかは・・・不明ですが。