C a p i t o l o 2
IL FORMATO DI FILE HDF
2.1
Introduzione al formato HDF5
Il formato HDF (Hierarchical Data Format) implementa un modello di gestione e memorizzazione dei dati. Tale modello comprende due ulteriori modelli astratti, uno per i dati stessi ed uno per lo storage dei dati, cioè l’organizzazione fisica del file e librerie per implementare il modello astratto dei dati e i diversi tipi di storage consentiti per essi. La struttura base di rappresentazione dei dati è l’array (eventualmente multidimensionale), comunque è possibile definire strutture per l’archiviazione come quelle riportate in figura 2.1, cioè formate da singoli campi (Variable), da array monodimensionali (Array), da matrici o array multidimensionali (Table) o da elementi misti, come ad esempio nel caso di tabelle con campi di tipi diversi (Mesh) [1].
Il file segue una struttura gerarchica per l’organizzazione dei dati, definendo al suo interno una sorta di file system, il quale permette di organizzare le tabelle secondo gruppi (come le cartelle di Windows) che raccolgono oggetti tra loro logicamente legati (Figura 2.2).
Fig. 2.2 – Organizzazione di un file HDF
Gli elementi chiave che contraddistinguono il formato sono:
File: una stringa contigua di bytes contenuta in un’unità di memorizzazione.
Group: una collezione di oggetti (inclusi altri gruppi).
Dataset: un array multidimensionale di dati con attributi ed altri metadati associati (come le dimensioni, il numero di elementi, etc.).
Attribute: struttura dati utilizzata per associare informazioni ad un dato oggetto (come un gruppo o un dataset).
La versione 5 (HDF5), sviluppata dal National Center for Supercomputing Applications (NCSA) è una libreria di I/O di alto livello largamente utilizzata. Un array memorizzato in un file HDF5 viene diviso in due parti, header e body; nell’header si possono trovare 4 tipi di informazioni [8]:
- Nome
- Tipo del dato - Dataspace
- Tipo di memorizzazione
Dove il nome è costituito da una stringa di testo, il tipo di dato può essere, ad esempio, primitivo o compound (un tipo primitivo come intero o double, o compound come collezione di tipi primitivi differenti), lo spazio del dato raccoglie informazioni circa le dimensioni del dato (la dimensione di un array può essere fissata a priori o illimitata) ed il tipo di memorizzazione specifica il tipo di storage dell’array nel file. Infatti, il modello di default per l’immagazzinamento dei dati all’interno del file è contiguo ed in questo modo il file è salvato nello stesso modo in cui è organizzato in memoria. L’altro modello di immagazzinamento è detto chunked e verrà illustrato nel paragrafo successivo in quanto il suo utilizzo è stato di grande importanza per l’ottenimento di buone prestazioni in lettura.
Per la visualizzazione dei file HDF è disponibile un programma free chiamato HDFView [3], realizzato dallo HDF Group e liberamente scaricabile. Tale programma, realizzato in Java, consente la visualizzazione di tutti i file in formato HDF, la creazione, la modifica, la cancellazione di gruppi, dataset, attributi, l’importazione di più dati insieme da file di testo, l’importazione e visualizzazione di immagini e la creazione di grafici di andamento da (parte di) dati contenuti nei diversi
dataset. Tutte le immagini raffiguranti la struttura del file HDF5 di simulazione sono “viste” del programma HDFView.
2.2 Utilizzo del
chunking
nella memorizzazione dei dati
Le tabelle contenenti dati in HDF5 sono, come detto, array multidimensionali e possono raggiungere dimensioni notevoli, in considerazione del fatto che il formato HDF è realizzato per l’archiviazione di dati scientifici, i quali, generalmente, formano grandi dataset con dati accumulati durante esperimenti, osservazioni, calcoli. Il modo in cui i dataset del formato HDF vengono memorizzati è, per default, quello contiguo, se non viene specificato altrimenti in fase di creazione del file. Tale metodo di allocazione prevede la serializzazione dell’intero dataset in un blocco monolitico salvato su disco; questo implica che la ricerca di un dato all’interno del dataset comporti il caricamento in memoria dell’intero dataset prima di poter effettuare la ricerca del dato di interesse.
Il secondo metodo di allocazione è detto chunked: i dataset chunked sono divisi in chunk (blocchi) di dimensioni uguali tra loro e salvati separatamente nel file. Di grande importanza è il fatto che tali chunk possono essere letti o scritti singolarmente, perciò caricati indipendentemente dal resto del dataset per svolgere tali operazioni. In
Figura 2.3 si vede un dataset allocato in maniera contigua e lo stesso dataset diviso in più chunk.
Fig. 2.3 – Dataset contiguo (sinistra) e chunked (destra)
2.2.1 Vantaggi dell’uso del chunking
Per comprendere i vantaggi dell’utilizzo di questo metodo di memorizzazione del dataset, occorre sapere che il formato HDF prevede un salvataggio dei dati su disco in ordine di riga: se si considera un array bidimensionale, i dati che lo costituiscono vengono memorizzati riga per riga. Pertanto operazioni che richiedono il prelievo di dati contigui lungo una riga risultano efficienti (Fig. 2.4), mentre altre che cercano dati magari su una colonna della matrice risultano molto lente (Fig. 2.5) [4].
Fig. 2.4 – Accesso ad una riga di un dataset contiguo
In un accesso per riga ad un dataset contiguo è necessaria una sola operazione di ricerca per individuare il punto di partenza della sequenza di dati cercata.
Fig. 2.5 – Accesso ad una colonna di un dataset contiguo
Per accedere ai dati collocati in una colonna del dataset sono invece necessari M (con M numero di righe) operazioni di ricerca. Nel
caso in cui il dataset sia invece chunked, il numero delle operazioni di ricerca per dati collocati lungo una colonna risulta decisamente ridotto. Inoltre non vi è più la necessità di caricare in memoria l’intero dataset per prelevare più dati, ma è sufficiente caricare quei chunk del dataset contenenti i dati di interesse. Ad esempio, nel caso di lettura di una colonna di un dataset di 8 righe per 10 colonne, con chunk 4x2, il numero di accessi è limitato a due, mentre prima sarebbero occorsi 8 accessi (uno per ogni riga), come mostrato in figura 2.6.
Fig. 2.6 – Accesso ad una colonna di un dataset chunked Questo tipo di memorizzazione, ossia l’utilizzo di dataset chunked, si è rivelato di fondamentale importanza nell’incremento delle performance delle operazioni di lettura, come verrà descritto successivamente nel capitolo 4.
2.3 Organizzazione del file HDF di simulazione
Nel capitolo 1 è stato mostrata la struttura del file di restart del simulatore Relap (Figura 1.5). Tale file, un esempio del quale si riporta in
Figura 2.7, risulta essere la componente di maggiori dimensioni che viene archiviata su HDF e la decisione sul modo di organizzare i dati in esso contenuti ha comportato un’attenta analisi preliminare per capire come interpretare (in primo luogo) i dati stessi e successivamente individuare una divisione tra gli stessi che potesse ricondurre ad un’organizzazione secondo tabelle simili a quelle che potrebbero formare un database di tipo relazionale.
Fig. 2.7 – Struttura del file di restart di Relap
La decisione finale è stata quella di prevedere un dataset per ogni quantità del record plotalf, in forma di array bidimensionale, in cui ogni riga corrisponde ad un istante di simulazione ed ogni colonna corrisponde ad un nodo. I valori temporali degli istanti e gli identificatori dei nodi sono contenuti in altre tabelle. I nodi sono elencati in un dataset nello stesso gruppo del dataset con i valori per la quantità. I tempi sono invece memorizzati in un dataset a parte, come mostrato in Figura 2.8, time.
Fig. 2.8 – Esempio del dataset della quantità time
Pertanto il dataset associato ad una data quantità non riporta informazioni sulle righe o sulle colonne, ma solo l’indice (intero) della corrispondente entry rispettivamente della tabella generale dei tempi e della tabella, associata alla quantità, dei nodi (Figura 2.9).
Fig. 2.9 – Corrispondenza fra le entry dei dataset dei tempi e dei nodi (per una data quantità) con il dataset di una
quantità
Oltre al file di restart di Relap, che ricordiamo essere uno dei file di output del simulatore, all’interno del file HDF si è cercato di memorizzare tutte quelle informazioni utili a definire una simulazione in modo completo, perciò tutto l’insieme di file di input e output sia di Relap che di Dynetz, molti dei quali semplicemente importati all’interno di HDF senza ulteriori elaborazioni, ma come semplice copia di file (ovviamente con la definizione di metodi appositi per effettuare tale operazione su HDF). Si è cercato di definire un’organizzazione che fosse logica dal punto di vista del raggruppamento dei dati che riguardassero aspetti correlati tra loro ed al contempo consentisse una facile comprensione del file per una ricerca visiva dei dati di interesse.
2.3.1 Gerarchia dei dati all’interno del file HDF5 di simulazione
I dati all’interno del file di simulazione sono organizzati secondo la struttura in Figura 2.10, che mostra i gruppi costituenti la radice del file HDF5.
Fig. 2.10 – Struttura del file HDF5 La radice del file comprende i gruppi:
SIMULATIONS: contiene i dati di input e output dei simulatori Relap e Dynetz.
SIMULATORS: contiene i files e le librerie per avviare un processo di simulazione.
fileInfo: contiene informazioni generali sul file HDF; tali informazioni sono fornite dall’utente che genera il file (vedere il dettaglio nel seguito).
internal: contiene strutture dati per facilitare/migliorare le operazioni di lettura.
Nelle figure seguenti si può vedere il dettaglio di ogni gruppo della radice.
Fig. 2.11 – Contenuto del gruppo SIMULATIONS
DYNETZ: input e output del simulatore Dynetz
Fig. 2.12 – Contenuto del gruppo DYNETZ
IN: contiene il file di input
OUT: contiene il file di output
Il gruppo RELAP comprende due sottogruppi, IN e OUT ed il gruppo OUT è quello contenente i dati del file di restart del simulatore Relap stesso; come detto in precedenza e come si può vedere anche dalla complessità, il gruppo OUT è quello avente un maggior peso nella dimensione complessiva del file (per un file HDF da 1,8 GB contenente tutti i dati di simulazione, il peso dei dati contenuti nel gruppo OUT di RELAP è di oltre il 90%). La libreria di lettura è stata realizzata per recuperare i dati del file di simulazione contenuti in questo gruppo.
in: tutti i dati di input di Relap
o database: dati riguardanti le proprietà fisiche delle sostanze chimiche coinvolte nella simulazione.
o inputFiles: files di input di Relap; ad ogni restart della simulazione può essere associato un file di input con le nuove condizioni iniziali e con lo stesso nome del restart stesso.
o steady State: condizioni iniziali della simulazione.
out: tutti i dati di output di Relap
o info: informazioni riguardanti il file di restart, come la data della simulazione, il campo fileType che assicura che si tratti di un file di restart di Relap, il campo problemType con la descrizione del problema e la versione del simulatore.
o restart: contiene i dati dei diversi restart della simulazione restart<i>: <i> è l’indice del restart della
simulazione, i va da 0 a N-1, con N numero totale dei restart.
<category>: rappresenta la categoria alla quale appartiene una determinata quantità
(ad esempio la quantità p appartiene alla categoria volume).
o <quantity> group: è il gruppo contenente i dati di una determinata quantità; contiene due dataset, <quantity> con i valori assunti dalla quantità in forma di matrice con una riga per ogni istante di simulazione ed una colonna per ciascun nodo e nodes con i codici dei nodi in cui la quantità viene misurata.
Fig. 2.14 – Contenuto del gruppo fileInfo
Il gruppo fileInfo contiene i seguenti campi:
FileID: identificativo del file HDF5
HDF5FileAuthor: autore del file HDF5
caseStudyDescription: dettagli sul problema della simulazione
checksum: un valore che garantisce che il file non ha subito modifiche
note: note aggiuntive
originalHDFfileName: nome originale del file, nel caso in cui sia stato modificato
revision: indica se il file ha subito revisioni
revisionDescription: descrizione delle revisioni effettuate
simulationStartDate: data della simulazione
version: versione della struttura del file HDF
versionDescription: descizione della struttura del file HDF
Fig. 2.15 – Contenuto del gruppo internal
relap: contiene la lista ordinata di tutti i codici dei nodi di Relap e per ogni restart la lista dei nodi associati ad esso con le loro caratteristiche geometriche (in sottogruppi del gruppo relap).
dynetz: contiene i dati del file output di Dynetz divisi per campi. Ricordiamo che il file di output del simulatore Dynetz è memorizzato nel gruppo SIMULATIONS/DYNETZ/OUT. L’organizzazione prevista all’interno del gruppo internal/data_cache/dynetz è pensata per rendere automatiche le operazioni di lettura del file di output stesso.
data: dati di interesse del file out di Dynetz o note: note riguardanti uno specific evento o signal: i segnali misurati
o status: per un segnale può assumere il valore TRUE o FALSE
o times: passi temporali della simulazione
info: contiene un dataset con l’informazione sull’offser temporal iniziale della simulazione
timestep: una lista con tutti i valori dei tempi della simulazione
unknownOutputs: tutti i segnali trovati all’interno del file per cui non si era in possesso di una documentazione associata vengono riportati all’interno di questa tabella come aiuto per la ricerca di ulteriori informazioni future.