phpの参照渡しはメモリを食うって本当?

  • 72
    いいね
  • 6
    コメント
この記事は最終更新日から1年以上が経過しています。

はじめ

phpでむやみに参照渡しすると、メモリ食うという。
↓ここら辺で検証されているが、果たしてphp5.4でも状況は変わらないのか検証したい。
http://tanakahisateru.hatenablog.jp/entry/2013/12/12/012728
http://qiita.com/dkkoma/items/7fd216795ee2b90d90c6

検証環境

  • linux
  • PHP 5.4.17 (cli) (built: Aug 21 2013 18:22:59)

ソースコード

参考にしているソースそのままだが、下記。

<?php
function profile($funcname)
{
    $bigarray = range(1, 1000000);

    echo $funcname . "\n";

    if ($funcname) {
        $start = microtime(true);
        $ret = $funcname($bigarray, 500000);
        $end = microtime(true);
    } else {
        $start = microtime(true);
        $end = microtime(true);
    }

    echo "  caller memory: " . number_format(memory_get_usage()) . "\n";
    echo "  time: " . (($end - $start) * 1000) . "(ms)\n";
    unset($bigarray);
    unset($ret);
}

ob_start();
profile(null); // ウォームアップ
ob_end_clean();
// 参照なし
function nop_noref($arr)
{
    echo "  callee memory: " . number_format(memory_get_usage()) . "\n";
    return $arr;
}

// 参照渡しのみ
function nop_ref_arg(&$arr)
{
    echo "  callee memory: " . number_format(memory_get_usage()) . "\n";
    return $arr;
}

// 参照渡し+参照返し
function &nop_ref_both(&$arr)
{
    echo "  callee memory: " . number_format(memory_get_usage()) . "\n";
    return $arr;
}

// 参照渡しでreturnしない
function nop_ref_arg_noreturn(&$arr)
{
    echo "  callee memory: " . number_format(memory_get_usage()) . "\n";
}

profile('nop_noref');
profile('nop_ref_arg');
profile('nop_ref_both');
profile('nop_ref_arg_noreturn');

検証結果

% php -d memory_limit=-1 /tmp/reference.php

nop_noref
  callee memory: 144,624,128
  caller memory: 144,624,128
  time: 0.053167343139648(ms)
nop_ref_arg
  callee memory: 144,624,208
  caller memory: 241,012,984
  time: 277.83894538879(ms)
nop_ref_both
  callee memory: 144,624,160
  caller memory: 144,624,160
  time: 0.052928924560547(ms)
nop_ref_arg_noreturn
  callee memory: 144,624,256
  caller memory: 144,624,304
  time: 0.041007995605469(ms)

各関数のechoの後に
$arr = [];を追記してから実行した結果

nop_noref
  callee memory: 144,624,856
  caller memory: 241,013,776
  time: 99.42102432251(ms)
nop_ref_arg
  callee memory: 144,624,920
  caller memory: 241,013,904
  time: 97.761869430542(ms)
nop_ref_both
  callee memory: 144,624,960
  caller memory: 144,625,096
  time: 0.056028366088867(ms)
nop_ref_arg_noreturn
  callee memory: 144,625,040
  caller memory: 144,625,224
  time: 0.038862228393555(ms)

所感

参照渡しが必ずしも遅いわけではない。
参照渡ししていない場合は、巨大な配列を関数に渡したタイミングでコピーが行われるが、
参照渡ししている場合は、巨大な配列のコピーが行われない。

ただし、参照渡しの罠として
関数内部で値を操作すると巨大な配列のコピーが行われるため激遅いプンプン丸になる。
値だけじゃなくて、関数自体が参照渡しに対する操作であることを
定義してあげるといいのだ。

とはいえ、参照渡しはどこで値が書き換わるか分からない。
状態は、クラスのメンバー変数に設け
状態の変化は、それを意図したメソッドを持つオブジェクトでのみ起こるようにすることで
状態の変化(値の書き換え)がどこで行われるか明確にするのが理想だ。

コメントを御覧ください。

ってなわけで
ぽっこぽこ、参照渡しをしているソースコードの改修にとりかかる・・・。~~

参考サイト

http://php.net/manual/ja/language.types.array.php
http://php.net/manual/ja/features.gc.refcounting-basics.php
http://keicode.com/cgi/object-in-php-5.php
http://keicode.com/cgi/object-in-php-5-reference.php