web-dev-qa-db-ja.com

速いのは何ですか:in_arrayまたはisset?

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

私は周りを見回しましたが、答えが見つかりませんでした。私の場合の配列のキーは重要ではないことを念頭に置いて、これらの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)]を使用できることを付け加えます。依然として衝突を引き起こす可能性がありますが、ユーザーが提供したファイルから読み取るときに起こりうるDoS攻撃を排除します( http://nikic.github.com/2011/12/28/Supercolliding-a-PHP-array .html

88
Fabrizio

これまでの答えはスポットオンです。この場合、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;
108
David Harkness

どちらが速いですか:isset() vs in_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;

追加のリソース

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

39
Jason McCreary

isset()を使用すると、 ハッシュテーブル が使用され、O(n)検索が不要になるため、より高速なルックアップが利用されます。

最初に djb hash function を使用してキーがハッシュされ、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_FCALL_)として呼び出す必要もありますが、isset()は単一のこのためのオペコード(_ZEND_ISSET_ISEMPTY_DIM_OBJ_)。

18
Ja͢ck

2番目の方法は、特定の配列キーのみを検索し、検出されるまで配列全体を反復処理する必要がないため、より高速になります(検出されない場合はすべての配列要素を参照します)。

7
Mike Brant