• ベストアンサー

PHPは何故値渡しより参照渡しの方が遅いのでしょうか。

PHPは何故値渡しより参照渡しの方が遅いのでしょうか。 値渡しの場合全て内容をコピーしなければならないので遅くなるように思うのですが。

  • PHP
  • 回答数5
  • ありがとう数6

質問者が選んだベストアンサー

  • ベストアンサー
回答No.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"; ?>

izayoimizuki
質問者

お礼

参照渡しが遅いのではなく 結局コピー+参照カウンタ>最初にコピー と言う事なのですね。 ありがとうございました。

その他の回答 (4)

回答No.4

ベンチマークに使ったコードを実際に見せていただければ、もっと詳しいことが分かると思います。

izayoimizuki
質問者

補足

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)
回答No.3

状況にもよるかと思いますが、 PHPの場合の参照の場合、参照カウンタがそれぞれ作られて、参照されている数の管理をしています。 なので、その分のオーバーヘッドがあって、単純なint のコピーで済む場合より遅い場合があるのかも知れません。

izayoimizuki
質問者

お礼

単純な"参照"ではないわけですね。 ありがとうございます。

回答No.2

私の実感だと、やはり参照渡しの方が速いと思います。 恐らく、PHPで実際にコピー操作が起こるのは、本当にそれが必要になったとき、だと思います。 つまり、$a = $b; も $a =& $b; とやったとも、処理は殆ど同じで、前者であっても実際のデータのコピーは起こらないのだと思います。 (ソースコードレベルで言うと、どちらもzvalue_value共用体のシャローコピーでしかないということ) データのディープコピーが起こるのは、 その後に例えば、$a .= 'B'; とかしたときなど、本当に $a の実体が必要になったとき、だと思います。 このあたりの事情がベンチマークを狂わせているのではないかと思います。

izayoimizuki
質問者

お礼

実際に代入されている値を変更するような動作を大量に行っても 参照渡しのほうが遅いのでベンチマークが翻弄されているだけではないと思います。 誤差と見る事も出来ないわけではない大きさではないですが 挙動の違うロジックで同様の結果が出た為誤差では無いのではないかと思うのです。

  • yambejp
  • ベストアンサー率51% (3827/7415)
回答No.1

それが真実かどうかはべつとして そういう風につくられているからでしょう。 ベンチマークをとれば実証はできますが、 ソースを追っかけて検証することはかなり難しく それをプログラム的に説明されても、理解できるとは 思いませんが・・・。 (逆にそれが理解できるならご自身で検証してください) また結果としてその命題が真実であっても、その 点がボトルネックになるプログラムを書くこと自体がない でしょうし、もし書くことがあってそれが致命的で あっても傾向がわかっているなら対処のしようもあるので、 なにも問題がないかと。

izayoimizuki
質問者

お礼

ありがとうございます。 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%遅かった為に疑問に思った次第です。

関連するQ&A

  • PHP5での参照渡しについて

    お世話になっております。 3点質問させて下さい。 1.PHP5での参照渡しについて PHP5では、参照渡しがデフォルトという記事を読んだのですが、classを使わず、以下のようにfunctionのみで記述した場合、参照渡しになっている気配がありません。 - $test = "テスト"; function a($a){ $a = "参照渡し"; } a($test); echo $test; - 結果:テスト &$aとすると参照渡しになりますが、classを使わない場合はPHP5のデフォルトが参照渡しというのを意識する必要は無く、&を用いた参照渡しを利用する形で良いのでしょうか? 2.オブジェクトについて オブジェクト=class だと思っても良いのでしょうか? また、独立した関数では無く、classを利用した方が良い場面というのはどういう時なのでしょうか? classやオブジェクト指向というものへの私の理解が足りてない事でこのような疑問を抱く事になっていると思います。 classやPHP5のオブジェクト指向について学習するのに便利な書籍やサイトなどあれば紹介して頂けないでしょうか? 3.$_POSTや$_GETについて 他の方のソースを見ていると、何かの処理後にPOSTの値を別の変数に入れている方が多いように思えます。 私もそれにならってPOSTの値をいじる場合は、 変数 = 関数($_POST["test"]); とするようにしています。 ですが、 $_POST["test"] = 関数($_POST["test"]); とするのは何か都合が悪い事があるのでしょうか? 要領を得ない質問で申し訳ありませんが、回答頂けましたら嬉しいです。

    • ベストアンサー
    • PHP
  • PHPの参照渡しについて

    以下のようなコードがあったとき $obj = new stdClass(); $foo = $obj; この場合、動作は いわゆる【参照の値渡し】というとらえかたでよいのでしょうか? 変数$obj には、 new stdClass();という記述によって 新規に作成されたオブジェクトの参照(※正確には別のメモリ内に保持されたオブジェクトのアドレス) が保持されるわけですよね? そのとき $foo = $obj; というコードは$objが保持するオブジェクトのアドレス値を$fooという変数にも コピーするという動作を意味するわけですよね。 この場合、両変数を用いて生成されたオブジェクトのプロパティなどの状態を 共有することとができます。 しかしながら変数$fooに別の値、例えば文字列を代入すうると $foo = "文字列"; とすると$fooの値が変更され$objの値は変更されません。 対して、次のようなコードがあった場合 $foo = &$obj; というコード、これはPHPにおける参照渡しですが、 この場合は$objが指し示す、オブジェクトが保持されているメモリ上のアドレスではなく そのメモリ上に確保された変数$objそのもののアドレスが$fooという変数にコピー? されるのでしょうか? この明示的な参照渡しだと、オブジェクトの状態を共有するのはもちろん $foo = "オブジェクト破棄"; と片方に文字列を代入すると print $obj; 同様に文字列にかわってしまいます。 この本来の意味?であろう参照渡しとは実際 $fooに変数$objそのもののアドレスが保持されるという 解釈でよいのでしょうか? おくわしいかたご教授ください。

    • ベストアンサー
    • PHP
  • PHPの参照わたしについて

    PHPの参照わたしについて質問です。 例えば、ユーザー定義関数の引数を参照渡しとする場合 function test(&$string){ $string .="参照渡し"; } $aaa = "文字列"; test($aaa); print $aaa; 等とすると、一切値のコピーがおこなわれませんよね? 次に function test($string){ $string . ="参照渡し"; return $string; } $bbb ="文字列"; $ccc = test($bbb); print $ccc; と上記のようにした場合、値のコピーが行われるのは 関数の引数に渡すときと 返り値を返すときの2回行われてるんですかね? もし、値のコピーが二回行われているとするなら 次のようにしたら値のコピーは一度だけ・・・少なくとも上の例よりPHPの動作より 軽い?ものになるのでしょうか・ function &test($string){ $string .= "参照わたし"; return $string; } $ddd = "文字列"; $eee =& test($ccc); //ここで関数の返り値を参照渡しする この場合、値のコピーが行われるのは関数に引数を渡すときの1回だけでしょうか? copy on write による動きは無視しておいて、 オブジェクト指向なプログラムでなくユーザー定義関数であれば このように関数の返り値を参照わたしにした方が、理論的?には早いのでしょうか? この場合、引数も参照渡しにすると破壊的関数になるのでそれは避けたいが、 なるべく値のコピーは防ぎたいという状況だと考えてください。 実際、こんな風にすべてのユーザー定義関数の返り値を参照にして定義するなんてこと おそらくないとおもうのですが、単純にコピーの回数がきになったのです。 よろしく御願いします。

    • ベストアンサー
    • PHP
  • 参照渡しの参照渡し

    こんにちは。PHPでの質問をさせて頂きます。 PHPで以下のようなコードを書いた場合値が参照渡しで返って来ません。 解決法をご存知の方がいたら是非教えて下さい。お願いします。 function &hoge(&$data,$key){  $ret=$data[$key]; } //これもダメ function hoge(&$data,$key,&ret){  $ret=$data[$key]; }

    • 締切済み
    • PHP
  • 参照渡し

    javaでintやStringって参照渡しって可能なのでしょうか? プリミティブタイプ以外は参照渡しとあるサイトに書いてあったのですが Stringは何故、値渡しなのでしょうか。 宜しくお願いします。

    • ベストアンサー
    • Java
  • 参照渡しやリファレンスを返す事について

    今疑問に思っているのが、「&」を付ける全ての事象についてす。 今まで参照渡しの方が効率や都合が良い場合にしか利用していませんで したが、「特に値渡しで構わない場合も、参照渡しにした方が良いの か?」という事から出てきた疑問です。 引数の値を直接操作する事はあまり良くない、と言われるのはこの辺の 絡みだと思うのですが、仮に関数内で引数の値を再セットする事を 忠実に守っていた場合、値渡しより常に参照渡しである方がメモリ 効率は良いと思います。(結局関数内で値渡しをする為、消費量としては 大きいと思いますが) 個人的には必要のある時にしか参照渡しを行わないという考えを変えるつもりは ありませんが(それが妥当な目的と利用法であると思うので)、 プロな方としてはどうなのかな、という事を思ってしまったのです。 次に、仮に変数同士で行った場合、ポインタもどきの動作をしている 事から、意味合いは通じるのですが、インスタンスを生成する場合や そのメソッドを呼び出して戻り値がオブジェクトの場合に見受けられます。 (マニュアルを見るとPHP5の場合、インスタンス生成時にそれを利用する 事は非推奨のようですが) 疑問なのは、インスタンスを初めて生成してオブジェクトが作られているのに 「&」で何を参照しに行っているのか?(クラスという雛形?) メソッドが終了したら内部の変数は開放されると思うが、それを参照 する事が何故可能なのか? リファレンス元をunset()してもリファレンス先が消えない事と同じ意味か? そういうものだと言ってしまえばそれでおしまいですが、 そういう疑問があるせいか、それの使いどころが分かりません。 加えてメソッドに関しては、呼び出し時に「&」をつけていたり メソッドの定義自体に「&」があったりして、使い方が分かりません。 呼び出し時の「&」は戻り値の参照でしょうけど、メソッド定義自体の 「&」が意味不明です。 ここばっかりはマニュアルを見ても理解出来ませんでした。 定義の参照って?という感じで考えが止まってしまいます。 (呼び出し時は、require等がされた時点でメモリで格納されている対象を見ている、という事なのか?) どなたかご教示願います。 今、かなりPHPについて悩んできてしまいました(笑)

    • ベストアンサー
    • PHP
  • javaでは基本型の参照渡しは無理ですか?

    先日のjavaの授業で値渡しと参照渡しについて学びました。 授業では オブジェクトは参照渡しになり 基本型は値渡しにされる(参照渡しは不可能) と教えられました。 オブジェクトを値渡しにできないのはなんとなく納得できたのですが 基本型を参照渡しにできないのが納得できません。 そこで 1.基本型を参照渡しにすることはできないのか? 2.基本型を参照渡しすることができないのはなぜか? 3.オブジェクトを値渡しすることができないのはなぜか? の3点を教えて頂きたいです。 よろしくお願いします。

    • ベストアンサー
    • Java
  • javascript 値渡し?参照?

    javascriptの値渡しについて勉強中なのですが、質問です。 ----------------------------------------------------------------- function aaa(a,b){ var ttt = a ; a = b ; b = ttt ; } var a1 = 1 ; var a2 = 2 ; aaa (a1,a2) ; document.write(a1 ,a2); ------------------------------------------------------------------- 上記のようなコードで引数の値が交換できると思うのですが、正しくは出来ないようです。 1,0と出力されてしまいます。 どうしてなのでしょうか。 お聞きしたいのは、 ・この引数は値渡しなのか、参照なのか。 ・どうして出来ないのか詳しい理由。 よろしくお願いします。

  • これは参照渡し・値渡ししてる事になりますか?

    Dim a As String Sub マクロ1() a = "test" Call マクロ2 a = Empty End Sub Sub マクロ2() MsgBox a End Sub これは参照渡し・値渡ししてる事になりますか? ByRefやByValを使わない限り、 参照渡しまたは値渡ししてるとは言えないのでしょうか? ご回答よろしくお願いします。

  • 参照・値渡しについて

    下記プログラム(ソース1)を実行すると「1」という値が出力されます。しかし、2行目をprivate static int a;とすると「0」という値が出力されます。 オブジェクトは参照渡しで基本データ型は値渡しと思ったのですが、2行目の値をString型で行ったところ全く変更されていない値が出力されました。(ソース2) 一体どういうことでしょうか? 回答のほどよろしくお願い致します。 (ソース1) public class X{ private static int a[] = new int[1]; public static void main(String []args){ modify(a); System.out.println(a[0]); } public static void modify(int a[]){ a[0]++; } } (ソース2) public class X{ static String a = "a"; public static void main(String []args){ modify(a); System.out.println(a); } public static void modify(String a){ a = "b"; } }

    • ベストアンサー
    • Java

専門家に質問してみよう