daha hızlı nedir: in_array veya isset? [kapalı]


99

Bu soru sadece benim için, çünkü her zaman ucuz yavaş sunucularda (veya ÇOK trafiği olan sunucularda) da çalışabilen optimize edilmiş kod yazmaktan hoşlanıyorum.

Etrafıma baktım ve bir cevap bulamadım. Benim durumumdaki dizinin anahtarlarının önemli olmadığını akılda tutarak bu iki örnek arasında neyin daha hızlı olduğunu merak ediyordum (doğal olarak sözde kod):

<?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
    }
}
?>

Sorunun noktası dizisi çarpışma olmadığı için, ben sizin için ekler çarpışan korkuyorsun eğer eklemek isterim $a[$new_value], sen kullanabilirsiniz $a[md5($new_value)]. yine de çarpışmalara neden olabilir, ancak kullanıcı tarafından sağlanan bir dosyadan okurken olası bir DoS saldırısından kurtulabilir ( http://nikic.github.com/2011/12/28/Supercolliding-a-PHP-array.html )


3
Her zaman optimize edilmiş kod yazmak için çabalıyorsanız, kesinlikle bir profil oluşturucu kullanıyor musunuz?
mario

61
Yeniden açılması için oy veriyorum. Soru iyi biçimlendirilmiş ve cevaplar gerçekler ve referanslarla desteklenmiştir. Mikro optimizasyon olsa da , bu tür sorular yapıcıdır .
Jason McCreary

5
@JasonMcCreary ikinci; sadece bir tane daha.
Ja͢ck

7
Bu yıllar sonra oldu, ancak bunu bir mikro optimizasyon olarak bile düşünmezdim. Büyük veri kümeleri için tonlarca fark yaratabilir !!
Robert

2
... bu soru bana "yapıcı" görünüyor. Başka bir yeniden açılış kampanyası başlatacağım.
mickmackusa

Yanıtlar:


120

Şimdiye kadarki cevaplar yerinde. issetBu durumda kullanmak daha hızlıdır çünkü

  • Anahtar üzerinde bir O (1) hash araması kullanır ancak in_arraybir eşleşme bulana kadar her değeri kontrol etmelidir.
  • Bir işlem kodu olarak, in_arrayyerleşik işlevi çağırmaktan daha az ek yüke sahiptir .

Bunlar, değerlere sahip bir dizi (aşağıdaki testte 10.000) kullanılarak in_arraydaha fazla arama yapmaya zorlanarak gösterilebilir .

isset:    0.009623
in_array: 1.738441

Bu, bazı rastgele değerleri doldurarak ve bazen dizide var olan bir değeri bularak Jason'ın kıyaslamasına dayanır. Hepsi rastgele, bu yüzden zamanların dalgalanacağına dikkat edin.

$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;

Karmaları biliyorum, ancak fonksiyonları hızlandırmak için mümkün olduğunda dizi değerlerinde neden benzer bir şey yapılmadığını merak ediyorum, eğer benzer değerler sadece değere fazladan bir karma ekleyerek kullanılırsa bellek tüketimini de azaltacaktır .. doğru mu?
Fabrizio

3
@Fabrizio - Dizi değerleri çoğaltılabilir ve hashable olmayan nesneler içerebilir. Anahtarlar benzersiz olmalıdır ve yalnızca dizeler ve tamsayılar olabilir, bu da onları kolayca karma hale getirebilir. Hem anahtarları hem de değerleri hash eden bire bir harita oluşturabilseniz de, PHP'nin dizisi böyle çalışmaz.
David Harkness

3
Dizinizin benzersiz değerler içerdiğinden eminseniz, başka bir seçenek daha vardır - flip + isset .
Arkadij Kuzhel

çevrilmiş bir isset'in bu örnekte in_array'den daha hızlı olduğuna dikkat etmek gerekir: `` $ start = microtime (true); $ foo = array_flip ($ a); için ($ i = 0; $ i <10000; ++ $ i) {isset ($ foo [rand (1, 1000000)]); } $ toplam_saat = mikrozaman (doğru) - $ başlangıç; echo "Toplam süre (ters çevrilen isset):", sayı_formatı ($ toplam_saat, 6), PHP_EOL;
Andre Baumeier

@AndreBaumeier Hangisinin daha hızlı olduğu, dizinin boyutuna ve kaç test yapacağınıza bağlı olacaktır. Üç testi gerçekleştirmek için on bin elemanlı bir diziyi çevirmek muhtemelen verimli değildir.
David Harkness

43

Hangisi daha hızlı: isset()vsin_array()

isset() daha hızlı.

Açık olması gerekirken, isset()yalnızca tek bir değeri test eder. Oysa in_array()her bir elemanın değerini test ederek tüm dizi üzerinde yineleme yapacaktır.

Kaba kıyaslamanın kullanımı oldukça kolaydır microtime().

Sonuçlar:

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

Not: Sonuçlar, var olup olmadığına bakılmaksızın benzerdi.

Kod:

<?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;

Ek kaynaklar

Şunlara da bakmanızı tavsiye ederim:


Güzel çözüm. Daha fazla insanın işlevlerini / kodlarını daha fazla kullanarak microtime()veya diğer araçları kullanarak ayırmamasına şaşırdım . İnanılmaz derecede değerli.
nickhar

1
Aynı anahtar için boş bir dizide arama yapmak in_array, issetyerleşik olanı kullanmak yerine yalnızca işlevi çağırmanın ek yükünü vurgular . Bu, bir grup rastgele anahtar içeren ve ara sıra var olan bir anahtar / değer arayan bir dizi ile daha iyi olur.
David Harkness

Ben ölçütleri ve mikro zamanı oldukça kullanıyorum, ancak test ederken whileve foreachher yenilemede farklı "kazananlar" elde ettiğimi de fark ettim . her zaman çok fazla sunucu değişkenine bağlıdır ve en iyisi, farklı zamanlarda çok sayıda kez yinelemek ve daha sık kazanan birini elde etmek veya sadece arka planda ne olduğunu bilmek ve bunun son kazanan olacağını bilmektir. ne olursa olsun
Fabrizio

@David Harkness, cevabımı çoktan seçmişsin. Daha fazlasını istiyorsanız, omuzlarımda durun ve kendi cevabınızı gönderin. :) Yine de, eğer fonksiyon ek yükü isset()ona göre çok daha pahalıysa, onu daha geniş bir diziye geçirmenin onu daha hızlı yapacağını düşündüren nedir?
Jason McCreary

1
@Fabrizio - Hashing fonksiyonları ve hash tablolarını okuyun .
David Harkness

19

Kullanım isset(), bir karma tablo kullandığından daha hızlı aramadan yararlanır ve arama ihtiyacını ortadan kaldırır O(n).

Anahtar, içindeki benzer şekilde karma hale getirilmiş anahtarların kümesini belirlemek için ilk olarak djb karma işlevi kullanılarak karma hale getirilirO(1) . Kova daha sonra tam anahtar bulunana kadar yinelemeli olarak aranır O(n).

Herhangi bir kasıtlı hash çarpışması dışında , bu yaklaşım çok daha iyi performans sağlar in_array().

Gösterdiğiniz şekilde kullanırken isset(), son değerleri başka bir işleve aktarmanın array_keys()yeni bir dizi oluşturmak için kullanılmasını gerektirdiğini unutmayın . Verilerin hem anahtarlarda hem de değerlerde saklanmasıyla bir bellek uzlaşması sağlanabilir.

Güncelleme

Kod tasarımı kararlarınızın çalışma zamanı performansını nasıl etkilediğini görmenin iyi bir yolu , betiğinizin derlenmiş sürümüne göz atabilirsiniz :

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

Sadece in_array()nispeten verimsiz bir O(n)arama kullanmakla kalmaz , aynı zamanda bir işlev ( DO_FCALL) olarak adlandırılması gerekir, bunun isset()için tek bir opcode ( ZEND_ISSET_ISEMPTY_DIM_OBJ) kullanır .


7

İkincisi, yalnızca belirli bir dizi anahtarını aradığından ve bulunana kadar tüm diziyi yinelemesine gerek olmadığından daha hızlı olacaktır (bulunamazsa her dizi öğesine bakacaktır)


ancak genel kapsamda aranan bir değişkenin nerede olduğuna da bağlıdır
el Dude

@ EL2002, bu ifadeyi biraz daha detaylandırır mısınız?
Fabrizio

1
Mike, bulunmasa bile dizinin tamamına bakmaz isset()mıydı?
Fabrizio

1
@Fabrizio Hayır, yinelemesine gerek yok. Dahili olarak (C'de) PHP dizisi sadece bir karma tablodur. Tek bir indeks değerini aramak için, C sadece bu değerin bir karmasını oluşturur ve bellekte atanmış konumunu arar. Orada bir değer var ya da yok.
Mike Brant

1
@Fabrizio Bu makale dizilerin dahili olarak PHP tarafından C'de nasıl temsil edildiğine dair iyi bir genel bakış sağlar. nikic.github.com/2012/03/28/…
Mike Brant
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.