web-dev-qa-db-ja.com

私はPhp 7でPHP 5.6と比較してより多くのメモリ消費に直面しています

ベンチマークを行っていたとき、PHP 7はPHP 5.6。

そこで、テストを行いました。次のもののみを含むスクリプトを実行しました。

  $a=10;

以下は、モジュールなしでPHP CLIを使用したときに使用したメモリの結果です(php -n

php 5.6 = 222600 Bytes
php 7.0 = 350448 Bytes

* PHP 5.6.23 (cli) (built: Jun 22 2016 12:13:15)
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies 

* PHP 7.0.9 (cli) (built: Jul 20 2016 10:47:41) ( NTS )
Copyright (c) 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright (c) 1998-2016 Zend Technologies

環境は

  • OS:ウィンドウ10
  • サーバー:IIS(ただし、サーバーではなくCLIを使用しましたが)、高速cgiを使用
  • マシン:64ビット
  • php-5.6.23-nts-Win32-VC11-x64
  • php-7.0.9-nts-Win32-VC14-x64

誰が私がこの結果を得た理由を説明できますか?


追加のテスト

@gordonが示唆するように、このコードを使用して、

$i=0;
while ($i++ < 100000) ;

php 5.6:227408バイト

php 7.0:386640バイト

私はこのコードでメモリ使用量を決定しました:

echo PHP_EOL;
echo "Memory Usage :".memory_get_usage();
echo PHP_EOL;
echo "Real Memory Usage :".memory_get_usage(true);
echo PHP_EOL;
echo "Real Peak Memory Usage :".memory_get_peak_usage(true);
echo PHP_EOL;
echo "Peak Memory Usage :".memory_get_peak_usage();
19
developerCK

あなたの質問への答えを理解するには-PHP5とPHP7がメモリを割り当てる方法を理解する必要があります。

PHP5 Zend Engineの構造であると想定して、「リクエストによる」メモリの割り当て。

PHP7これは、この側でいくつかの最適化が行われているため、「チャンク単位」でメモリを割り当てる

  • 起動時にメモリの大きなチャンクを割り当てます
  • アプリ内割り当てでは、断片化を避けるために小さなチャンクを割り当てます

この違いにより、パフォーマンスが非常に向上します(エンジンは必要なたびにランタイムにメモリを割り当て、断片化の時間を節約する必要がないため)が、「非常に小さな」プログラムのメモリ消費量が増加します。 「チャンクサイズ」。

そして、はい、PHP7は大きなプログラムでメモリを非常に節約します。

以下の写真にこの違いをすべて表示できます。

PHP memory allocation for large programsPHP memory allocation for small programs

ベンチマークで構築されたグラフィック:1.php

<?php

ini_set('memory_limit', '5G');
$a=range(1,$argv[1]);

echo PHP_EOL;
echo "Memory Usage :".memory_get_usage();
echo PHP_EOL;
echo "Real Memory Usage :".memory_get_usage(true);
echo PHP_EOL;
echo "Real Peak Memory Usage :".memory_get_peak_usage(true);
echo PHP_EOL;
echo "Peak Memory Usage :".memory_get_peak_usage();
echo PHP_EOL;

bench.sh

// Small programs
(for i in $(seq 0 5 5000);do php5 dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php5.m
(for i in $(seq 0 5 5000);do php dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php7.m
//Large Programs
(for i in $(seq 0 50 100000);do php5 dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php5.m    
(for i in $(seq 0 50 100000);do php dev/Tools/mem/1.php $i|cut -f 2 -d:|sed -r 's/^$/;/g'|sed -r 's/([0-9]+)$/\1,/g'|tr -d '\n'; echo $i; done)|tr -d '\n'|sed -r 's/$/]/g'|sed -r 's/^;/[/g'>php7.m

オクターブ引き出し

php7;php7=ans;
php5;php5=ans;
plot(php5(:,5)',[php5(:,1:4)';php7(:,1:4)']');
legend("PHP5 mgu", "PHP5 rmu", "PHP5 rpmu", "PHP5 pmu","PHP7 mgu", "PHP7 rmu", "PHP7 rpmu", "PHP7 pmu");

続きを読む

  1. 公式のPHP7/PHP-NGプレゼンテーション: https://drive.google.com/file/d/0B3UKOMH_4lgBUTdjUGxIZ3l1Ukk/view
  2. 公式のPHP7/PHP-NG内部変更の説明: https://wiki.php.net/phpng-int
  3. 公式の拡張機能移行ガイド: https://wiki.php.net/phpng-upgrading
  4. @NikiCの良い記事: http://nikic.github.io/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html および http://nikic.github.io/2015/06/19/Internal-value-representation-in-PHP-7-part-2
  5. PHP5内部の詳細: http://www.phpinternalsbook.com/
  6. Badoo PHP5-> PHP7の詳細と成功事例: https://techblog.badoo.com/blog/2016/03/14/how-badoo-saved-one-million-dollars-switching-to-php7/
22
MobDev

テストコードは非常に単純であるため、テストではPHP 7.0でより多くのメモリ使用量が示されます。

PHP 7.0は、内部ZENDエンジン(インタープリターコア)の抜本的な書き換えにより、PHP 5.6

Gordon がコメントしたように、PHP 7.0の新機能と改善には "bootstrap"が必要です。小さいコードでテストした結果。

もっと複雑なもので試してみましょう:10.000整数の配列を構築し、Quicksortalgorythmを使用してソートします。

私が得る結果は次のとおりです。

PHP 7.0

Memory Usage: 1432752
Real Memory Usage: 4194304
Real Peak Memory Usage: 4194304
Peak Memory Usage: 3152360


PHP 5.6

Memory Usage: 2756744
Real Memory Usage: 4980736
Real Peak Memory Usage: 6029312
Peak Memory Usage: 5710464

それでも、単純な20行のクイックソートは、数千行のコード、多くのクラス宣言、多くのインスタンスを備えた実際のアプリケーションとはかけ離れています...

http://phptester.net でテストを実行しました

以下はコードです

<?php
function quick_sort($array)
{
    $length = count($array);
    $pivot = $array[0];
    $left = $right = array();
    for($i = 1; $i < count($array); $i++)
    {
        if($array[$i] < $pivot)
        {
            $left[] = $array[$i];
        }
        else
        {
            $right[] = $array[$i];
        }
    }
    return array_merge(quick_sort($left), array($pivot), quick_sort($right));
}

$unsorted = array();
for($i=0;$i<10000;$i++)
{
    $unsorted[] = Rand(1,1000000);
}

$sorted = quick_sort($unsorted);

$lf = "<br/>";

echo $lf;
echo "Memory Usage: ".memory_get_usage();
echo $lf;
echo "Real Memory Usage: ".memory_get_usage(true);
echo $lf;
echo "Real Peak Memory Usage: ".memory_get_peak_usage(true);
echo $lf;
echo "Peak Memory Usage: ".memory_get_peak_usage();
echo $lf;

PHPのクイックソートアルゴリズムの功績: http://andrewbaxter.net/quicksort.php

13
Paolo

前もって、実際のコードのPHP 7でより高いメモリ使用量が報告されている場合、最も可能性の高い原因は、PHP 7がメモリ使用量の一部としてmysqlndバッファクエリのメモリ使用量を報告することです。 PHP 5では、このメモリ使用量は報告されませんでした(もちろん、メモリはまだ使用されていました)。大規模なクエリの場合、これは非常に大きな違いをもたらします。

さて、実際のケースでは、基本的にリクエストの起動直後のPHPのメモリ使用量です。 MobDevによる回答 は、「実際の」メモリ使用量に矛盾がある理由を既に説明しています。これは、PHPのアロケータがカーネルのシステムアロケータから要求したメモリ量を報告するメモリ使用量メトリックです。 MobDevが指摘しているようにPHP 7はより大きなチャンク(2MB)でメモリを割り当て、割り当てられたチャンクのキャッシングについても積極的です。

ただし、これは、これらのアロケータの詳細を考慮しない「非実」メモリ使用量の不一致を説明しません。メモリプロファイラを使用すると、メモリが正確に使用されているかどうかを簡単に確認できます。 PHPをUSE_ZEND_ALLOC=0 valgrind --tool=massifで実行します。 USE_ZEND_ALLOC=0部分は、独自のアロケーターを使用しないようにPHPに指示します。

まず第一に、実際のメモリ使用量とPHPによって報告されるメモリ使用量がかなり異なることを示します。 Massifは、PHP 5.6で3.2MB、PHP 7.で2.3MBの使用量を表示します。理由は、PHPは自身のアロケータ(ZMM)を通過するメモリのみを報告するためです。複数のリクエストにまたがって生き残ることは、それを使用して割り当てられません。

システムアロケーターを通過する最大の割り当て(メモリ使用量で報告されない)は次のとおりです。

                       | PHP 5.6 | PHP 7
interned string buffer | 1 MB    | 150 KB + strings
GC buffer              | 320 KB  | 320 KB
internal classes/funcs | >1.3 MB | >0.5 MB

「内部クラス/ファンク」数は粗い下限です。これは、カウントするのが難しい小さな割り当てが多数含まれているためです。 1つの主な違いは明らかです。PHP 7は固定インターンストリングバッファーを使用しません(リストされているサイズは、私が見ているハッシュテーブルバッファー用で、ストリング自体のサイズは含まれません)。

ただし、これは実際に報告されたメモリ使用量の質問にはまだ答えません。この場合、最大の割り当ては次のとおりです。

             | PHP 5.6 | PHP 7
VM stack     | 130 KB  | 256 KB
Object store | 64 KB   | (8 KB)
CG arena     | ---     | 64 KB

ここにはいくつかの違いがあります。主なものは、PHP 7がより大きなVMページサイズ(約2倍)を使用することです。さらにPHP 7は、アリーナを使用して特定の構造(ユーザー関数など)を格納します。これは、デフォルトサイズの64KBで始まります。一方、PHP 7では、オブジェクトストアバッファーのサイズが大幅に小さくなりました。

したがって、本質的にTL; DRの答えは、PHP 7はより大きなVMスタックページサイズを使用するということです。

9
NikiC