この投稿は、現在の大規模なZendFrameworkプロジェクトのメモリ消費量の調査から着想を得たものです。 いつものように、研究の結果によると、私たちがプログラムの慢さにショックを受けました。これは、PHPで何か大きなものを書くときによくあります。 そして、おそらく、PHPだけではありません。
しかし、まず最初に。
この記事は、次の記事の論理的な続きです。
どのように測定しますか
まず、「重量」の測定方法を決定します。 テンプレートは次のとおりです。
$startMemory = 0; $startMemory = memory_get_usage();
このようなテンプレートは、新しく割り当てられたメモリ、つまり変数のメモリを測定するのに適しています。 しかし、スクリプトが開始される前にメモリに保存されるため、このアプローチでどの程度の定義、つまり関数とクラスの記述が食べられるかを測定することは不可能です。 定義を測定するために、次のテンプレートを使用します。
$startMemory = 0; $startMemory = memory_get_usage();
$ include_overheadは、内部のニーズに合わせてincludeステートメントが消費する量です。 この記事では、$ include_overheadの測定方法については学習しません。 消費されるメモリのサイズは、次の3つのことに依存することに注意してください。
- 絶対ファイルパスの長さ
- このファイルはどのように含まれていましたか(8、16、32、64など、内部構造に追加の選択があります)
- 将来のために定期的にメモリを割り当てるPHP自体の内部構造の充実。
誰もがこの詳細を調査することに興味がある場合、ファイル
run.include-test.phpを調べることができます。これは、インクルード中のメモリ消費の不均一性を非常によく示しています。 また、以下のすべてのテストでは、正確な値は必要ないが、32ビットバージョンと64ビットバージョンの傾向と違いが必要なので、およそ$ include_overheadを測定します。
「オブジェクト」の重さ
そのため、TestSuiteは、多数のテストを自動的に実行するように作成されました。 すべてのテストは、
Ubuntu 12.04.1 LTS i386および
Ubuntu 12.04.1 LTS amd64の VirtualBoxで実行されました。 PHPバージョン-5.3.10、ZendFramework-1.11.11。 コンソールで実行するコマンド:
php run.testsuite-without-accelerator.php
さらに、制御のためにGentoo amd64を使用してマシンでテストを行いました。 コンソールから起動した場合、PHPアクセラレータは機能しません。 結果は次のとおりです。
テスト名 | 説明 | Ubuntu x86、 PHP 5.3.10、 ZF 1.11.11 | Ubuntu x86-64、 PHP 5.3.10、 ZF 1.11.11 | Gentoo x86-64、 PHP 5.3.15、 ZF 1.11.4 |
---|
a.mention_variable | 可変言及 | 44 | 80 | 48 |
a.new_null_variable | 新しいヌル変数の作成 | 108 | 208 | 144 |
a.unset_null_variable | 変数を削除 | -108 | -208 | -144 |
stdClass.new | オブジェクト作成 | 120 | 232 | 168 |
stdClass.tovar1 | オブジェクトを作成して$ aをリンクする | 264 | 512 | 352 |
stdClass.tovar2_unset_and_thesame | リンク$ aを削除し、リンク$ aを再作成します | 0 | 0 | 0 |
stdClass.tovar3_unset_and_another | リンク$ aを削除してリンク$ bを作成 | 0 | 0 | 0 |
stdClass.tovar4_another | オブジェクトを作成して$ cをリンクする | 264 | 512 | 352 |
stdClass.tovar5_addlink | $ aを$ bと同じオブジェクトにリンクします | 64 | 128 | 96 |
stdClass.z.free_memory | $ a、$ b、および$ cリンクの削除 | -592 | -1152 | -800 |
myclass.a.empty | クラスAの説明 | 700 | 1344 | 1128 |
myclass.aa.interface | インターフェイスの説明A | 700 | 1344 | 1128 |
myclass.ab.final | 最終クラスABの説明 | 700 | 1344 | 1128 |
myclass.ac.abstract | AC抽象クラスの説明 | 700 | 1344 | 1128 |
myclass.b.extended.empty | Aを拡張するクラスBの説明 | 700 | 1344 | 1128 |
myclass.c.empty.namespace | 空の名前空間Cの説明 | 0 | 0 | 0 |
myclass.d.construct | クラスDコンストラクターの説明 | 1104 | 2288 | 1920 |
myclass.dd.method | メソッドを使用したDDクラスの説明 | 1088 | 2280 | 1912 |
myclass.ddd.private.var | プライベート変数を持つDDDクラスの説明 | 960 | 1840 | 1472 |
myclass.dddd.public.var | パブリック変数を持つDDDDクラスの説明 | 960 | 1840 | 1472 |
myclass.ddddd.static.var | 静的変数を持つDDDDDクラスの説明 | 960 | 1840 | 1472 |
myclass.e.extended.destruct | クラスDを拡張するデストラクタを使用したクラスEの説明 | 1344 | 2704 | 2272 |
myclass.e.instance.ab | ABオブジェクトと$ eリンクの作成 | 264 | 512 | 352 |
myclass.e.instance.ddddd | DDDDDオブジェクトと$ eリンクの作成 | 0 | 0 | 0 |
myclass.e.instance.e | オブジェクトEを作成し、$ eにリンクします | 0 | 0 | 0 |
myclass.f.instance.ddddd | DDDDDオブジェクトと$ fリンクの作成 | 264 | 512 | 352 |
myclass.z.free_memory | リンクの削除$ e、$ f | -484 | -944 | -656 |
zend.a.init.autoload | ZendFrameworkの自動ロードを初期化する | 127 444 | 276,288 | 249 232 |
zend.a.init.model | ベースのデフォルトアダプターの初期化 | 1 018 388 | 2 081 600 | 1 871 256 |
zend.extended.controller1 | Zend_Controller_Actionからのコントローラー定義。 途中で、標準のZendクラスをロードします | 378,296 | 809 384 | 712 816 |
zend.extended.controller2 | コントローラーの定義。 Zendクラスは既にロードされています。クラスの重量を確認してください | 11 328 | 19 608 | 16,008 |
zend.extended.model1 | Zend_Db_Tableによるモデル定義。 途中で、標準Zendクラスのロードが行われます。 | 27 936 | 48 544 | 40 224 |
zend.extended.model2 | モデルの定義。 Zendクラスは既にロードされています。クラスの重量を確認してください | 27 936 | 48 536 | 40 208 |
zend.use.model1.e.instance1 | Model1オブジェクトと$ eリンクの作成 | 2492 | 4648 | 3432 |
zend.use.model1.f.instance2 | Model1オブジェクトの作成と$ fのリンク | 1764 | 3256 | 2488 |
zend.use.model1.g.instance3 | Model1オブジェクトと$ gリンクの作成 | 1764 | 3256 | 2488 |
zend.use.model2.e.instance1 | Model2オブジェクトと$ eリンクの作成 | 740 | 1400 | 944 |
zend.use.model2.f.instance2 | Model2オブジェクトと$ f参照の作成 | 0 | 0 | 0 |
Gentooビルドが消費するメモリが10〜20%少ないことに気付くかもしれません。まれに、最大50%の節約になります。 どうやら、内部構造のサイズはプロセッサの最適化に依存します。 実験のために、さまざまなバージョンのCFLAGSを使用してphpを再構築しましたが、これ以上の消費は開始しませんでした。 どうやら、違いはPHP自体の再構築によるものではなく、標準のCisternライブラリの再構築によるものです。
上記のように、$ include_overheadを正確に測定することは難しいため、これらのテストを実行すると、同じものを消費するテストであっても、4、8、12、16バイトにジャンプするメモリ消費が発生する場合があります。 これに集中しないでください。 テストを異なる順序で実行し、実際のメモリ消費量を設定しました。
ZendFrameworkに関連するテストについて話しましょう。 Zendのクラス定義をメモリにロードすると、リソースが大量に消費されますが、オブジェクト参照はすでにそれほど消費していません。 Controller2は、すべての中間クラスが既にメモリ内にある場合に同様のコントローラーがどれだけ食べるかを確認するために必要です。 Model2は同じ目的で作成されます。
潜在的に、PHPアクセラレータを使用すると、すべての定義のメモリが節約されます。定義は既にメモリに格納されているためです。 このステートメントを確認しましょう。
加速器試験
APCはテスト用に取得され、テストはスクリプトを使用してWeb経由で実行されました。
php run.testsuite-with-accelerator.php
結果は、アクセラレータが効果を発揮するテストのみを示しています。
テスト名 | 説明 | Ubuntu x86、 PHP 5.3.10、 ZF 1.11.11、 空のキャッシュ | Ubuntu x86、 PHP 5.3.10、 ZF 1.11.11、 リフレッシュ | Ubuntu x86-64、 PHP 5.3.10、 ZF 1.11.11、 空のキャッシュ | Ubuntu x86-64、 PHP 5.3.10、 ZF 1.11.11、 リフレッシュ |
---|
myclass.a.empty | クラスAの説明 | 840 | 672 | 1480 | 1256 |
myclass.aa.interface | インターフェイスの説明A | 856 | 676 | 1512 | 1264 |
myclass.ab.final | 最終クラスABの説明 | 844 | 672 | 1488 | 1256 |
myclass.ac.abstract | AC抽象クラスの説明 | 852 | 680 | 1504 | 1264 |
myclass.b.extended.empty | Aを拡張するクラスBの説明 | 912 | 700 | 1512 | 1264 |
myclass.c.empty.namespace | 空の名前空間Cの説明 | 176 | -16 | 184 | -72 |
myclass.d.construct | クラスDコンストラクターの説明 | 1256 | 960 | 2448 | 1736 |
myclass.dd.method | メソッドを使用したDDクラスの説明 | 1268 | 968 | 2432 | 1728 |
myclass.ddd.private.var | プライベート変数を持つDDDクラスの説明 | 1140 | 964 | 2000年 | 1760 |
myclass.dddd.public.var | パブリック変数を持つDDDDクラスの説明 | 1132 | 952 | 2000年 | 1760 |
myclass.ddddd.static.var | 静的変数を持つDDDDDクラスの説明 | 1124 | 952 | 2000年 | 1760 |
myclass.e.extended.destruct | クラスDを拡張するデストラクタを持つクラスEの説明 | 1528 | 1228 | 2888 | 2160 |
myclass.z.free_memory | リンクの削除$ e、$ f | -332 | -548 | -784 | -1024 |
zend.a.init.autoload | ZendFrameworkの自動ロードを初期化する | 127 596 | 16 196 | 276,440 | 28,992 |
zend.a.init.model | ベースのデフォルトアダプターの初期化 | 1 018 564 | 251 840 | 2 081 696 | 479,280 |
zend.extended.controller1 | Zend_Controller_Actionからのコントローラー定義。 途中で、標準のZendクラスをロードします | 378,464 | 66 804 | 809 608 | 120 864 |
zend.extended.controller2 | コントローラーの定義。 クラスZendはすでにロードされています。クラスの重量を確認します | 11 476 | 11 140 | 19 792 | 19 056 |
zend.extended.model1 | Zend_Db_Tableによるモデル定義。 途中で、標準のZendクラスの読み込みが行われます。 | 28,080 | 25,676 | 48,704 | 42 944 |
zend.extended.model2 | モデルの定義。 Zendクラスは既にロードされています。クラスの重量を確認してください | 28,080 | 25,704 | 48,672 | 42 960 |
また、xcacheでいくつかのテストを行い、APCとの2つの違いに気付きました。 まず、xcacheは(ほとんどの場合)メモリを10〜15%節約します。 次に、xcacheはキャッシュからファイルをすぐに返しますが、APCは繰り返しアクセスした後のみです。 役に立たないが、利点。
すぐに、ファイルの名前が変更されず、$ include_overheadが大きなエラーで計算されたため、結果のばらつきはアクセラレータなしでテストする場合よりもはるかに大きいことに注意してください。
ご覧のように、アクセラレータは定義のためにメモリを節約しますが、PHPがキャッシュから現在のセッションにいくつかのピースを転送するように見えるため、完全ではありません。
次に、抽象的なテストから実際のテストに移りましょう。
ZendFrameworkで小さなアプリケーションをテストする
テストのために、プログラマーの1人(
Simple-blog )のテストタスクを行いました。機能は、登録、承認、投稿のリストの読み取り、投稿のオープン、コメント付きの機能を持つ集合ブログサービスです。 index.phpの最後に次のように書かれています:
echo memory_get_peak_usage();
ページ生成中にスクリプトが消費したメモリ量を確認します。 結果:
ページの種類 | Ubuntu x86、 PHP 5.3.10、 ZF 1.11.11、 空のキャッシュ | Ubuntu x86、 PHP 5.3.10、 ZF 1.11.11、 リフレッシュ | Ubuntu x86-64、 PHP 5.3.10、 ZF 1.11.11、 空のキャッシュ | Ubuntu x86-64、 PHP 5.3.10、 ZF 1.11.11、 リフレッシュ |
---|
投稿リスト | 5 328 648 | 1 792 968 | 10 938 160 | 3 306 720 |
投稿とコメント | 5 372 356 | 1 831 452 | 11 015 320 | 3 373 528 |
ログインフォーム | 6 781 656 | 2,277,164 | 13 982 104 | 4 187 600 |
登録フォーム | 6 796 496 | 2,291,568 | 14 009 384 | 4 211 432 |
さらに、Gentooのアセンブリがテストされましたが、すべてのテストで25%の効果が得られました。
結論
- メモリが高価なリソース(VPSなど)であり、64ビットの数値が実際に必要でない場合は、32ビットバージョンのOSを使用するのが理にかなっています。 ゲインは約1.8倍になります。
- 現在のアーキテクチャ向けにパッケージが強化されたOSでは、25%の追加メモリを節約できます。
- 重いフレームワークのようなPHPのメモリを消費するものはありません。 アクセラレータを使用しても、重いフレームワークでメモリを消費することはありません。 おそらく、人気とパフォーマンスのバランスを自分で選択するために、次のPHPフレームワークの比較に慣れることが理にかなっています。
- APCキャッシュサイズが使い果たされた場合、注意を引くために写真に示されている状況を取得できます。 同じマシンに多くのサイトがあり、十分なメモリがあるかどうかを確認せずにAPCをインストールした場合、これを達成するのは難しくありません。 同時に、統計(apc.php)により、メモリの約40%がまだあることがわかりますが、APCのメモリマネージャが不良であり、メモリマネージャを効果的に使用する方法がわからないため、特に信じてはいけません。 ヒット値とミス値に常に注意を払うのが良いでしょう。
コジャラ
UPD
AntonShevchukは 、PHP 5.4のテスト結果を追加しました。 PHP 5.4は5.3よりもはるかに経済的に見えます。
公式文書でもこれが確認されています。