PHP のパフォーマンスは常に向上しています。ただし、不適切に使用したり、注意を怠ったりすると、PHP の内部実装の落とし穴に陥る可能性があります。数日前にパフォーマンスの問題が発生しました
PHP のパフォーマンスは向上しています。ただし、不適切に使用したり、注意を怠ったりすると、PHP の内部実装の落とし穴に陥る可能性があります。数日前にパフォーマンスの問題が発生しました。
問題は次のようなものです。私たちのインターフェイスの 1 つが戻るのに毎回 5 秒かかると同僚が報告しました。私たちは一緒にコードを確認しましたが、実際には読み取りキャッシュがループ内で呼び出されていることに「驚きました」。 900 回) の操作を実行しましたが、キャッシュされたキーは変更されていないため、このコードをループの外に移動して再度テストすると、インターフェイスの戻り時間が 2 秒に短縮されました。倍増しましたが、明らかに受け入れられる結果ではありません。
パフォーマンスの問題を引き起こしたコードの量はそれほど多くなかったので、IO の問題を解決した後、テスト コードを作成しましたが、案の定、すぐに問題が再発しました。
コードをコピーします。 如 コードは次のとおりです。
<?php $y="1800"; $x = array(); for($j=0;$j<2000;$j++){ $x[]= "{$j}"; } for($i=0;$i<3000;$i++){ if(in_array($y,$x)){ continue; } } ?>
shell $ Time/USR/LOCAL/PHP/BIN/PHP TEST.PHP Rereal 0m1.132s
User 0M1.118sSys 0m0.015s
はい、文字列番号を使用します。キャッシュから取り出すと次のようになります。そこで、ここでは特別に文字列に変換します(直接数値であればこの問題は発生しません。自分で確認できます)。消費される時間は 1 秒で、これはわずか 3000 サイクルであることがわかります。また、その後のシステム時間も strace を使用しても有効な情報が得られないことがわかります。
shell$ strace -ttt -o xxx /usr/local/php/bin/php test.php
shell$less xxx
これら 2 つのシステムコール間の遅延が非常に大きいことだけがわかります。何をしたのか分かりませんか?幸いなことに、Linux のデバッグ ツールには strace に加えて ltrace も含まれています (もちろん、dtrace や ptrace もありますが、これらはこの記事の範囲外なので省略します)。
引用: strace はプロセスのシステムコールまたはシグナル生成を追跡するために使用され、ltrace はライブラリ関数を呼び出すプロセスを追跡するために使用されます (IBM 開発者ワークス経由)。
干渉要因を排除するために、結果に影響を与える過剰な malloc 呼び出しを避けるために、$x を array("0","1","2",…) の形式に直接割り当てます。
shell$ ltrace -c /usr/local/php/bin/php test.php を実行します
図 2 に示すように
ライブラリ関数 __strtol_internal が非常に頻繁に呼び出されており、94% に達していることがわかります。あまりにも大げさなので、このライブラリ関数 __strtol_internal が何をしているのか調べてみると、これは strtol のエイリアスであることがわかり、これは PHP エンジンがそれを検出したと推測できます。この変換プロセスは時間がかかりすぎるため、再度実行します:
コードをコピーします
コードは次のとおりです:
shell$ ltrace -e "__strtol_internal" /usr/local/php/bin/php test.php
は、下の図のように大量の呼び出しを簡単にキャッチできます。 この時点で、in_array の緩やかな比較が見つかります。 2つの文字列を長整数型に変換してから比較しますが、これでパフォーマンスが消費されるかどうかはわかりません。
問題の核心はわかったので、解決策はたくさんあります。最も簡単な方法は、in_array の 3 番目のパラメーターを true に追加することです。これは、厳密な比較となり、型が同時に比較されることを意味します。これにより、PHP が賢くなりすぎてしまうことがなくなり、コードは次のようになります:
コードをコピーします:
<?php $y="1800"; $x = array(); for($j=0;$j<2000;$j++){ $x[]= "{$j}"; } for($i=0;$i<3000;$i++){ if(in_array($y,$x,true)){ continue; } } ?>
コードは次のとおりです。以下:
shell$ time /usr/local/php/bin/ php test.php
real 0m0.267suser 0m0.247s sys 0m0.020s
何倍も速くなりました! ! ! sys にかかる時間はほとんど変わっていないことがわかります。もう一度 ltrace してみましょう。malloc 呼び出しの干渉を排除するために $x を直接割り当てる必要があります。実際のアプリケーションではキャッシュから一度に取得するため、サンプル コードのような適用するループはありません。思い出のために。
再度実行します
コードをコピーします
コードは次のとおりです:
shell$ ltrace -c /usr/local/php/bin/php test.php
以下に示すように:
__ctype_to lower_loc が最も時間を要します。ライブラリ関数 __ctype_to lower_loc の機能を確認しました。簡単に理解すると、文字列を小文字に変換することになります。つまり、in_array の比較文字列では大文字と小文字が区別されないということですか?実際、この関数呼び出しは in_array とはほとんど関係がありません。in_array の実装については、PHP のソース コードを見たほうがよいでしょう。これ以上は説明できません。私とのコミュニケーションを歓迎します。間違ったことを書いた場合は修正してください。
——————2013.08.29 区切り線————————
夕方、再度PHP 5.4.10のソースコードを読みました。in_arrayにとても興味があります、ははは。 /ext/standard/array.c の 1248 行目で、php_search_array 関数を呼び出していることがわかりますが、以下の array_serach もこれを調整していますが、最後のパラメータが異なります。いくつかの追跡の後、in_array の緩い比較の場合、彼は最終的に ./Zend/zend_operators.c にある関数 zendi_smart_strcmp (実際には「スマート」関数) を比較のために呼び出しました。 ltrace を使用して、大量のキャプチャされたデータを変換しました。整数への変換は is_numeric_string_ex の動作です。
関数 is_numeric_string_ex は ./Zend/zend_operators.h で定義されています。一連の判断と変換の後、strtol が 232 行目で呼び出され、文字列を次のように変換します。長整数、写真と真実があります
関連する推奨事項:
MYSQLに中国語を挿入してPHPで表示すると、またしても文字化けに遭遇しました
以上がPHP の in_array でパフォーマンスの低下の問題が発生するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。