o que é mais rápido: in_array ou isset? [fechadas]

96

Esta pergunta é apenas para mim, pois sempre gosto de escrever código otimizado que pode ser executado também em servidores lentos e baratos (ou servidores com MUITO tráfego)

Olhei em volta e não consegui encontrar uma resposta. Eu queria saber o que é mais rápido entre esses dois exemplos, tendo em mente que as chaves do array no meu caso não são importantes (pseudo-código 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
    }
}
?>

Como o ponto da questão não é a colisão array, eu gostaria de acrescentar que, se você tem medo de colidir inserções para $a[$new_value], você pode usar $a[md5($new_value)]. ele ainda pode causar colisões, mas impediria um possível ataque DoS ao ler um arquivo fornecido pelo usuário ( http://nikic.github.com/2011/12/28/Supercolliding-a-PHP-array.html )

Fabrizio
fonte
3
Se você está sempre se esforçando para escrever código otimizado, certamente está usando um criador de perfil de vez em quando?
mario de
59
Eu voto para reabrir. A pergunta está bem formulada e as respostas são apoiadas por fatos e referências. Enquanto um micro -optimization, esses tipos de perguntas são construtivas .
Jason McCreary
5
@JasonMcCreary segundo; apenas mais um.
Ja͢ck de
7
Isso muitos anos depois, mas eu nem consideraria isso uma micro otimização. Para grandes conjuntos de dados, isso pode fazer muita diferença !!
Robert
2
... esta questão parece "construtiva" para mim. Vou começar outra campanha de reabertura.
mickmackusa

Respostas:

117

As respostas até agora são precisas. Usar issetneste caso é mais rápido porque

  • Ele usa uma pesquisa hash O (1) na chave, enquanto in_arraydeve verificar todos os valores até encontrar uma correspondência.
  • Por ser um opcode, ele tem menos sobrecarga do que chamar a in_arrayfunção integrada.

Isso pode ser demonstrado usando uma matriz com valores (10.000 no teste abaixo), forçando in_arraya fazer mais pesquisas.

isset:    0.009623
in_array: 1.738441

Isso se baseia no benchmark de Jason, preenchendo alguns valores aleatórios e, ocasionalmente, encontrando um valor que existe na matriz. Tudo aleatório, então tome cuidado, pois os tempos irão flutuar.

$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;
David Harkness
fonte
Eu sei sobre hashes, mas me perguntando por que algo semelhante não é feito em valores de arrays quando possível para acelerar funções, também reduzirá o consumo de memória se valores semelhantes forem usados ​​simplesmente adicionando um hashing extra no valor .. correto?
Fabrizio de
3
@Fabrizio - Os valores do array podem ser duplicados e conter objetos não hashable. As chaves devem ser exclusivas e podem ser apenas strings e inteiros, o que as torna facilmente hashble. Embora você possa criar um mapa um-para-um que faz hash de chaves e valores, não é assim que o array do PHP funciona.
David Harkness de
3
Caso você tenha certeza de que seu array contém valores únicos, existe outra opção - flip + isset .
Arkadij Kuzhel de
vale a pena notar que um isset invertido é ainda mais rápido neste exemplo do que in_array: `` `$ start = microtime (true); $ foo = array_flip ($ a); para ($ i = 0; $ i <10000; ++ $ i) {isset ($ foo [rand (1, 1000000)]); } $ total_time = microtime (true) - $ start; echo "Tempo total (isset invertido):", number_format ($ total_time, 6), PHP_EOL;
Andre Baumeier
@AndreBaumeier Qual é mais rápido dependerá do tamanho do array e de quantos testes você fará. Inverter uma matriz de dez mil elementos para realizar três testes provavelmente não é eficiente.
David Harkness
42

O que é mais rápido: isset()vsin_array()

isset() é mais rápido.

Embora deva ser óbvio, isset()testa apenas um único valor. Visto que in_array()irá iterar em todo o array, testando o valor de cada elemento.

O benchmarking aproximado é bastante fácil de usar microtime().

Resultados:

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

Nota: Os resultados foram semelhantes, independentemente se existiam ou não.

Código:

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

Recursos adicionais

Eu o encorajo a olhar também para:

Jason McCreary
fonte
Ótima solução. Estou surpreso que mais pessoas não dividam o tempo de suas funções / código mais usando microtime()ou outras ferramentas. Incrivelmente valioso.
nickhar de
1
Pesquisar uma matriz vazia para a mesma chave apenas destaca a sobrecarga de chamar a in_arrayfunção em comparação com o uso do issetembutido. Isso seria melhor com uma matriz contendo um monte de chaves aleatórias e ocasionalmente procurando por uma chave / valor existente.
David Harkness de
I fazer benchmarks de uso e microtime um pouco, mas eu também percebi, enquanto eu estava testando whilee foreachque em cada atualização que eu estava ficando diferentes "vencedores". sempre depende de muitas variáveis ​​de servidor, e o melhor é iterar um número muito grande de vezes em momentos diferentes e conseguir aquele que ganha com mais frequência, ou apenas saber o que está acontecendo em segundo plano e saber que será o vencedor final não importa o que
aconteça
@David Harkness, você já escolheu minuciosamente minha resposta. Se você quiser mais, fique em meus ombros e publique sua própria resposta. :) No entanto, se a sobrecarga da função já é significativamente mais cara em relação a isset(), o que o faz pensar que transmiti-la a um array maior a tornaria mais rápida ?
Jason McCreary
1
@Fabrizio - Leia sobre funções de hash e tabelas de hash .
David Harkness de
19

O uso isset()aproveita a pesquisa mais rápida porque usa uma tabela hash , evitando a necessidade de O(n)pesquisas.

A chave é hash primeiro usando a função djb hash para determinar o intervalo de chaves com hash semelhantes em O(1). O balde é então pesquisado iterativamente até que a chave exata seja encontrada em O(n).

Excluindo quaisquer colisões de hash intencionais , essa abordagem produz um desempenho muito melhor do que in_array().

Observe que, ao usar isset()da maneira que você mostrou, passar os valores finais para outra função requer o uso array_keys()para criar um novo array. Um compromisso de memória pode ser feito armazenando os dados nas chaves e valores.

Atualizar

Uma boa maneira de ver como suas decisões de design de código afetam o desempenho do tempo de execução, você pode verificar a versão compilada de seu 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

Não apenas in_array()usa uma O(n)pesquisa relativamente ineficiente , mas também precisa ser chamada como uma função ( DO_FCALL), enquanto isset()usa um único opcode ( ZEND_ISSET_ISEMPTY_DIM_OBJ) para isso.

Ja͢ck
fonte
7

O segundo seria mais rápido, pois está procurando apenas por aquela chave de array específica e não precisa iterar em todo o array até que seja encontrado (vai olhar para cada elemento do array se não for encontrado)

Mike Brant
fonte
mas também depende do paradeiro de uma var pesquisada no escopo global
el Dude
@ EL2002, você pode explicar melhor essa declaração?
Fabrizio de
1
Mike, não estaria olhando para todo o array mesmo com o isset()se ele não fosse encontrado?
Fabrizio de
1
@Fabrizio Não, não precisa iterar. Internamente (em C), o array PHP é apenas uma tabela hash. Para pesquisar um único valor de índice, C apenas cria um hash desse valor e procura sua localização atribuída na memória. Ou existe um valor ou não existe.
Mike Brant de
1
@Fabrizio Este artigo fornece uma boa visão geral de como os arrays são representados internamente em C pelo PHP. nikic.github.com/2012/03/28/…
Mike Brant