¿El hash más rápido para usos no criptográficos?

154

Básicamente estoy preparando frases para ponerlas en la base de datos, pueden estar mal formadas, por lo que quiero almacenar un hash corto de ellas (simplemente compararé si existen o no, por lo que el hash es ideal).

Supongo que MD5 es bastante lento en más de 100,000 solicitudes, por lo que quería saber cuál sería el mejor método para mezclar las frases, ¿tal vez implementar mi propia función hash o usarlo hash('md4', '...'sería más rápido al final?

Sé que MySQL tiene MD5 (), por lo que complementaría un poco de velocidad en el final de la consulta, pero tal vez haya una función de hash más rápida en MySQL. No sé si eso funcionaría con PHP.

Juan
fuente
66
¿Qué te impide comparar los hashes?
NullUserException
3
NullUserException: Tienes razón, los probaré con frases de longitud aleatoria. Solo quería saber cuál sería la norma, si es que hay alguna, para manejar este tipo de cosas.
John
55
MD5 no es realmente tan lento ...
Ámbar
25
¿estás seguro de que la función de hash es un cuello de botella de toda la aplicación? Lo dudo
su sentido común
44
Esta es una muy buena pregunta, y los comentarios que implican que no lo son, o que no son importantes, y / o deberían ser obvios y / o intuitivos, son decepcionantes y frustrantes. (Y tampoco nada inesperado)
Michael

Respuestas:

56

CRC32 es bastante rápido y tiene una función: http://www.php.net/manual/en/function.crc32.php

Pero debe tener en cuenta que CRC32 tendrá más colisiones que MD5 o incluso hashes SHA-1, simplemente debido a la longitud reducida (32 bits en comparación con 128 bits, respectivamente, 160 bits). Pero si solo desea verificar si una cadena almacenada está dañada, estará bien con CRC32.

joschi
fuente
1
Wow, solo el tipo de datos requerido es un entero sin signo, esto será SIGNIFICATIVAMENTE más rápido que otro hashing.
John
2
@John: o no. CRC32 resulta ser más lento que MD4, y no mucho más rápido que MD5, en procesadores ARM. Además, CRC32 utiliza un tipo entero de 32 bits sin signo, que es exactamente todo lo que necesita MD5 ...
Thomas Pornin
3
Si tiene el beneficio / lujo de una nueva CPU Intel, hay un comando de ensamblaje crc32c que es ... probablemente realmente rápido (aunque no es el valor crc32 tradicional). Ver también 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

Y el código utilizado para generar esto es:

 $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
fuente
3
Ah, gracias por esta información en realidad, solo fortalece mi uso de CRC32 como el más rápido.
John
@ John - Puede recuperar los algoritmos hash usando: hash_algos(). El siguiente código de evaluación comparativa de hash estaba en los comentarios de PHP ==> codepad.viper-7.com/5Wdhw6
Peter Ajtai
Gracias por tu código. Lo he mejorado un poco. No creo que debamos comparar funciones como md5 () que procesan toda la cadena y los bucles que hacen byte a byte como lo hiciste con xor. En PHP, estos bucles son muy lentos e incluso más lentos que el md5. Deberíamos comparar unos hases con otros, todos implementados como funciones.
Maxim Masiutin
1
Solo una nota rápida: probé esto con una cadena mucho más larga (~ 5000 caracteres) y CRC32 fue más lento que MD5 y SHA1 en mi máquina (i7-6650U, 16GB). CRC32 - 1.7s, MD5 - 1.4s, SHA1 - 1.5s. Siempre prueba por ti mismo.
Sam Tolton
44
@Quamis la prueba es buena pero puede ser engañosa, como @samTolton señaló que los resultados son diferentes y md5más rápidos. Una mejor prueba será aleatorizar el contenido y la longitud de las cadenas también. De esta forma tenemos una mejor idea sobre el rendimiento real del mundo real. Esto también evitará el almacenamiento en caché. Echa un vistazo: rendimiento de la suma de comprobación de hash php
Shlomi Hassid
43

Lista clasificada donde cada bucle comparte la misma cosa que todas las demás.

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

?>

Y la salida

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
fuente
44
Hay un mínimo error uno por uno al final. strlen($characters)debería ser strlen($characters) - 1:)
MM.
29

Hay una comparación de velocidad en el sitio xxhash. Copia pegándolo aquí:

 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

Por lo tanto, parece que xxHash es, con mucho, el más rápido, mientras que muchos otros superan los hashes más antiguos, como CRC32, MD5 y SHA.

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

Tenga en cuenta que este es el pedido en una compilación de 32 bits. En una compilación de 64 bits, el orden de rendimiento es muy diferente. Algunos de los hashes se basan principalmente en multiplicaciones y recuperaciones de 64 bits.

hdante
fuente
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 crc32 es más rápido para mensajes pequeños (en este caso 26 caracteres) mientras que md5 para mensajes más largos (en este caso> 852 caracteres).

Aalex Gabi
fuente
17

Actualización de 2019: esta respuesta es la más actualizada. Las bibliotecas para apoyar el soplo están ampliamente disponibles para todos los idiomas.

La recomendación actual es utilizar la familia Murmur Hash (ver específicamente las variantes murmur2 o murmur3 ).

Los hash de murmullo fueron diseñados para un hashing rápido con colisiones mínimas (mucho más rápido que CRC, MDx y SHAx). Es perfecto para buscar duplicados y muy apropiado para los índices de HashTable.

De hecho, muchas de las bases de datos modernas (Redis, ElastisSearch, Cassandra) lo utilizan para calcular todo tipo de hashes para diversos fines. Este algoritmo específico fue la fuente raíz de muchas mejoras de rendimiento en la década actual.

También se usa en implementaciones de Bloom Filters . Debe tener en cuenta que si está buscando "hashes rápidos", probablemente se enfrenta a un problema típico que se resuelve con los filtros Bloom. ;-)

Nota : el soplo es un hash de propósito general, lo que significa NO criptográfico. No impide encontrar el "texto" de origen que generó un hash. NO es apropiado usar contraseñas hash.

Algunos detalles más: MurmurHash : ¿qué es?

usuario5994461
fuente
2
Hay una solicitud de apertura aquí para agregar murmurhash a php, que se puede votar.
keune
8

En lugar de asumir que MD5 es "bastante lento", pruébelo. Una implementación simple basada en C de MD5 en una PC simple (la mía, un Core2 de 2.4 GHz, usando un solo núcleo) puede generar 6 millones de mensajes pequeños por segundo . Aquí hay un pequeño mensaje de hasta 55 bytes. Para mensajes más largos, la velocidad de hash MD5 es lineal con el tamaño del mensaje, es decir, procesa datos a aproximadamente 400 megabytes por segundo. Puede observar que esto es cuatro veces la velocidad máxima de un buen disco duro o una tarjeta de red gigabit ethernet.

Dado que mi PC tiene cuatro núcleos, esto significa que los datos de hash tan rápido como mi disco duro pueden proporcionar o recibir usos como máximo del 6% de la potencia informática disponible. Se necesita una situación muy especial para que la velocidad de hash se convierta en un cuello de botella o incluso para inducir un costo notable en una PC.

En arquitecturas mucho más pequeñas donde la velocidad de hash puede volverse algo relevante, es posible que desee usar MD4. MD4 está bien para fines no criptográficos (y para fines criptográficos, no debe usar MD5 de todos modos). Se ha informado que MD4 es incluso más rápido que CRC32 en plataformas basadas en ARM.

Thomas Pornin
fuente
Hay un punto a considerar. MD5 ocupa 128 bits en lugar de 32. Esto significa que el almacenamiento de la base de datos ocupa 4 veces más espacio y, por lo tanto, es 4 veces más lento para buscar hashes de comparación ( creo ). Lo que me preocupa (para mis usos) es lo rápido que será consultar la base de datos más tarde cuando esté llena de hashes.
Camilo Martin
3
Si no utiliza una salida lo suficientemente amplia, obtendrá colisiones aleatorias, lo que será malo ya que el objetivo es consultar una base de datos para saber si ya se conoce una "frase" dada; Las colisiones aquí se convierten en falsos positivos. Con 32 bits, comenzará a ver colisiones tan pronto como tenga 60000 o más frases. Esto es cierto para todas las funciones hash, criptográficas o no. Dicho esto, siempre puede tomar la salida de una función hash y truncarla a la longitud que considere conveniente, dentro de las limitaciones explicadas anteriormente.
Thomas Pornin
@ThomasPornin Si seguimos por el camino truncado, ¿no volvería a enfrentar el problema de la colisión? Quiero decir, la única razón por la que se supone que el md5 no tendrá una colisión fácil es el no adicional de caracteres que tiene en comparación con CRC32, ¿verdad?
Mohd Abdul Mujib
4

Consideración

La respuesta a continuación no responde la pregunta como se le preguntó, ya que no recomienda las funciones hash. Recuerde, "Una función hash es cualquier función que se puede utilizar para asignar datos de tamaño arbitrario a valores de tamaño fijo". (Wikipedia) La respuesta a continuación recomienda transformaciones que no garantizan resultados de tamaño fijo.

Si está dispuesto a relajar el requisito de usar una función hash , siga leyendo ...

Respuesta original

Sugiero urlencode () o base64_encode () por estos motivos:

  • No necesitas criptografía
  • Quieres velocidad
  • Desea una forma de identificar cadenas únicas mientras limpia cadenas 'malformadas'

Adaptando el código de referencia en otras partes de estas respuestas, he demostrado que cualquiera de estos es mucho más rápido que cualquier algoritmo hash. Dependiendo de su aplicación, puede usar urlencode () o base64_encode () para limpiar cualquier cadena 'malformada' que desee almacenar.

Anacronista
fuente
Re: "Desea una manera de identificar cadenas únicas mientras limpia las cadenas 'malformadas'": ¿podría elaborar por favor?
David J.
Es difícil recordar lo que estaba pensando hace seis años ... Podría haber estado aludiendo al hecho de que no se producen colisiones con urlencode o base64_encode, por lo que los resultados serían tan únicos como las cadenas originales.
Anacronista
2

Paso uno: Instale libsodium (o asegúrese de estar usando PHP 7.2+)

Paso dos: utilice uno de los siguientes:

  1. sodium_crypto_generichash(), que es BLAKE2b , una función hash más segura que MD5 pero más rápida que SHA256. (El enlace tiene puntos de referencia, etc.)
  2. sodium_crypto_shorthash(), que es SipHash-2-4 , que es apropiado para las tablas hash pero no se debe confiar en su resistencia a la colisión.

_shorthashes aproximadamente 3 veces más rápido _generichash, pero necesita una llave y tiene un riesgo pequeño pero realista de colisiones. Con _generichash, es probable que no tenga que preocuparse por las colisiones y no necesite usar una llave (pero puede querer hacerlo de todos modos).

Scott Arciszewski
fuente
1
la pregunta es "¿qué tan rápido es esta cosa"?
Mi1
1
sodium_crypto_generichash(), which is BLAKE2b, a hash function more secure than MD5 but faster than SHA256. (Link has benchmarks, etc.)- blake2b seguro lo es, pero una implementación PHP de USERLAND de blake2b va a ser mucho más lenta que el sha256 implementado en C para PHP ... desearía que PHP pudiera adoptar blake2b en la suite hash_algos () ..
hanshenrik
La implementación pura de PHP no se sugirió aquí.
Scott Arciszewski
1

Si está buscando un método rápido y único, le recomiendo xxHash o algo que use el comando incorporado crc32c de la cpu más reciente, consulte https://stackoverflow.com/a/11422479/32453 . También se vincula allí a hashes posiblemente incluso más rápidos si no le importa tanto la posibilidad de colisión.

rogerdpack
fuente
1

Adler32 funciona mejor en mi máquina. Y md5()resultó más rápido que crc32().

Max Tsepkov
fuente
3
Si MD5 es más rápido que una función CRC32 genérica, entonces algo está muy mal.
nxasdf
0

La implementación de md5 dentro de hash es un poco más rápida que md5 (). Por lo tanto, esta puede ser una opción u otra, intente:

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

Puede ver en http://www.dozent.net/Tipps-Tricks/PHP/hash-performance

Franco
fuente
0

CRC32 es más rápido, pero menos seguro que MD5 y SHA1. No hay tanta diferencia de velocidad entre MD5 y SHA1.

Sjoerd
fuente
MD5 ahora se considera inseguro. Es mucho más inseguro que SHA1. Lea la página wiki MD5.
Ahmed