- ベストアンサー
PHPは何故値渡しより参照渡しの方が遅いのでしょうか。
PHPは何故値渡しより参照渡しの方が遅いのでしょうか。 値渡しの場合全て内容をコピーしなければならないので遅くなるように思うのですが。
- みんなの回答 (5)
- 専門家の回答
質問者が選んだベストアンサー
参照が速くない大きな理由の一つは、 $a =& $b; $c = $a; // ここで値のコピーが発生と思われる と同じことが、組み込み関数をコールするところで起こってしまうため、 参照の意味が無くなることが多いことだと思います。 提示していただいたベンチマークのコードだと、 is_array, implodeのところで$dataがコピーされていると思います。 関数をコールする都度これが起こると思われるので、 それが速度低下の大きな原因だと思います。 やはり「参照渡しそのものが遅い」わけではないと思います。 ただ、上の理由から、余程注意しないと、「参照渡しを使うと全体として遅くなる」 という結果を招くことは確かだと思います。 また、場合によってはガベコレの影響も大きいようなので、 ベンチマークの時は注意する必要があると思います。 (下のサンプルのガベコレのところを変えるだけで随分違います) 下は本当に差が出るサンプルです。 <?php // 配列 $array に $data をプッシュしたものを返す関数 function pushed($array, $data) { // ここで$arrayを操作するため、 // 実際に$arrayのデータをコピーする必要が生じる。 // ここをコメントアウトすれば、 // $arrayが値渡しであっても、 // $arrayの中身のコピーは起こらない。 $array []= $data; return $array; } // 配列 $array に $data をプッシュする関数 function push(&$array, $data) { $array []= $data; } $n = 10; $time = 0; for ($i = 0; $i < $n; $i++) { $dat = range(0,99999); $res = null; // GCの時間を除外するため、ここでGCを起こす $t = microtime(true); $res = pushed($dat, null); $time += microtime(true) - $t; } echo "V={$time}\n"; $time = 0; for ($i = 0; $i < $n; $i++) { $dat = range(0,99999); $t = microtime(true); push($dat, null); $time += microtime(true) - $t; } echo "R={$time}\n"; ?>
その他の回答 (4)
- nezumi0t0k0
- ベストアンサー率70% (12/17)
ベンチマークに使ったコードを実際に見せていただければ、もっと詳しいことが分かると思います。
補足
function Ufun_implode_recursive ($istr, $data) { if (is_array ($data)) { while (list ($key,) = each ($data)) { $data[$key] = Ufun_implode_recursive ($istr, $data[$key]); } $data = implode ($istr, $data); } return $data; } と function Ufun_implode_recursive ($istr, &$data) { if (is_array ($data)) { while (list ($key,) = each ($data)) { Ufun_implode_recursive ($istr, $data[$key]); } $data = implode ($istr, $data); } } です。 他の2つに関しましては余り意味が無いので既に破棄しています。
- BLUEPIXY
- ベストアンサー率50% (3003/5914)
状況にもよるかと思いますが、 PHPの場合の参照の場合、参照カウンタがそれぞれ作られて、参照されている数の管理をしています。 なので、その分のオーバーヘッドがあって、単純なint のコピーで済む場合より遅い場合があるのかも知れません。
お礼
単純な"参照"ではないわけですね。 ありがとうございます。
- nezumi0t0k0
- ベストアンサー率70% (12/17)
私の実感だと、やはり参照渡しの方が速いと思います。 恐らく、PHPで実際にコピー操作が起こるのは、本当にそれが必要になったとき、だと思います。 つまり、$a = $b; も $a =& $b; とやったとも、処理は殆ど同じで、前者であっても実際のデータのコピーは起こらないのだと思います。 (ソースコードレベルで言うと、どちらもzvalue_value共用体のシャローコピーでしかないということ) データのディープコピーが起こるのは、 その後に例えば、$a .= 'B'; とかしたときなど、本当に $a の実体が必要になったとき、だと思います。 このあたりの事情がベンチマークを狂わせているのではないかと思います。
お礼
実際に代入されている値を変更するような動作を大量に行っても 参照渡しのほうが遅いのでベンチマークが翻弄されているだけではないと思います。 誤差と見る事も出来ないわけではない大きさではないですが 挙動の違うロジックで同様の結果が出た為誤差では無いのではないかと思うのです。
- yambejp
- ベストアンサー率51% (3827/7415)
それが真実かどうかはべつとして そういう風につくられているからでしょう。 ベンチマークをとれば実証はできますが、 ソースを追っかけて検証することはかなり難しく それをプログラム的に説明されても、理解できるとは 思いませんが・・・。 (逆にそれが理解できるならご自身で検証してください) また結果としてその命題が真実であっても、その 点がボトルネックになるプログラムを書くこと自体がない でしょうし、もし書くことがあってそれが致命的で あっても傾向がわかっているなら対処のしようもあるので、 なにも問題がないかと。
お礼
ありがとうございます。 http://rio.st/archives/2003/12/php_tips_2.html の記事が事実なのか検証の為に 関数が再帰的に呼び出しその時の引数を参照渡しで渡すものと値渡しで渡すもの for文で変数を配列に参照渡しでコピーするもの値渡しでコピーするもの 要素数10*10*10の3次元配列をimplodeで結合し全ての要素を結合する際に参照渡しで行うものと値渡しで行うもの を作成し実際にベンチマークを行いました。 Solaris 10+Apache2.0.52+PHP4.3.2 Fedora Core 5(Kernel 2.6.17)+Apache 2.2.3+PHP 5.1.4 Windows XP+Apache 2.2.3+PHP 5.1.4 以上の環境での100回の結果を大きい方と小さい方から20個計40個を除去し 残り60個の平均が参照渡しの方が約2~5%遅かった為に疑問に思った次第です。
お礼
参照渡しが遅いのではなく 結局コピー+参照カウンタ>最初にコピー と言う事なのですね。 ありがとうございました。