Edited at

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

More than 3 years have passed since last update.


はじめ

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