Sto facendo un progetto per una classe che si concentra sull'archiviazione di una matrice enorme con la maggior parte dei valori 0 in memoria e su cui viene eseguita una matematica matriciale. Il mio primo pensiero è stato quello di utilizzare uno HashMap
per memorizzare gli elementi della matrice e memorizzare solo gli elementi che non sono zero, al fine di evitare l'utilizzo di enormi quantità di memoria.Perché è così, più bit "1" nella mia chiave, più tempo è necessario inserire in HashMap?
Volevo creare una chiave per lo HashMap
che rappresenterebbe sia il numero di riga e di colonna dell'elemento in un modo che, quando ho avuto accesso a quella voce nella mappa, è stato possibile estrarre entrambi i valori. Non conosco Java e C# - in C# vorrei creare un struct
con i membri e Column
, ma in Java ho realizzato rapidamente che non ci sono tipi di valori utente. Con una scadenza incombente sono andato con una scommessa sicura e reso il Key
un lungo. Ho archiviato i dati di riga (int 32 bit) nei primi 32 bit e i dati di colonna negli ultimi 32 utilizzando un cambio di bit molto semplice. [EDIT: Mi piacerebbe anche notare che la mia HashMap è inizializzata con una dimensione iniziale specifica che rappresenta esattamente il numero di valori che immagazzino, che non viene mai superato.]
[Nota a margine: la ragione per cui voglio per poter estrarre nuovamente i dati riga/colonna è di aumentare notevolmente l'efficienza della moltiplicazione di matrici, dalle O(n^2)
a O(n)
, e una più piccola n
per l'avvio]
quello che ho notato dopo l'attuazione questa struttura è che ci vuole un enorme 7 secondi per leggere una matrice 23426 x 23426 da un file di testo in cui vengono dati solo elementi diversi da zero, ma ci vogliono solo 2 secondi per calcolare i valori di autovalori che ci viene richiesto di dare! Dopo aver commentato in modo selettivo i metodi, ho concluso che la maggior parte di questo intervallo di 7 secondi è dedicato alla memorizzazione dei miei valori nello 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);
}
Questo è il codice per l'impostazione di un valore. Se invece utilizzo questo metodo:
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 lettura richiede solo 2 secondi. Entrambe queste versioni della chiave sono distinte per ogni elemento, entrambi sono di tipo lungo e il codice effettivo per crearli è di complessità minima. È il elements.put(key, value)
che fa la differenza tra 7 secondi e 2.
La mia domanda è, perché? La differenza che vedo tra queste versioni chiave è che il primo ha bit impostati su 1 in tutto e più frequentemente, mentre il secondo ha tutti i suoi 32 bit più alti impostati su 0. Sto inseguendo un'aringa rossa, o è questa differenza abbastanza drammatica nelle prestazioni il risultato di qualcosa di interno nel metodo HashMap.put
?
Senza un SSCCE, è piuttosto difficile dirvi il motivo. La mia ipotesi è che non stai specificando una dimensione iniziale per la mappa. Inizia quindi molto piccolo e deve ridimensionarsi frequentemente. Il ridimensionamento, specialmente per le mappe grandi, è piuttosto costoso. – jackrabbit
La dimensione iniziale è specificata e mai superata. Modificherò il mio post per riflettere questo. –
Forse un piccolo miglioramento, ma creare la HashMap con un numero appropriato di elementi iniziali per evitare di rielaborare costantemente quando viene raggiunta la nuova capacità. ad esempio, nuova HashMap (20000); –
brettw