質問

PHPでは、次のように関数宣言のパラメーターにアンパサンドを追加することにより、関数パラメーターを参照で渡すことができます。

function foo(&$bar)
{
    // ...
}

今、これはパフォーマンスを改善するように設計されているのではなく、通常はスコープ外の変数を関数が変更できるように設計されていることを認識しています。

代わりに、PHPは変更時までオブジェクト(および場合によっては配列)のコピーを避けるためにコピーオンライトを使用するようです。そのため、パラメータを変更しない関数の場合、参照で渡した場合と同じ効果が得られます。

ただし、参照渡し時にコピーオンライトロジックが短絡する可能性があるかどうか、およびパフォーマンスに影響があるかどうか疑問に思っていました。

ETA:確かに、高速ではないと思いますが、これは参照の目的ではないことをよく知っています。だから、私は自分の推測はかなり良いと思う、私は本当にフードの下で何が起こっているかを本当に知っている誰かからの答えを探しています。 5年間のPHP開発では、ソースを読むだけではPHP内部の質の高い情報を取得することは常に困難でした。

役に立ちましたか?

解決

Zend Engineはコピーオンライトを使用します。参照を自分で使用する場合、少し余分なオーバーヘッドが発生します。 この言及執筆時点、およびマニュアルのコメントには他のリンクが含まれています。

(編集)オブジェクトと参照のマニュアルページオブジェクト変数が参照とどのように異なるかについてもう少し情報が含まれています。

他のヒント

20 kBの文字列で関数を呼び出して100 000回反復するテストでは、結果は次のようになります。

パラメータの読み取り/使用のみを行う関数

pass by value:      0.12065005 seconds
pass by reference:  1.52171397 seconds

パラメータを記述/変更する関数

pass by value:      1.52223396 seconds
pass by reference:  1.52388787 seconds

結論

  1. 値によるパラメーターの受け渡しは常に高速です

  2. 関数が渡された変数の値を変更する場合、実際の目的では、値によるよりも参照による受け渡しと同じです

与えられた答えに確信が持てなかったので、これについていくつかのテストを実行しました。

私の結果は、参照による大きな配列または文字列の受け渡しが大幅に高速であることを示しています。

ここに私の結果があります: ベンチマーク

Y軸(実行)は、1秒間に関数を呼び出すことができる回数* 10

テストは関数/変数ごとに8回繰り返されました

そして私が使用した変数は次のとおりです。

$large_array = array_fill(PHP_INT_MAX / 2, 1000, 'a');
$small_array = array('this', 'is', 'a', 'small', 'array');
$large_object = (object)$large_array;
$large_string = str_repeat('a', 100000);
$small_string = 'this is a small string';
$value = PHP_INT_MAX / 2;

これらは関数です:

function pass_by_ref(&$var) {
}

function pass_by_val($var) {
}

2つの同じ関数に渡す10kバイトの文字列の値と参照を試しました。 1つは値で引数を取り、2つ目は参照で受け取ります。それらは一般的な関数でした-引数を取り、単純な処理を行い、値を返します。私は両方を10万回呼び出しましたが、参照はパフォーマンスを向上させるように設計されていないことがわかりました-参照の利益は4-5%に近く、文字列が十分に大きくなったときにのみ増加します(100k以上、6-7%の改善をもたらしました) 。したがって、私の結論は、パフォーマンスを向上させるために参照を使用しないでください。これはそのためではありません。

PHPバージョン5.3.1を使用しました

いいえ、それは速くありません。 さらに、マニュアルでは、パフォーマンスを向上させるために参照を使用しないでくださいと具体的に述べています。

編集:どこにあるかわかりませんが、そこにあります!

テスト用のコードに勝るものはありません

<?PHP
$r = array();

for($i=0; $i<500;$i++){
$r[]=5;
}

function a($r){
$r[0]=1;
}
function b(&$r){
$r[0]=1;
}

$start = microtime(true);
for($i=0;$i<9999;$i++){
  //a($r);
  b($r);
}
$end = microtime(true);

echo $end-$start;
?>

最終結果!配列が大きいほど(または呼び出し回数が多いほど)、差は大きくなります。したがって、この場合、値は関数内で変更されるため、参照による呼び出しは高速です。

それ以外の場合、「参照による」間に実質的な違いはありません。および「値による」と言うと、コンパイラは、必要がない場合に毎回新しいコピーを作成しないように十分にスマートです。

私が取り組んでいるプロジェクトに基づいて、これを実際の例でベンチマークしようとしました。いつものように、違いは些細なことですが、結果はやや予想外でした。私が見たほとんどのベンチマークでは、呼び出された関数は実際に渡される値を変更しません。単純なstr_replace()を実行しました。

**Pass by Value Test Code:**

$originalString=''; // 1000 pseudo-random digits

function replace($string) {
    return str_replace('1', 'x',$string);
}
$output = '';
/* set start time */
$mtime = microtime();
$mtime = explode(" ", $mtime);
$mtime = $mtime[1] + $mtime[0];
$tstart = $mtime;
set_time_limit(0);

for ($i = 0; $i < 10; $i++ ) {
    for ($j = 0; $j < 1000000; $j++) {
        $string = $originalString;
        $string = replace($string);
    }
}

/* report how long it took */
$mtime = microtime();
$mtime = explode(" ", $mtime);
$mtime = $mtime[1] + $mtime[0];
$tend = $mtime;
$totalTime = ($tend - $tstart);
$totalTime = sprintf("%2.4f s", $totalTime);
$output .= "\n" . 'Total Time' .
    ': ' . $totalTime;
$output .= "\n" . $string;
echo $output;

参照テストコードによるパス

同じことを除いて

function replace(&$string) {
    $string = str_replace('1', 'x',$string);
}
/* ... */
replace($string);

秒単位の結果(1000万回の反復):

PHP 5
    Value:     14.1007
    Reference: 11.5564

PHP 7
    Value:     3.0799
    Reference: 2.9489

違いは関数呼び出しごとに1ミリ秒の小数ですが、このユースケースでは、参照渡しはPHP 5とPHP 7の両方で高速です。

(注:PHP 7テストはより高速なマシンで実行されました。PHP7はより高速ですが、おそらくそれほど高速ではありません。)

簡単です。何もテストする必要はありません。 ユースケースに依存。

値による受け渡しは、少量の引数に対する参照よりも常に値によって速くなります。これは、アーキテクチャーがレジスター(ABI)を通過できる変数の数によって異なります。

たとえば、x64を使用すると、4つの値をそれぞれ64ビットでレジスタに渡すことができます。 https://en.wikipedia.org/wiki/X86_calling_conventions

これは、ポインタを逆参照する必要がなく、値を直接使用するだけだからです。

渡す必要があるデータがABIよりも大きい場合、残りの値はスタックされます。 この場合、配列またはオブジェクト(インスタンスはクラス、または構造体+ヘッダー)は常に参照により高速になります。

これは、参照が単なるデータ(データ自体ではない)へのポインタであり、固定サイズ、たとえばマシンに応じて32ビットまたは64ビットであるためです。そのポインターは1つのCPUレジスターに収まります。

PHPはC / C ++で記述されているため、同じように動作するはずです。

&amp;を追加する必要はありません。オブジェクトを渡すときの演算子。 PHP 5以降では、オブジェクトは参照によって渡されます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top