Hash mais rápido para usos não criptográficos?

154

Estou essencialmente preparando frases para serem colocadas no banco de dados, pois elas podem estar malformadas, então, em vez disso, quero armazenar um pequeno hash (simplesmente compararei se elas existem ou não, para que o hash seja ideal).

Eu suponho que o MD5 seja bastante lento em mais de 100.000 solicitações, então eu queria saber qual seria o melhor método para fazer o hash das frases, talvez lançar minha própria função de hash ou usar hash('md4', '...'seria mais rápido no final?

Eu sei que o MySQL tem MD5 (), então isso complementaria um pouco de velocidade no final da consulta, mas talvez haja mais uma função de hash mais rápida no MySQL que eu não conheço que funcionaria com PHP.

John
fonte
6
O que está impedindo você de comparar os hashes?
NullUserException
3
NullUserException: Você está certo, vou experimentá-los com frases de tamanho aleatório. Só queria uma visão sobre qual seria a norma, se houver, para lidar com esse tipo de coisa.
John
5
MD5 não é realmente lento ...
Âmbar
25
tem certeza de que a função de hash é um gargalo de todo o aplicativo? Duvido que sim
Seu senso comum
4
Esta é uma pergunta muito boa a ser feita, e os comentários que implicam que não são ou não são importantes e / ou devem ser óbvios e / ou intuitivos - são decepcionantes e frustrantes. (E também não é de todo inesperado.)
michael

Respostas:

56

O CRC32 é bem rápido e existe uma função para ele: http://www.php.net/manual/en/function.crc32.php

Mas você deve estar ciente de que o CRC32 terá mais colisões que os hashes MD5 ou mesmo SHA-1, simplesmente por causa do comprimento reduzido (32 bits em comparação com 128 bits, respectivamente, 160 bits). Mas se você quiser apenas verificar se uma sequência armazenada está corrompida, você ficará bem com o CRC32.

joschi
fonte
1
Uau, apenas o tipo de dados necessário é um número inteiro não assinado, isso será SIGNIFICAMENTE mais rápido que outro hash.
John
2
@ John: ou não. O CRC32 acaba sendo mais lento que o MD4, e não muito mais rápido que o MD5, nos processadores ARM. Além disso, CRC32 usa um tipo inteiro de 32 bits sem sinal, que é exatamente tudo o que MD5 precisa ...
Thomas Pornin
3
se você tem o benefício / luxo de uma CPU Intel mais recente, há um comando de montagem crc32c que é ... provavelmente muito rápido (embora não seja o valor tradicional de crc32). Veja também xxhash code.google.com/p/xxhash
rogerdpack
146
fcn     time  generated hash
crc32:  0.03163  798740135
md5:    0.0731   0dbab6d0c841278d33be207f14eeab8b
sha1:   0.07331  417a9e5c9ac7c52e32727cfd25da99eca9339a80
xor:    0.65218  119
xor2:   0.29301  134217728
add:    0.57841  1105

E o código usado para gerar isso é:

 $loops = 100000;
 $str = "ana are mere";

 echo "<pre>";

 $tss = microtime(true);
 for($i=0; $i<$loops; $i++){
  $x = crc32($str);
 }
 $tse = microtime(true);
 echo "\ncrc32: \t" . round($tse-$tss, 5) . " \t" . $x;

 $tss = microtime(true);
 for($i=0; $i<$loops; $i++){
  $x = md5($str);
 }
 $tse = microtime(true);
 echo "\nmd5: \t".round($tse-$tss, 5) . " \t" . $x;

 $tss = microtime(true);
 for($i=0; $i<$loops; $i++){
  $x = sha1($str);
 }
 $tse = microtime(true);
 echo "\nsha1: \t".round($tse-$tss, 5) . " \t" . $x;

 $tss = microtime(true);
 for($i=0; $i<$loops; $i++){
  $l = strlen($str);
  $x = 0x77;
  for($j=0;$j<$l;$j++){
   $x = $x xor ord($str[$j]);
  }
 }
 $tse = microtime(true);
 echo "\nxor: \t".round($tse-$tss, 5) . " \t" . $x;

 $tss = microtime(true);
 for($i=0; $i<$loops; $i++){
  $l = strlen($str);
  $x = 0x08;
  for($j=0;$j<$l;$j++){
   $x = ($x<<2) xor $str[$j];
  }
 }
 $tse = microtime(true);
 echo "\nxor2: \t".round($tse-$tss, 5) . " \t" . $x;

 $tss = microtime(true);
 for($i=0; $i<$loops; $i++){
  $l = strlen($str);
  $x = 0;
  for($j=0;$j<$l;$j++){
   $x = $x + ord($str[$j]);
  }
 }
 $tse = microtime(true);
 echo "\nadd: \t".round($tse-$tss, 5) . " \t" . $x;
Quamis
fonte
3
Ah, obrigado por esse insight, na verdade, apenas fortalece meu uso do CRC32 por ser mais rápido.
John
@ John - Você pode recuperar os algoritmos de hash usando: hash_algos(). O seguinte código de benchmarking de hash estava nos comentários do PHP ==> codepad.viper-7.com/5Wdhw6
Peter Ajtai
Obrigado pelo seu código. Eu melhorei um pouco. Eu não acho que devemos comparar funções como md5 () que processam toda a string e loops que executam byte a byte como você fez com o xor. No PHP, esses loops são muito lentos e ainda mais lentos que o próprio md5. Devemos comparar um hases com outro, todos implementados como funções.
precisa saber é o seguinte
1
Apenas uma observação rápida - tentei isso com uma string muito mais longa (~ 5000 caracteres) e o CRC32 foi mais lento que o MD5 e SHA1 na minha máquina (i7-6650U, 16GB). CRC32 - 1,7s, MD5 - 1,4s, SHA1 - 1,5s. Sempre teste por si mesmo.
Sam Tolton 24/10
4
@Quamis, o teste é bom, mas pode ser enganoso - como @samTolton observou que os resultados são diferentes e md5são mais rápidos. Um teste melhor será aleatorizar também o conteúdo e o comprimento das strings. Dessa forma, temos uma idéia melhor sobre o desempenho real do mundo real. Isso também evitará o armazenamento em cache. Dê uma olhada: php hashing checksum performance #
Shlomi Hassid 04/04
43

Lista classificada em que cada loop compartilha a mesma coisa para criptografar que todos os outros.

<?php

set_time_limit(720);

$begin = startTime();
$scores = array();


foreach(hash_algos() as $algo) {
    $scores[$algo] = 0;
}

for($i=0;$i<10000;$i++) {
    $number = rand()*100000000000000;
    $string = randomString(500);

    foreach(hash_algos() as $algo) {
        $start = startTime();

        hash($algo, $number); //Number
        hash($algo, $string); //String

        $end = endTime($start);

        $scores[$algo] += $end;
    }   
}


asort($scores);

$i=1;
foreach($scores as $alg => $time) {
    print $i.' - '.$alg.' '.$time.'<br />';
    $i++;
}

echo "Entire page took ".endTime($begin).' seconds<br />';

echo "<br /><br /><h2>Hashes Compared</h2>";

foreach($scores as $alg => $time) {
    print $i.' - '.$alg.' '.hash($alg,$string).'<br />';
    $i++;
}

function startTime() {
   $mtime = microtime(); 
   $mtime = explode(" ",$mtime); 
   $mtime = $mtime[1] + $mtime[0]; 
   return $mtime;   
}

function endTime($starttime) {
   $mtime = microtime(); 
   $mtime = explode(" ",$mtime); 
   $mtime = $mtime[1] + $mtime[0]; 
   $endtime = $mtime; 
   return $totaltime = ($endtime - $starttime); 
}

function randomString($length) {
    $characters = '0123456789abcdefghijklmnopqrstuvwxyz';
    $string = '';    
    for ($p = 0; $p < $length; $p++) {
        $string .= $characters[mt_rand(0, strlen($characters) - 1)];
    }
    return $string;
}

?>

E a saída

1 - crc32b 0.111036300659
2 - crc32 0.112048864365
3 - md4 0.120795726776
4 - md5 0.138875722885
5 - sha1 0.146368741989
6 - adler32 0.15501332283
7 - tiger192,3 0.177447080612
8 - tiger160,3 0.179498195648
9 - tiger128,3 0.184012889862
10 - ripemd128 0.184052705765
11 - ripemd256 0.185411214828
12 - salsa20 0.198500156403
13 - salsa10 0.204956293106
14 - haval160,3 0.206098556519
15 - haval256,3 0.206891775131
16 - haval224,3 0.206954240799
17 - ripemd160 0.207638263702
18 - tiger192,4 0.208125829697
19 - tiger160,4 0.208438634872
20 - tiger128,4 0.209359407425
21 - haval128,3 0.210256814957
22 - sha256 0.212738037109
23 - ripemd320 0.215386390686
24 - haval192,3 0.215610980988
25 - sha224 0.218329429626
26 - haval192,4 0.256464719772
27 - haval160,4 0.256565093994
28 - haval128,4 0.257113456726
29 - haval224,4 0.258928537369
30 - haval256,4 0.259262084961
31 - haval192,5 0.288433790207
32 - haval160,5 0.290239810944
33 - haval256,5 0.291721343994
34 - haval224,5 0.294484138489
35 - haval128,5 0.300224781036
36 - sha384 0.352449893951
37 - sha512 0.354603528976
38 - gost 0.392376661301
39 - whirlpool 0.629067659378
40 - snefru256 0.829529047012
41 - snefru 0.833986997604
42 - md2 1.80192279816
Entire page took 22.755341053 seconds


Hashes Compared

1 - crc32b 761331d7
2 - crc32 7e8c6d34
3 - md4 1bc8785de173e77ef28a24bd525beb68
4 - md5 9f9cfa3b5b339773b8d6dd77bbe931dd
5 - sha1 ca2bd798e47eab85655f0ce03fa46b2e6e20a31f
6 - adler32 f5f2aefc
7 - tiger192,3 d11b7615af06779259b29446948389c31d896dee25edfc50
8 - tiger160,3 d11b7615af06779259b29446948389c31d896dee
9 - tiger128,3 d11b7615af06779259b29446948389c3
10 - ripemd128 5f221a4574a072bc71518d150ae907c8
11 - ripemd256 bc89cd79f4e70b73fbb4faaf47a3caf263baa07e72dd435a0f62afe840f5c71c
12 - salsa20 91d9b963e172988a8fc2c5ff1a8d67073b2c5a09573cb03e901615dc1ea5162640f607e0d7134c981eedb761934cd8200fe90642a4608eacb82143e6e7b822c4
13 - salsa10 320b8cb8498d590ca2ec552008f1e55486116257a1e933d10d35c85a967f4a89c52158f755f775cd0b147ec64cde8934bae1e13bea81b8a4a55ac2c08efff4ce
14 - haval160,3 27ad6dd290161b883e614015b574b109233c7c0e
15 - haval256,3 03706dd2be7b1888bf9f3b151145b009859a720e3fe921a575e11be801c54c9a
16 - haval224,3 16706dd2c77b1888c29f3b151745b009879a720e4fe921a576e11be8
17 - ripemd160 f419c7c997a10aaf2d83a5fa03c58350d9f9d2e4
18 - tiger192,4 112f486d3a9000f822c050a204d284d52473f267b1247dbd
19 - tiger160,4 112f486d3a9000f822c050a204d284d52473f267
20 - tiger128,4 112f486d3a9000f822c050a204d284d5
21 - haval128,3 9d9155d430218e4dcdde1c62962ecca3
22 - sha256 6027f87b4dd4c732758aa52049257f9e9db7244f78c132d36d47f9033b5c3b09
23 - ripemd320 9ac00db553b51662826267daced37abfccca6433844f67d8f8cfd243cf78bbbf86839daf0961b61d
24 - haval192,3 7d706dd2d37c1888eaa53b154948b009e09c720effed21a5
25 - sha224 b6395266d8c7e40edde77969359e6a5d725f322e2ea4bd73d3d25768
26 - haval192,4 d87cd76e4c8006d401d7068dce5dec3d02dfa037d196ea14
27 - haval160,4 f2ddd76e156d0cd40eec0b8d09c8f23d0f47a437
28 - haval128,4 f066e6312b91e7ef69f26b2adbeba875
29 - haval224,4 1b7cd76ea97c06d439d6068d7d56ec3d73dba0373895ea14e465bc0e
30 - haval256,4 157cd76e8b7c06d432d6068d7556ec3d66dba0371c95ea14e165bc0ec31b9d37
31 - haval192,5 05f9ea219ae1b98ba33bac6b37ccfe2f248511046c80c2f0
32 - haval160,5 e054ec218637bc8b4bf1b26b2fb40230e0161904
33 - haval256,5 48f6ea210ee1b98be835ac6b7dc4fe2f39841104a37cc2f06ceb2bf58ab4fe78
34 - haval224,5 57f6ea2111e1b98bf735ac6b92c4fe2f43841104ab7cc2f076eb2bf5
35 - haval128,5 ccb8e0ac1fd12640ecd8976ab6402aa8
36 - sha384 bcf0eeaa1479bf6bef7ece0f5d7111c3aeee177aa7990926c633891464534cd8a6c69d905c36e882b3350ef40816ed02
37 - sha512 8def9a1e6e31423ef73c94251d7553f6fe3ed262c44e852bdb43e3e2a2b76254b4da5ef25aefb32aae260bb386cd133045adfa2024b067c2990b60d6f014e039
38 - gost ef6cb990b754b1d6a428f6bb5c113ee22cc9533558d203161441933d86e3b6f8
39 - whirlpool 54eb1d0667b6fdf97c01e005ac1febfacf8704da55c70f10f812b34cd9d45528b60d20f08765ced0ab3086d2bde312259aebf15d105318ae76995c4cf9a1e981
40 - snefru256 20849cbeda5ddec5043c09d36b2de4ba0ea9296b6c9efaa7c7257f30f351aea4
41 - snefru 20849cbeda5ddec5043c09d36b2de4ba0ea9296b6c9efaa7c7257f30f351aea4
42 - md2 d4864c8c95786480d1cf821f690753dc
Pez Cuckow
fonte
4
Há um erro mínimo de um por um no final. strlen($characters)deve ser strlen($characters) - 1:)
MM.
29

Há uma comparação de velocidade no site xxhash. Copie e cole aqui:

 Name            Speed       Q.Score   Author
 xxHash          5.4 GB/s     10
 MumurHash 3a    2.7 GB/s     10       Austin Appleby
 SpookyHash      2.0 GB/s     10       Bob Jenkins
 SBox            1.4 GB/s      9       Bret Mulvey
 Lookup3         1.2 GB/s      9       Bob Jenkins
 CityHash64      1.05 GB/s    10       Pike & Alakuijala
 FNV             0.55 GB/s     5       Fowler, Noll, Vo
 CRC32           0.43 GB/s     9
 MD5-32          0.33 GB/s    10       Ronald L. Rivest
 SHA1-32         0.28 GB/s    10

Parece que o xxHash é de longe o mais rápido, enquanto muitos outros batem em hashes mais antigos, como CRC32, MD5 e SHA.

https://code.google.com/p/xxhash/

Observe que este é o pedido em uma compilação de 32 bits. Em uma compilação de 64 bits, a ordem de desempenho provavelmente é muito diferente. Alguns dos hashes são fortemente baseados em multiplicações e buscas de 64 bits.

hdante
fonte
17
+-------------------+---------+------+--------------+
|       NAME        |  LOOPS  | TIME |     OP/S     |
+-------------------+---------+------+--------------+
| sha1ShortString   | 1638400 | 2.85 | 574,877.19   |
| md5ShortString    | 2777680 | 4.11 | 675,834.55   |
| crc32ShortString  | 3847980 | 3.61 | 1,065,922.44 |
| sha1MediumString  | 602620  | 4.75 | 126,867.37   |
| md5MediumString   | 884860  | 4.69 | 188,669.51   |
| crc32MediumString | 819200  | 4.85 | 168,907.22   |
| sha1LongString    | 181800  | 4.95 | 36,727.27    |
| md5LongString     | 281680  | 4.93 | 57,135.90    |
| crc32LongString   | 226220  | 4.95 | 45,701.01    |
+-------------------+---------+------+--------------+

Parece que o crc32 é mais rápido para mensagens pequenas (nesse caso, 26 caracteres) e o md5 para mensagens mais longas (nesse caso,> 852 caracteres).

Aalex Gabi
fonte
17

Atualização de 2019: esta resposta é a mais atualizada. As bibliotecas para apoiar o murmúrio estão amplamente disponíveis para todos os idiomas.

A recomendação atual é usar o Murmur Hash Família (ver especificamente o murmur2 ou murmur3 variantes).

Os hashes de Murmur foram projetados para o hash rápido com colisões mínimas (muito mais rápidas que CRC, MDx e SHAx). É perfeito procurar duplicatas e muito apropriado para índices HashTable.

De fato, é usado por muitos bancos de dados modernos (Redis, ElastisSearch, Cassandra) para calcular todo tipo de hashes para vários propósitos. Esse algoritmo específico foi a fonte raiz de muitas melhorias de desempenho na década atual.

Também é usado em implementações de filtros Bloom . Você deve estar ciente de que, se estiver pesquisando "hashes rápidos", provavelmente está enfrentando um problema típico resolvido pelos filtros Bloom. ;-)

Nota : o sopro é um hash de uso geral, que significa NÃO criptográfico. Não impede encontrar o "texto" de origem que gerou um hash. NÃO é apropriado usar senhas de hash.

Mais alguns detalhes: MurmurHash - o que é?

user5994461
fonte
2
Há uma solicitação aberta aqui para adicionar murmurhash ao php, na qual você pode votar.
Keune
8

Em vez de assumir que o MD5 é "bastante lento", tente. Uma implementação simples do MD5 baseada em C em um PC simples (o meu, um Core2 de 2,4 GHz, usando um único núcleo) pode gerar 6 milhões de pequenas mensagens por segundo . Uma pequena mensagem está aqui com até 55 bytes. Para mensagens mais longas, a velocidade de hash MD5 é linear com o tamanho da mensagem, ou seja, processa dados a cerca de 400 megabytes por segundo. Você pode observar que isso é quatro vezes a velocidade máxima de um bom disco rígido ou de uma placa de rede Ethernet de gigabit.

Como meu PC possui quatro núcleos, isso significa que o hash de dados tão rápido quanto meu disco rígido pode fornecer ou receber usos no máximo 6% da capacidade de computação disponível. É necessária uma situação muito especial para que a velocidade do hash se torne um gargalo ou até induza um custo perceptível em um PC.

Em arquiteturas muito menores, nas quais a velocidade do hash pode se tornar um pouco relevante, convém usar o MD4. O MD4 é adequado para fins não criptográficos (e para fins criptográficos, você não deve usar o MD5 de qualquer maneira). Foi relatado que o MD4 é ainda mais rápido que o CRC32 em plataformas baseadas em ARM.

Thomas Pornin
fonte
Há um ponto a considerar. O MD5 ocupa 128 bits em vez de 32. Isso significa que o armazenamento do banco de dados ocupa 4 vezes mais espaço e, portanto, 4 vezes mais lento para comparar hashes (eu acho ). O que me preocupa (para meus usos) é a rapidez com que será consultar o banco de dados mais tarde, quando estiver cheio de hashes.
Camilo Martin
3
Se você não usar uma saída suficientemente ampla, obterá colisões aleatórias, o que será ruim, pois o objetivo é consultar um banco de dados para saber se uma determinada "frase" já é conhecida; as colisões aqui se transformam em falsos positivos. Com 32 bits, você começará a ver colisões assim que tiver mais de 60000 frases. Isso vale para todas as funções de hash, criptográficas ou não. Dito isto, você sempre pode pegar a saída de uma função de hash e truncá-la no tamanho que desejar, dentro das limitações explicadas acima.
Thomas Pornin 28/03
@ThomasPornin Se seguirmos o caminho truncante, não enfrentaremos novamente o problema da colisão, quero dizer que a única razão pela qual o md5 não deve ter uma colisão fácil é o número extra de caracteres que ele possui quando comparado ao CRC32, certo?
Mohd Abdul Mujib
4

Embargo

A resposta abaixo não responde à pergunta, pois não recomenda funções de hash. Lembre-se: "Uma função hash é qualquer função que pode ser usada para mapear dados de tamanho arbitrário para valores de tamanho fixo". (Wikipedia) A resposta abaixo recomenda transformações que não garantem resultados de tamanho fixo.

Se você deseja relaxar o requisito de usar uma função hash , continue lendo ...

Resposta original

Sugiro urlencode () ou base64_encode () pelos seguintes motivos:

  • Você não precisa de criptografia
  • Você quer velocidade
  • Você deseja uma maneira de identificar cadeias únicas enquanto limpa cadeias 'malformadas'

Adaptando o código de benchmark em outras partes dessas respostas, demonstrei que qualquer um deles é muito mais rápido que qualquer algoritmo de hash. Dependendo do seu aplicativo, você poderá usar o urlencode () ou o base64_encode () para limpar as seqüências de caracteres 'malformadas' que deseja armazenar.

Anacronista
fonte
Re: "Você quer uma maneira de identificar strings únicas enquanto limpa strings 'malformadas'": você elaboraria por favor?
David J.
É difícil lembrar o que eu pensava há seis anos ... Eu poderia estar aludindo ao fato de você não ter colisões com urlencode ou base64_encode, para que os resultados sejam tão únicos quanto as strings originais.
Anacronista
2

Etapa 1: Instale o libsodium (ou verifique se você está usando o PHP 7.2+)

Etapa 2: Use um dos seguintes:

  1. sodium_crypto_generichash(), que é BLAKE2b , uma função de hash mais segura que MD5, mas mais rápida que SHA256. (O link tem referências, etc.)
  2. sodium_crypto_shorthash(), que é SipHash-2-4 , que é apropriado para tabelas de hash, mas não deve ser invocado para resistência à colisão.

_shorthashé cerca de 3x mais rápido que _generichash, mas você precisa de uma chave e tem um risco pequeno, mas realista de colisões. Com _generichash, você provavelmente não precisa se preocupar com colisões e não precisa usar uma chave (mas pode querer de qualquer maneira).

Scott Arciszewski
fonte
1
pergunta é "quão rápido é essa coisa"?
my1
1
sodium_crypto_generichash(), which is BLAKE2b, a hash function more secure than MD5 but faster than SHA256. (Link has benchmarks, etc.)- blake2b com certeza é, mas uma implementação de USERLAND PHP do blake2b será muito mais lenta que o sha256 implementado em C para PHP ... eu gostaria que o PHP pudesse adotar o blake2b na suíte hash_algos () ..
hanshenrik
A implementação pura do PHP não foi sugerida aqui.
Scott Arciszewski
1

Se você procura um serviço rápido e exclusivo, recomendo o xxHash ou algo que use o comando interno crc32c da CPU mais recente, consulte https://stackoverflow.com/a/11422479/32453 . Ele também liga a hashes possivelmente ainda mais rápidos, se você não se importa com a possibilidade de colisão.

rogerdpack
fonte
1

O Adler32 tem melhor desempenho na minha máquina. E md5()saiu mais rápido que crc32().

Max Tsepkov
fonte
3
Se o MD5 for mais rápido que uma função CRC32 genérica, algo está muito errado.
Nxasdf 14/03
0

A implementação do md5 dentro do hash é um pouco mais rápida que o md5 (). Portanto, isso pode ser uma opção ou outra coisa, tente:

echo '<pre>';

$run = array();

function test($algo)
{
  #static $c = 0;
  #if($c>10) return;
  #$c++;

 $tss = microtime(true);
 for($i=0; $i<100000; $i++){
  $x = hash($algo, "ana are mere");
 }
 $tse = microtime(true);

 $GLOBALS['run'][(string)round($tse-$tss, 5)] = "\nhash({$algo}): \t".round($tse-$tss, 5) . " \t" . $x;
 #echo "\n$i nhash({$algo}): \t".round($tse-$tss, 5) . " \t" . $x;
}
array_map('test', hash_algos());
ksort($run);
print_r($run);
echo '</pre>';

Você pode ver em http://www.dozent.net/Tipps-Tricks/PHP/hash-performance

Frank
fonte
0

O CRC32 é mais rápido, mas menos seguro que o MD5 e o SHA1. Não há muita diferença de velocidade entre o MD5 e o SHA1.

Sjoerd
fonte
O MD5 agora é considerado inseguro. É muito mais inseguro que o SHA1. Leia a página da wiki MD5.
Ahmed