array_mapの性能

array_map 使っていますか。私はあまり使いません。RubyHaskell だと map はよく使うのですが、PHP では foreach ばかり使っていて、array_map はあまり使いません。

使わない理由としては、いちいち関数を定義するのが面倒、と言うのがまず一つ。Haskell の場合は for 文などはないので map を使わざるを得ないし、関数定義が面倒ならそもそも Haskell 自体が面倒のはず。Ruby であればブロックを記述できるので、面倒ではない。 なら PHP の場合 foreach の中を Ruby でいう所のブロックと思い込めばいい、というのはなんか違う。なにが違うかと言えば、処理を渡しているか否か、というところ。Haskell であれば関数、Ruby であれば Proc。でも foreach はそうじゃない。

もう一つの理由としては、性能面で劣るのではないかと思っていること。関数の呼び出しなのでループよりコストが高そう。

今まで foreach を使った場合と array_map() を使った場合の性能を計ったことはない。であれば試してみようと思った次第。

1. 無名関数を使わない方法

5.3 にならないと無名関数が使えないので、こういう書き方になるはず。array_map に渡しているのは関数ではなく関数名を示す文字列。

<?php
$xs = array_pad(array(), 1000000, 1);

function plus100($x) { return $x + 100; }

$xs = array_map('plus100', $xs);

2. 無名関数を使う方法

5.3 以降は無名関数が使える。今のところ仕事のコードでは無名関数は使ったことがない。

<?php
$xs = array_pad(array(), 1000000, 1);

$xs = array_map(function($x) { return $x + 100; }, $xs);

{:lang="php"}

3. foreach

PHP と言えば配列。配列と言えば foreach 。単純な処理だと foreach の方がわかりやすく書けるよね。

<?php
$xs = array_pad(array(), 1000000, 1);

foreach ($xs as &$x) {
  $x += 100;
}

結果

上記 1 〜 3 のコードを実行して、実行時間を計測すると、

  1. 1.64 sec
  2. 0.98 sec
  3. 0.15 sec

という結果。やはり array_map は遅かった。

まあ、実際こんなでかい配列に対して処理を行なうことは、そうそうないと思う。性能の差は気にしない方がいいのかも。

array_map だと配列の各要素に対して、この関数を適用してその結果で置き換える、というのが明示的になるからコードとしては読みやすくなるのかも。ただ、無名関数を使う場合は名前から処理を想像できないのでちょっと微妙?