cosa è più veloce: in_array o isset? [chiuso]


96

Questa domanda è solo per me poiché mi piace sempre scrivere codice ottimizzato che possa essere eseguito anche su server lenti economici (o server con MOLTO traffico)

Mi sono guardato intorno e non sono riuscito a trovare una risposta. Mi chiedevo cosa sia più veloce tra questi due esempi tenendo presente che le chiavi dell'array nel mio caso non sono importanti (pseudo-codice naturalmente):

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

Poiché il punto della domanda non è la collisione dell'array, vorrei aggiungere che se hai paura di collidere inserti per $a[$new_value], puoi usare $a[md5($new_value)]. può ancora causare collisioni, ma eliminerebbe un possibile attacco DoS durante la lettura da un file fornito dall'utente ( http://nikic.github.com/2011/12/28/Supercolliding-a-PHP-array.html )


3
Se ti sforzi sempre di scrivere codice ottimizzato, stai sicuramente usando un profiler di tanto in tanto?
mario

60
Voto per riaprire. La domanda è ben formata e le risposte sono supportate da fatti e riferimenti. Mentre un micro -ottimizzazione, questi tipi di domande sono costruttivo .
Jason McCreary

5
@JasonMcCreary secondo; solo un altro.
Ja͢ck

7
Sono passati molti anni, ma non la considererei nemmeno una micro ottimizzazione. Per set di dati di grandi dimensioni può fare una grande differenza !!
Robert,

2
... questa domanda mi sembra "costruttiva". Inizierò un'altra campagna di riapertura.
mickmackusa

Risposte:


117

Le risposte finora sono esatte. L'utilizzo issetin questo caso è più veloce perché

  • Utilizza una ricerca hash O (1) sulla chiave mentre in_arraydeve controllare ogni valore finché non trova una corrispondenza.
  • Essendo un codice operativo, ha meno overhead rispetto alla chiamata della in_arrayfunzione incorporata.

Questi possono essere dimostrati utilizzando un array con valori (10.000 nel test seguente), costringendo in_arraya fare più ricerche.

isset:    0.009623
in_array: 1.738441

Questo si basa sul benchmark di Jason inserendo alcuni valori casuali e trovando occasionalmente un valore presente nell'array. Tutto casuale, quindi fai attenzione che i tempi fluttueranno.

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

Conosco gli hash, ma chiedendomi perché qualcosa di simile non viene fatto sui valori degli array quando possibile per accelerare le funzioni, ridurrà anche il consumo di memoria se vengono utilizzati valori simili semplicemente aggiungendo un hashing extra al valore .. corretto?
Fabrizio

3
@Fabrizio - I valori degli array possono essere duplicati e contenere oggetti non hashable. Le chiavi devono essere univoche e possono essere solo stringhe e numeri interi, il che le rende facilmente modificabili. Sebbene tu possa creare una mappa uno-a-uno che hash sia chiavi che valori, non è così che funziona l'array di PHP.
David Harkness,

3
Se sei sicuro che l'array contenga valori univoci, c'è un'altra opzione: flip + isset .
Arkadij Kuzhel

degno di nota un isset capovolto è ancora più veloce in questo esempio che in_array: `` `$ start = microtime (true); $ foo = array_flip ($ a); for ($ i = 0; $ i <10000; ++ $ i) {isset ($ foo [rand (1, 1000000)]); } $ total_time = microtime (true) - $ start; echo "Tempo totale (set ribaltato):", number_format ($ total_time, 6), PHP_EOL;
Andre Baumeier

@AndreBaumeier Quale è più veloce dipenderà dalla dimensione dell'array e dal numero di test che farai. Capovolgere un array di diecimila elementi per eseguire tre test probabilmente non è efficiente.
David Harkness,

42

Che è più veloce: isset()vsin_array()

isset() è più veloce.

Anche se dovrebbe essere ovvio, isset()verifica solo un singolo valore. Mentre in_array()itererà sull'intero array, testando il valore di ogni elemento.

Il benchmarking approssimativo è abbastanza facile da usare microtime().

Risultati:

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

Nota: i risultati erano simili indipendentemente dal fatto che esistessero o meno.

Codice:

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

Risorse addizionali

Ti incoraggio a guardare anche:


Bella soluzione. Sono sorpreso che più persone non dividano il tempo delle loro funzioni / codice utilizzando di più microtime()o altri strumenti. Incredibilmente prezioso.
nickhar

1
La ricerca di una matrice vuota per la stessa chiave evidenzia solo l'overhead della chiamata della in_arrayfunzione rispetto all'utilizzo dell'integrato isset. Sarebbe meglio con un array contenente un mucchio di chiavi casuali e occasionalmente cercando una chiave / valore esistente.
David Harkness

Io faccio uso di parametri di riferimento e microtime un bel po ', ma ho anche capito, mentre stavo testando whilee foreachche ad ogni aggiornamento mi è stato sempre diverse "vincitori". dipende sempre da troppe variabili del server e la cosa migliore è iterare un numero molto elevato di volte in tempi diversi e ottenere quella che vince più spesso, o semplicemente sapere cosa sta succedendo in background e sapere che sarà il vincitore finale non importa cosa
Fabrizio

@David Harkness, hai già scelto la mia risposta. Se vuoi di più, mettiti sulle mie spalle e posta la tua risposta. :) Tuttavia, se l'overhead della funzione è già significativamente più costoso rispetto a isset(), cosa ti fa pensare che passare un array più grande lo renderebbe più veloce ?
Jason McCreary

1
@Fabrizio - Leggi le informazioni sulle funzioni di hashing e sulle tabelle hash .
David Harkness

19

L'utilizzo isset()sfrutta una ricerca più rapida perché utilizza una tabella hash , evitando la necessità di O(n)ricerche.

La chiave viene prima sottoposta ad hashing utilizzando la funzione hash djb per determinare il bucket di chiavi con hash in modo simile O(1). Il bucket viene quindi cercato in modo iterativo fino a trovare la chiave esatta in O(n).

Escludendo eventuali collisioni di hash intenzionali , questo approccio offre prestazioni molto migliori rispetto a in_array().

Si noti che quando si utilizza isset()nel modo mostrato, il passaggio dei valori finali a un'altra funzione richiede l'utilizzo array_keys()per creare un nuovo array. È possibile compromettere la memoria archiviando i dati sia nelle chiavi che nei valori.

Aggiornare

Un buon modo per vedere come le decisioni di progettazione del codice influenzano le prestazioni di runtime, puoi controllare la versione compilata del tuo script:

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

Non solo in_array()utilizza una O(n)ricerca relativamente inefficiente , ma deve anche essere chiamata come funzione ( DO_FCALL) mentre isset()utilizza un singolo codice operativo ( ZEND_ISSET_ISEMPTY_DIM_OBJ) per questo.


7

Il secondo sarebbe più veloce, poiché sta cercando solo quella specifica chiave dell'array e non ha bisogno di iterare sull'intero array finché non viene trovato (esaminerà ogni elemento dell'array se non viene trovato)


ma dipende anche da dove si trova una variabile ricercata in ambito globale
el Dude,

@ EL2002, puoi approfondire questa affermazione?
Fabrizio

1
Mike, non starebbe guardando l'intero array anche con il isset()se non viene trovato?
Fabrizio

1
@Fabrizio No, non è necessario iterare. Internamente (in C) l'array PHP è solo una tabella hash. Per cercare un singolo valore di indice, C crea semplicemente un hash di quel valore e cerca la posizione assegnata in memoria. C'è un valore lì o non c'è.
Mike Brant

1
@Fabrizio Questo articolo fornisce una buona panoramica di come gli array sono rappresentati internamente in C da PHP. nikic.github.com/2012/03/28/…
Mike Brant
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.