Estoy haciendo un proyecto para una clase que se enfoca en almacenar una enorme matriz con la mayoría de los 0 valores en la memoria y realizar algunos cálculos matriciales en ella. Mi primer pensamiento fue utilizar un HashMap
para almacenar los elementos de la matriz, y solo almacenar los elementos que no son cero, para evitar el uso de grandes cantidades de memoria.¿Por qué es eso, cuantos más '1' bits tengo en mi clave, más tiempo me toma colocar en el HashMap?
Quería hacer una clave para el HashMap
que representara tanto la fila como el número de columna del elemento de manera que, cuando accedí a esa entrada en el mapa, pude volver a extraer ambos valores. No sé Java tan bien como C# - en C# Haría un struct
con y Column
miembros, pero en Java me di cuenta rápidamente de que no hay tipos de valores de usuario. Con una fecha límite inminente, hice una apuesta segura e hice que el Key
fuera largo. Guardé los datos de la fila (int de 32 bits) en los primeros 32 bits y los datos de la columna en los últimos 32 usando algunos cambios de bit muy simples. [EDITAR: También me gustaría señalar que mi HashMap se inicializó con un tamaño inicial específico que representa exactamente el número de valores que almaceno en él, que nunca se excede.]
[Nota al margen: el motivo que quiero para poder extraer los datos de fila/columna de nuevo es aumentar considerablemente la eficiencia de la multiplicación de matrices, desde O(n^2)
a O(n)
, y una más pequeña n
para arrancar]
lo que noté después de la implementación de esta estructura es que se necesita un enorme 7 segundos para leer una matriz de 23426 x 23426 de un archivo de texto en el que solo se dan elementos distintos de cero, ¡pero solo lleva 2 segundos calcular los valores propios que debemos dar! Después de comentar de forma selectiva los métodos, he llegado a la conclusión de que la mayor parte de este lapso de tiempo de 7 segundos se usa para almacenar mis valores en el HashMap
.
public void Set(double value, int row, int column) {
//assemble the long key, placing row and column in adjacent sets of bits
long key = (long)row << SIZE_BIT_MAX; //(SIZE_BIT_MAX is 32)
key += column;
elements.put(key, value);
}
Ese es el código para establecer un valor. Si utilizo este método en su lugar:
public void Set(double value, int row, int column) {
//create a distinct but smaller key (around 32 bits max)
long key = (long)(row * matrixSize) + column;
elements.put(key, value);
}
La lectura solo toma 2 segundos. Ambas versiones de la clave son distintas para cada elemento, ambas son de tipo largo, y el código real para crear cualquiera de ellas es de complejidad mínima. Es el elements.put(key, value)
que marca la diferencia entre 7 segundos y 2.
Mi pregunta es, ¿por qué? La diferencia que veo entre estas versiones clave es que la primera tiene bits establecidos en 1 en todo y con más frecuencia, mientras que la segunda tiene todos sus 32 bits más altos establecidos en 0. ¿Estoy persiguiendo a una arenga roja, o es esta diferencia bastante dramática en el rendimiento el resultado de algo interno en el método HashMap.put
?
Sin un SSCCE, es bastante difícil decirte la razón. Supongo que no estás especificando un tamaño inicial para el mapa. Entonces comienza muy pequeño y tiene que cambiar el tamaño con frecuencia. Cambiar el tamaño, especialmente para mapas grandes, es bastante costoso. – jackrabbit
El tamaño inicial se especifica y nunca se excede. Editaré mi publicación para reflejar eso. –
Puede que sea una pequeña mejora, pero cree el HashMap con una cantidad adecuada de elementos iniciales para evitar volver a mezclar constantemente cuando se alcance la nueva capacidad. por ejemplo, nuevo HashMap (20000); –
brettw