Mapa hash C / C ++ de alto rendimiento (tabla, diccionario) [cerrado]

84

Necesito mapear claves primitivas (int, tal vez long) para estructurar valores en una estructura de datos de mapa hash de alto rendimiento.

Mi programa tendrá algunos cientos de estos mapas, y cada mapa generalmente tendrá como máximo algunos miles de entradas. Sin embargo, los mapas se "actualizarán" o "agitarán" constantemente; imaginar procesar millones de addy deletemensajes de un segundo.

¿Qué bibliotecas en C o C ++ tienen una estructura de datos que se ajusta a este caso de uso? O, ¿cómo recomendarías construir el tuyo propio? ¡Gracias!

Haywood Jablomey
fuente
1
¿Necesita procesar la búsqueda por claves en sus datos?
Guillaume Lebourgeois
3
¿Las actualizaciones o recuperaciones serán más frecuentes? (agregar / eliminar, o leer / actualizar que no cambia la clave)
falstro
stackoverflow.com/questions/266206/… . Este quizás sea un buen lugar para comenzar.
DumbCoder
2
@roe:Las operaciones de agregar / eliminar son mucho (100 veces) más frecuentes que la operación de obtención.
Haywood Jablomey
1
Después de cuatro años y medio sería interesante saber qué se ajusta mejor a sus necesidades. Si ninguna de las respuestas actuales fue satisfactoria, puede escribir la suya y aceptarla.
Walter Tross

Respuestas:

31

Le recomendaría que pruebe Google SparseHash (o la versión C11 de Google SparseHash-c11 ) y vea si se adapta a sus necesidades. Tienen una implementación de memoria eficiente, así como una optimizada para la velocidad. Hice un punto de referencia hace mucho tiempo, fue la mejor implementación de tabla hash disponible en términos de velocidad (sin embargo, con inconvenientes).

Scharron
fuente
16
¿Puede explicarnos cuáles fueron los inconvenientes?
Haywood Jablomey
IIRC, fue un problema de memoria, al eliminar un elemento, el elemento se destruyó pero su memoria aún estaba viva (se usa como caché, supongo).
Scharron
4
@Haywood Jablomey: El principal inconveniente es que requiere que separes uno o dos (si alguna vez borras elementos) valores y nunca los uses. En algunos casos, esto es fácil de hacer, por ejemplo, entradas negativas o similares, pero en otros casos no del todo.
doblep
3
¿Seguiría esta recomendación hoy?
einpoklum
11

¿Qué bibliotecas en C o C ++ tienen una estructura de datos que se ajusta a este caso de uso? O, ¿cómo recomendarías construir el tuyo propio? ¡Gracias!

Echa un vistazo a las matrices Judy LGPL . Nunca me utilicé, pero me lo anunciaron en pocas ocasiones.

También puede intentar comparar contenedores STL (std :: hash_map, etc.). Dependiendo de la plataforma / implementación y el ajuste del código fuente (preasignar tanto como sea posible, la administración de memoria dinámica es costosa), podrían tener el rendimiento suficiente.

Además, si el rendimiento de la solución final supera el costo de la solución, puede intentar ordenar el sistema con suficiente RAM para poner todo en arreglos simples. El rendimiento de acceso por índice es inmejorable.

Las operaciones de agregar / eliminar son mucho (100 veces) más frecuentes que la operación de obtención.

Eso sugiere que es posible que desee concentrarse primero en mejorar los algoritmos. Si los datos solo se escriben, no se leen, ¿por qué escribirlos?

Dummy00001
fuente
11

Simplemente use boost::unordered_map(o tr1etc.) de forma predeterminada. Luego, perfile tu código y fíjate si ese código es el cuello de botella. Solo entonces sugeriría analizar con precisión sus requisitos para encontrar un sustituto más rápido.

Marca B
fuente
15
Está. VS2013 std::unordered_mapestá tomando más del 90% de todo mi tiempo de ejecución, aunque solo uso los mapas para una parte relativamente pequeña del procesamiento.
Cameron
2

Primero verifique si las soluciones existentes como libmemcache se ajustan a sus necesidades.

Si no ...

Los mapas hash parecen ser la respuesta definitiva a sus necesidades. Proporciona o (1) búsqueda basada en las claves. La mayoría de las bibliotecas STL proporcionan algún tipo de hash en estos días. Así que usa el que te proporciona tu plataforma.

Una vez que termine esa parte, debe probar la solución para ver si el algoritmo de hash predeterminado es lo suficientemente bueno en cuanto a rendimiento para sus necesidades.

Si no es así, debería explorar algunos buenos algoritmos de hash rápido que se encuentran en la red.

  1. buen número primo multiplicar algo
  2. http://www.azillionmonkeys.com/qed/hash.html
  3. http://burtleburtle.net/bob/
  4. http://code.google.com/p/google-sparsehash/

Si esto no es lo suficientemente bueno, puede lanzar un módulo hash por su cuenta, que solucione el problema que vio con los contenedores STL que ha probado y uno de los algoritmos hash anteriores. Asegúrese de publicar los resultados en algún lugar.

Ah, y es interesante que tenga múltiples mapas ... quizás pueda simplificar al tener su clave como un número de 64 bits con los bits altos utilizados para distinguir a qué mapa pertenece y agregar todos los pares de valores de clave a un hash gigante. He visto hashes que tienen cientos de miles de símbolos que funcionan perfectamente bien en el algoritmo básico de hash de números primos bastante bien.

Puede comprobar cómo funciona esa solución en comparación con cientos de mapas ... creo que podría ser mejor desde el punto de vista del perfil de la memoria ... por favor publique los resultados en algún lugar si puede hacer este ejercicio

Creo que más que el algoritmo hash, podría ser la adición / eliminación constante de memoria (¿se puede evitar?) Y el perfil de uso de caché de la CPU lo que podría ser más crucial para el rendimiento de su aplicación.

buena suerte

computacion
fuente
2

Pruebe las tablas hash de varias plantillas de contenedores . Tiene closed_hash_mapaproximadamente la misma velocidad que la de Google dense_hash_map, pero es más fácil de usar (sin restricciones en los valores contenidos) y también tiene otras ventajas.

doble
fuente
2

Sugeriría uthash . Simplemente incluya y #include "uthash.h"luego agregue UT_hash_handlea la estructura y elija uno o más campos en su estructura para que actúen como clave. Unas palabras sobre el rendimiento aquí .

saharsh-jain
fuente
1

http://incise.org/hash-table-benchmarks.html gcc tiene una muy buena implementación. Sin embargo, tenga en cuenta que debe respetar una decisión estándar muy mala:

Si ocurre un refrito, todos los iteradores se invalidan, pero las referencias y punteros a elementos individuales siguen siendo válidos. Si no ocurre ningún refrito real, no hay cambios.

http://www.cplusplus.com/reference/unordered_map/unordered_map/rehash/

Esto significa que básicamente el estándar dice que la implementación DEBE ESTAR basada en listas enlazadas. Evita el direccionamiento abierto que tiene un mejor rendimiento.

Creo que Google Sparse utiliza direcciones abiertas, aunque en estos puntos de referencia solo la versión densa supera a la competencia. Sin embargo, la versión dispersa supera a toda la competencia en el uso de memoria. (tampoco tiene meseta, línea recta pura con número de elementos)

v.oddou
fuente
1
Consulte también esto , que explica cómo la interfaz del depósito también requiere encadenamiento. El punto sobre las referencias es muy bueno. Es tentador discutir y decir que es una garantía útil, pero en muchos casos solo queremos referencias para evitar buscar elementos nuevamente, y la razón habitual es porque la búsqueda es demasiado lenta ... lo cual no sería así si no fuera así. ¡Debe mantener las referencias válidas y, por lo tanto, podría usar direcciones abiertas! Entonces parece un poco la gallina y el huevo. Esto cita la propuesta de 2003, discutiendo explícitamente la elección.
underscore_d