PHPにおけるコピーオンライトのメカニズム
PHPでは値渡しの場合でも、内部的にはrefcount(参照カウント)を増やすことで同じメモリ領域を参照する最適化が行われています。これは「コピーオンライト(Copy-On-Write)」と呼ばれるメカニズムです。
具体的には以下のように動作します:
-
値渡しで変数を渡す際、PHPは即座に新しいメモリ領域を確保してデータをコピーするのではなく、元の変数が参照しているzval(PHP内部の値を表現する構造体)のrefcountを増やします。
-
この時点では、元の変数と関数内の引数変数は同じzvalを参照しています。
-
関数内で引数の値を変更しようとした場合にのみ、PHPは新しいzvalを作成し、そこに値をコピーします(これが「コピーオンライト」の由来です)。
例えば:
$a = "hello";
function foo($x) {
// この時点では$aと$xは同じzvalを参照している
// $xの値を変更しようとすると、新しいzvalが作成される
$x .= " world";
}
foo($a);
この最適化により、PHPは不必要なメモリ割り当てとコピー操作を避けることができ、パフォーマンスが向上します。
ただし、大きなデータ構造(大きな配列やオブジェクトなど)を扱う場合、値渡しは依然としてパフォーマンスに影響を与える可能性があります。そのような場合は、参照渡しを使用することで、不要なコピーを完全に回避できます。
また、PHP 7以降では、さらに最適化が進み、小さな整数や文字列などの一部のデータ型については、zvalを使用せずに直接値を保持する最適化が行われています。これにより、メモリ使用量とパフォーマンスがさらに改善されています。
PHP 7以降では、小さな整数や短い文字列などの一部のデータ型に対して、zvalを使用せずに直接値を保持する最適化が行われており、これによりrefcountを使用しない場合があります。
具体的には:
-
小さな整数(small integers):
64ビットシステムでは-2^63から2^63-1までの整数、32ビットシステムでは-2^31から2^31-1までの整数は、zvalを使用せずに直接値として保存されます。これらの値にはrefcountが使用されません。 -
短い文字列(interned strings):
一定の長さ以下の文字列(通常は8バイト以下)は、zvalを使用せずに直接保存されます。これらの文字列もrefcountを使用しません。 -
true、false、null:
これらの特殊な値も直接保存され、refcountを使用しません。
この最適化により、以下のような利点があります:
- メモリ使用量の削減: zval構造体を使用しないため、メモリ使用量が減少します。
- パフォーマンスの向上: refcountの操作が不要になるため、処理速度が向上します。
- キャッシュ効率の改善: 直接値を保持することで、CPUキャッシュの効率が向上します。
ただし、大きな整数、長い文字列、配列、オブジェクトなどの複雑なデータ型については、従来通りzvalとrefcountが使用されます。
この最適化は、PHPの内部実装の詳細であり、通常のPHPプログラミングでは意識する必要はありませんが、大量のデータを扱う場合やパフォーマンスクリティカルな場面では、この最適化の恩恵を受けることができます。
良記事:
【PHP超入門】値の代入と値渡しについて
【PHP超入門】参照(リファレンス)の代入について
Discussion