PHP
HHVM

array_reduceのバグがhhvmにあるか調べる

報告したけど絶賛放置中wのarray_reduceの性能問題( https://bugs.php.net/bug.php?id=75533 ) 1 ですが、hhvmは実装が違うだろうから多分ないと思うので確認してみた。

型とかつけて以下のように書き換えて実行。

test_array_reduce.php:

<?hh

function test_array_reduce(int $n): void
{
    $d = range(1, $n);

    $t0 = microtime(true);
    $r = array_reduce(
            $d,
            function (&$carry, $item) {
                $carry []= $item;
                return $carry;
            },
            []
        );
    printf("%fms\n", (microtime(true) - $t0) * 1000);
}

test_array_reduce((int)$argv[1]);

test_my_reduce.php:

<?hh

function foo_reduce<T1,T2>(array<T1> $array, (function(T2, T1): T2) $func, T2 $init): T2
{
    $carry = $init;
    foreach ($array as $item) {
        $carry = $func($carry, $item);
    }
    return $carry;
}

function test_my_reduce(int $n): void
{
    $d = range(1, $n);

    $t0 = microtime(true);
    $r = foo_reduce(
            $d,
            function (&$carry, $item) {
                $carry []= $item;
                return $carry;
            },
            []
    );
    printf("%fms\n", (microtime(true) - $t0) * 1000);
}

test_my_reduce((int)$argv[1]);

結果はこんな感じで問題なし。

% hhvm test_array_reduce.php 100000
17.694950ms
% hhvm test_my_reduce.php 100000
15.845776ms
% hhvm test_array_reduce.php 200000
24.797916ms
% hhvm test_my_reduce.php 200000
23.839951ms

組み込みのarray_reduceより自分で書いた関数の方が速いのはご愛敬か?

さらにいうとfoo_reduceの中で$carryの値は$funcの呼び出し後は必ず破壊されるので保持する必要がないからさらにリファレンスカウントを1つ減らすことが可能で、そのような最適化を行えば$carryを参照渡しにしなくてもコピーが行われなくなり便利そうですが、さすがにそこまではやってないようでした。


  1. 組み込みのarray_reduceが$carryが大きな配列でcallback関数の中で書き換えを行うときに、$carryを参照渡しにしていても自分でforeach使って実装したのより遅くなることがあるという性能バグです。(array_reduceの内部で$carryへのリファレンスを1つ余分に持っているので参照で受けても余計な配列コピーが行われる模様)