表題の通りです。職場にて array_flip
を2回やると、 array_unique
より早いらしいという言説を見かけたため、腹落ちするまで調べてみました。なにしろ「らしい」という単語に過剰反応してしまうもので…。
マイクロベンチマーク
適当に重複した80万件の配列を作って、比較対象の関数で実行します。
1 |
|
関数名 | オプション | 処理時間(秒) | 最速を1とした時の速度比 |
---|---|---|---|
array_flip x2 | - | 0.0085070133209 | 1 |
array_unique | SORT_STRING | 0.0282361507416 | 3.32 |
array_unique | SORT_NUMERIC | 0.0873460769653 | 10.28 |
array_unique | SORT_REGULAR | 0.0463910102844 | 5.45 |
array_unique | SORT_LOCALE_STRING | 0.5002570152283 | 58.81 |
結果の考察
結果だけ見ると、「うぉぉ、 array_flip x2 でいいじゃん!」となりそうですが、世の中はそう美味い話は転がっていません。array_flip を array_unique の代用として使うには、条件があります。
- 対象が int配列
これが絶対条件です。array_flip すると numeric な文字列は型が吹き飛びます。array_unique は最初に登場した値を残していきますが、array_flip では単純にhashのキーを上書きしていくので後勝ちになります。
1 | $ php -a |
array_unique のオプションによる違い
SORT_STRING
というオプションは、PHP 7.2 から導入されたもので、 Hash をメモに使うことで、O(n)
オーダーでの処理を可能にしています。
SORT_REGULAR
, SORT_NUMERIC
, SORT_LOCALE_STRING
の3つについては、アルゴリズムとしては同じで、配列の値をソートして、ソート済みの配列に対して先勝ちでユニークな値を拾っていきます。オプションに違いによって、ソートするときのコンパレーターが変わります。計算量は O(n log n)
ですが、コンパレーターの差で処理速度が変わるようです。
FlameGraph
perf-tools を使ってPHPソースレベルの Flamegraph を作る
当ブログで紹介した方法で、各関数に対してFlameGraphを取ってみました。詳細は中身を見てほしいのですが、 array_unique SORT_STRING
が array_flip
と同じ計算料にもかかわらず処理速度が遅いのは、__toString
の呼び出しにコストがかかるためでした。同様に array_unique SORT_LOCALE_STRING
が飛び抜けて処理効率が悪いのも __toString
が原因のようです。
FlameGraph は、どれくらいのコストがかかっているのかが一目瞭然で、わかりやすいですね!!
[array_flip x2](/images/flame/flame_array_flip.svg) [array_unique SORT_STRING](/images/flame/flame_array_unique_string.svg) [array_unique SORT_REGULAR](/images/flame/flame_array_unique_regular.svg) [array_unique SORT_NUMERIC](/images/flame/flame_array_unique_numeric.svg) [array_unique SORT_LOCALE_STRING](/images/flame/flame_array_unique_locale.svg)