PHP のパフォーマンスは常に向上しています。ただし、不適切に使用したり、注意を怠ったりすると、PHP の内部実装の落とし穴に陥る可能性があります。数日前にパフォーマンスの問題に遭遇しました
PHP のパフォーマンスは向上しています。ただし、不適切に使用したり、注意を怠ったりすると、PHP の内部実装の落とし穴に陥る可能性があります。数日前にパフォーマンスの問題が発生しました。
これが起こりました。私たちのインターフェイスの 1 つが戻るのに毎回 5 秒かかったと同僚が報告しました。私たちは一緒にコードを確認し、読み取りキャッシュがループ内で呼び出されていることに「驚きました」。動作しましたが、キャッシュされたキーは変更されていないため、このコードをループの外に移動して、再度テストすると、インターフェイスの戻り時間が 2 秒に短縮されました。倍増しましたが、明らかに受け入れられる結果ではありません。
パフォーマンスの問題を引き起こしたコードの量はそれほど多くなかったので、IO の問題を解決した後、テスト コードを作成しましたが、案の定、すぐに問題が再発しました。
次のようにコードをコピーします。
$y="1800";
$x = array();
for($j=0;$j<2000;$j++){
$x[]= "{$j}";
}
for($i=0;$i<3000;$i++){
if(in_array($y,$x)){
続ける; }
}
?>
shell$ time /usr/local/php/bin/php test.php
real 0m1.132s
user 0m1.118s
sys 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 つの文字数値列を Long 整数型に変換して再度比較しますが、これでパフォーマンスが消費されるかどうかはわかりません。
問題の核心はわかったので、解決策はたくさんあります。最も簡単な方法は、in_array に 3 番目のパラメータを追加して true にすることです。これは、厳密な比較となり、PHP の巧妙な型の比較も回避します。コードは次のとおりです:
$y="1800";
$x = array();
for($ j=0; $j<2000;$j++){
$x[]= "{$j}";
for($i=0;$i<3000;$i++){
$x、 true)){ 267s
user 0m0.247s
sys 0m0.020s
再度実行します
コードをコピーします コードは次のとおりです:
以下に示すように:
__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 で定義されています。一連の判断と変換の後、232 行目で strtol が呼び出されます。これは、記事で説明した文字列を長い文字列に変換します。整数、写真と事実付き