はじめ
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