105

この質問は、安価で低速なサーバー (または大量のトラフィックを持つサーバー) でも実行できる最適化されたコードを常に書きたいので、私だけに向けたものです。

私は周りを見回しましたが、答えを見つけることができませんでした。私の場合、配列のキーは重要ではないことを念頭に置いて、これら2つの例の間でどちらが速いのか疑問に思っていました(当然のことながら疑似コード):

<?php
$a = array();
while($new_val = 'get over 100k email addresses already lowercased'){
    if(!in_array($new_val, $a){
        $a[] = $new_val;
        //do other stuff
    }
}
?>

<?php
$a = array();
while($new_val = 'get over 100k email addresses already lowercased'){
    if(!isset($a[$new_val]){
        $a[$new_val] = true;
        //do other stuff
    }
}
?>

質問のポイントは配列の衝突ではないので、 の挿入の衝突が怖い場合は$a[$new_value]、 を使用できることを追加したいと思います$a[md5($new_value)]。それでも衝突が発生する可能性はありますが、ユーザーが提供したファイル ( http://nikic.github.com/2011/12/28/Supercolliding-a-PHP-array.html )から読み取るときに、DoS 攻撃の可能性を回避できます。

4

4 に答える 4

128

これまでの答えは的を射ています。この場合に使用issetする方が高速です。

  • キーに対して O(1) ハッシュ検索を使用しますin_arrayが、一致が見つかるまですべての値をチェックする必要があります。
  • オペコードであるため、in_array組み込み関数を呼び出すよりもオーバーヘッドが少なくなります。

これらは、値を持つ配列 (以下のテストでは 10,000) を使用して実証でき、in_arrayさらに検索を行う必要があります。

isset:    0.009623
in_array: 1.738441

これは、いくつかのランダムな値を入力し、配列に存在する値を時折見つけることによって、Jason のベンチマークに基づいています。すべてランダムなので、時間が変動することに注意してください。

$a = array();
for ($i = 0; $i < 10000; ++$i) {
    $v = rand(1, 1000000);
    $a[$v] = $v;
}
echo "Size: ", count($a), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    isset($a[rand(1, 1000000)]);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    in_array(rand(1, 1000000), $a);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;
于 2012-11-20T22:48:14.670 に答える
46

どちらが速いですか: isset()vsin_array()

isset()より高速です。

明らかなはずですがisset()、単一の値のみをテストします。一方in_array()、各要素の値をテストしながら、配列全体を反復処理します。

を使用すると、大まかなベンチマークは非常に簡単microtime()です。

結果:

Total time isset():    0.002857
Total time in_array(): 0.017103

注:存在するかどうかに関係なく、結果は同様でした。

コード:

<?php
$a = array();
$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    isset($a['key']);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

$start = microtime( true );

for ($i = 0; $i < 10000; ++$i) {
    in_array('key', $a);
}

$total_time = microtime( true ) - $start;
echo "Total time: ", number_format($total_time, 6), PHP_EOL;

exit;

その他のリソース

以下もご覧になることをお勧めします。

于 2012-11-20T22:27:34.760 に答える
19

を使用すると、ハッシュテーブルを使用し、検索の必要がなくなるため、isset()より高速なルックアップを利用できます。O(n)

キーは最初にdjbハッシュ関数を使用してハッシュされ、で同様にハッシュされたキーのバケットを決定しますO(1)。次に、正確なキーがで見つかるまで、バケットが繰り返し検索されO(n)ます。

意図的なハッシュ衝突を除けば、このアプローチはよりもはるかに優れたパフォーマンスをもたらしますin_array()

isset()示した方法で使用する場合、最終値を別の関数に渡すには、を使用array_keys()して新しい配列を作成する必要があることに注意してください。キーと値の両方にデータを格納することにより、メモリの妥協を行うことができます。

アップデート

コード設計の決定がランタイムパフォーマンスにどのように影響するかを確認する良い方法は、スクリプトのコンパイル済みバージョンを確認することです。

echo isset($arr[123])

compiled vars:  !0 = $arr
line     # *  op                           fetch      ext  return  operands
-----------------------------------------------------------------------------
   1     0  >   ZEND_ISSET_ISEMPTY_DIM_OBJ              2000000  ~0      !0, 123
         1      ECHO                                                 ~0
         2    > RETURN                                               null

echo in_array(123, $arr)

compiled vars:  !0 = $arr
line     # *  op                           fetch      ext  return  operands
-----------------------------------------------------------------------------
   1     0  >   SEND_VAL                                             123
         1      SEND_VAR                                             !0
         2      DO_FCALL                                 2  $0      'in_array'
         3      ECHO                                                 $0
         4    > RETURN                                               null

in_array()比較的非効率的な検索を使用するだけでなく、関数( )O(n)として呼び出す必要がありますが、これには単一のオペコード()を使用します。DO_FCALLisset()ZEND_ISSET_ISEMPTY_DIM_OBJ

于 2012-11-20T23:09:04.820 に答える
7

2番目は、その特定の配列キーのみを探しており、それが見つかるまで配列全体を反復する必要がないため、より高速です(見つからない場合はすべての配列要素を調べます)

于 2012-11-20T22:22:45.487 に答える