Parallel sparse Matrix- Vector and Matrix-Transpose- Vector multiplication using compressed sparse blocks Presentazione a cura di: Marco Cherubini, Andrea De Pirro, David Santucci, Andrea Tersigni, Luca Tracuzzi A. Buluc, J. T. Fineman, M. Frigo, J. R. Gilbert, C. E. Leiserson Calcolo Parallelo e Distribuito Anno Accademico 2009/2010 21 maggio 2010
56
Embed
Parallel Sparse Matrix Vector Multiplication Using CSB
This presentation (in italian) shows a parallel algorithm for matrix-vector multiplication using compressed sparse blocks, a very efficient way to perform huge matrix multiplications.
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Marco Cherubini, Andrea De Pirro, David Santucci,Andrea Tersigni, Luca Tracuzzi
A. Buluc, J. T. Fineman, M. Frigo, J. R. Gilbert, C. E. Leiserson
Calcolo Parallelo e DistribuitoAnno Accademico 2009/2010
21 maggio 2010
Sommario
• Formati di memorizzazione convenzionali
• Il nuovo formato CSB
• Moltiplicazione Matrice-Vettore con CSB
• Analisi della complessità
• Sperimentazione
Formati convenzionali: CSR
• Analizziamo alcuni formati di memorizzazione convenzionali
• Consideriamo matrici sparse n×n con nnz elementi non nulli
• CSR - Compressed Sparse Rowso Memorizzazione per righeo Efficiente: memorizza n+nnz indici o puntatori
per matrici sparse n×n con nnz elementi non nullio Adatto per y Axo Non adatto per y ATx
Formati convenzionali: CSR
Ax parallelo con CSR
CSR_SPMV (A, x, y)
1 nA.rows2 for i0 to n−1 in parallel3 do y[i]04 for kA.row_ptr[i] to A.row_ptr[i+1]−15 do y[i]y[i]+A.val[k]·x[A.col_ind[k]]
• val[nnz] : array dei valori non nulli della matrice (ordinati per righe)• col_ind[nnz] : indici di colonna degli elementi nell'array val• row_ptr[n] : puntatori al'inizio della riga n nell'array val
• Nota: ATx con CSC è analogo
Formati convenzionali: CSC
• CSC - Compressed Sparse Columnso Memorizzazione per colonneo Efficiente: memorizza n + nnz indici o puntatori o Adatto per y ATx
risoluzione di problemi di programmazione lineareo Non adatto per y Ax
Formati convenzionali: CSC
Il nuovo formato CSB
• Consideriamo matrici sparse n×n con nnz elementi non nulli
• β = block-size parameter o valore ideale = circa √no per semplicità si assume β potenza di 2
• CSB - Compressed Sparse Blockso Partizionamento della matrice in blocchi quadrati di
dimensione β × βo Numero di blocchi n2 / β 2
o Ordinamento Z-Morton interno ai blocchi o Sostiene y Ax e y ATx
Il nuovo formato CSB
Il nuovo formato CSB
Il nuovo formato CSB
Il nuovo formato CSB
Il nuovo formato CSB
Prod. Matrice-Vettore con CSB
CSB_SPMV (A, x, y)
1 for i0 to n/β−1 in parallel // riga di blocco2 do Initialize a dynamic array Ri
3 Ri[0]0 // Array di indici per // gli ultimi blocchi dei chunk
4 count0 // Contatore nnz in un chunk5 for j0 to n/β−2
6 do countcount+nnz(Ai,j)
7 if count+nnz(Ai,j+1) > O(β)8 then // Fine chunk
9 append j to Ri // Ultimo blocco del chunk10 count0
11 append n/β−1 to Ri
12 CSB_BLOCKROWV (A, i, Ri, x, y[(i∙β),…,((i+1)∙β)−1])
Prod. Matrice-Vettore con CSB
Divisione in chunk
-1
Prod. Matrice-Vettore con CSB
Divisione in chunk
-1
Prod. Matrice-Vettore con CSB
Divisione in chunk
-1
Prod. Matrice-Vettore con CSB
Divisione in chunk
-1
CSB_BLOCKROWV (A, i, R, x, y)
11 if R.length = 2 // Singolo chunk12 then ℓR[0]+1 // Blocco più a sinistra nel chunk13 rR[1] // Blocco più a destra nel chunk14 if ℓ = r15 then // Il chunk contiene un singolo blocco denso16 startA.blk_ptr[ f(i,ℓ)]17 endA.blk_ptr[ f(i,ℓ)+1]−118 CSB_BLOCKV (A, start, end, β, x, y)19 else // Il chunk è sparso20 multiply y(Ai,ℓ Ai,ℓ+1 … Ai,r)x serially
21 return
// Se la riga di blocchi contiene più chunk22 mid R.length/2 −1 ⌈ ⌉ // Divide i chunk in due sottoinsiemi // Calcola il punto di split per il vettore x23 xmid β·(R[mid]−R[0])24 Alloca un vettore z di cardinalità β, inizializzati a 025 in parallel26 do CSB_BLOCKROWV(A, i, R[0…mid], x[0…xmid−1], y)27 do CSB_BLOCKROWV(A, i, R[mid…R.length−1],
x[xmid…x.length−1], z)28 for k0 to β−129 do y[k]y[k]+z[k]
CSB_BLOCKROWV (A, i, R, x, y)
11 if R.length = 2 // Singolo chunk12 then ℓR[0]+1 // Blocco più a sinistra nel chunk13 rR[1] // Blocco più a destra nel chunk14 if ℓ = r15 then // Il chunk contiene un singolo blocco denso16 startA.blk_ptr[ f(i,ℓ)]17 endA.blk_ptr[ f(i,ℓ)+1]−118 CSB_BLOCKV (A, start, end, β, x, y)19 else // Il chunk è sparso20 multiply y(Ai,ℓAi,ℓ+1…Ai,r)x serially21 return
// Se la riga di blocchi contiene più chunk22 mid R.length/2 −1 ⌈ ⌉ // Divide i chunk in due sottoinsiemi // Calcola il punto di split per il vettore x23 xmid β·(R[mid]−R[0])24 Alloca un vettore z di cardinalità β, inizializzati a 025 in parallel26 do CSB_BLOCKROWV(A, i, R[0…mid], x[0…xmid−1], y)27 do CSB_BLOCKROWV(A, i, R[mid…R.length−1],
x[xmid…x.length−1], z)28 for k0 to β−129 do y[k]y[k]+z[k]
Prod. Matrice-Vettore con CSB
Split ricorsivo dei chunk
y[β..2β-1]
Prod. Matrice-Vettore con CSB
Split ricorsivo dei chunk
y[β..2β-1]
CSB_BLOCKV (A, start, end, dim, x, y)
28 if end−start ≤ O(dim)29 then // Calcola la computazione seriale y←y+Mx30 for kstart to end // A.val[start…end] è un blocco dim×dim31 do y[A.row_ind[k]] y[A.row_ind[k]] + A.val[k]·x[A.col_ind[k]]32 return
33 // Ricorsione: divide il blocco M in 4 quadranti 34 binary search start, start+1,…,end per il più piccolo s2
tale che (A.row_ind[s2] & dim/2) ≠ 0
35 binary search start, start+1,…,s2−1 per il più piccolo s1
tale che (A.col_ind[s1] & dim/2) ≠ 0
36 binary search s2, s2+1,…,end per il più piccolo s3
tale che (A.col_ind[s3] & dim/2) ≠ 0
37 in parallel38 do CSB_BLOCKV (A, start, s1−1, dim/2, x, y) // M00
39 do CSB_BLOCKV (A, s3, end, dim/2, x, y) // M11
40 in parallel41 do CSB_BLOCKV (A, s1, s2−1, dim/2, x, y) // M01
• Al fine di valutare la complessità dell'algoritmo definiamo:o work: denotato con T1, rappresenta il tempo di
esecuzione in una macchina monoprocessore monothread
o span: denotato con T∞, rappresenta il tempo di esecuzione con un infinito numero di processi o thread
• Viene definito grado di parallelismo il rapporto T1/T∞
Lemma 1
• Lemma 1: il formato CSR usa n∙log(nnz) + nnz∙log(n) bit di indici di supporto per una matrice n×n con nnz elementi non nulli.
• Dimostrazione: per indicizzare x elementi sono necessari log(x) bit. Dal prodotto righe-colonne risultano n∙log(nnz) bit per il row_ptr e nnz∙log(n) bit per col_ind.
Lemma 1 (continua)
Lemma 2
• Lemma 2: Il formato CSB usa (n2/β2)∙log(nnz)+2∙nnz∙log(β) bit di indici di supporto per una matrice n×n con nnz elementi non nulli.
• Dimostrazione: per ogni elemento in val, usiamo log(β) bit per rappresentare l'indice di riga e log(β) bit per rappresentare l'indice di colonna e richiede quindi nnz∙log(β) bit per ciascuno degli indici. Aggiungiamo lo spazio dato dall'array blk_ptr, ossia (n2/β2) ∙ log(nnz) bit.
Lemma 2 (continua)
Lemma 2 (continua)
Corollario 3
• Corollario 3: il formato CSB usa (n)∙log(nnz)+nnz∙log(n) bit di indici di supporto per una matrice n×n con nnz elementi non nulli, con β=√n.
Lemma 4• Lemma 4: Su un blocco di dimensioni β×β, contenente r
elementi non nulli, CSB_BlockV viene eseguito con work O(r) e span O(β).
• Dimostrazione (span):o lo span relativo alla moltiplicazione di una matrice
dim×dim può essere descritto da S(dim)=2∙S(dim/2)+O(log(dim))=O(dim). 2∙S(dim/2): viene invocata 2 volte in parallelo la
ricorsione su un singolo blocco di dim/2 O(log(dim)): costo della ricerca binaria dei tre indici
di split caso base = O(dim): il caso base è seriale su
O(dim) elementi ed è dominante sui casi ricorsivi
Lemma 4 (continua)
• Dimostrazione (work):o Consideriamo l'albero di grado 4 generato dalle
chiamate ricorsive della funzione CSB_BlockV; ogni nodo corrisponde alla computazione di un sottoblocco dim×dim, con dim=2h, e 0<h<log(β). Se un nodo è una foglia, allora verifica il caso base
ed ha sicuramente al più O(2h)=O(dim) elementi non nulli. Ne deriva quindi che il costo di computazione del nodo è O(r).
[..]
CSB_BLOCKV (A, start, end, dim, x, y)
28 if end−start ≤ O(dim)29 then // Calcola la computazione seriale y←y+Mx30 for kstart to end // A.val[start…end] è un blocco dim×dim31 do y[A.row_ind[k]] y[A.row_ind[k]] + A.val[k]·x[A.col_ind[k]]32 return
33 // Ricorsione: divide il blocco M in 4 quadranti 34 binary search start, start+1,…,end per il più piccolo s2
tale che (A.row_ind[s2] & dim/2) ≠ 0
35 binary search start, start+1,…,s2−1 per il più piccolo s1
tale che (A.col_ind[s1] & dim/2) ≠ 0
36 binary search s2, s2+1,…,end per il più piccolo s3
tale che (A.col_ind[s3] & dim/2) ≠ 0
37 in parallel38 do CSB_BLOCKV (A, start, s1−1, dim/2, x, y) // M00
39 do CSB_BLOCKV (A, s3, end, dim/2, x, y) // M11
40 in parallel41 do CSB_BLOCKV (A, s1, s2−1, dim/2, x, y) // M01
Lemma 4 (continua) [...] Se un nodo è interno, allora ha almeno O(dim) elementi
non nulli. Il costo computazionale del nodo, senza considerare
nodi figli, è pari a O(log(dim))=O(log(2h))=O(h), dovuto alla ricerca binaria.
I nodi generici di livello h sono al più O(r/dim), e quindi concorrono ad un lavoro complessivo per ogni livello pari a O(h∙r/dim).
sommando, per ogni h, nodi interni su tali livelli e nodi foglia, otteniamo O(r):
Lemma 5
• Questo lemma analizza la moltiplicazione fra una riga di blocchi e un vettore
• Lemma 5: Su una riga di blocchi contenente n/β blocchi e r elementi non nulli, CSB_BlockrowV viene eseguito con work O(r) e span O(β∙log(n/β)).
Lemma 5 (continua)
• Dimostrazione (work):o consideriamo la chiamata su una riga di blocco
partizionata in C chunk e definiamo W(C) il lavoro (work) eseguito. La funzione inizializza un vettore z di O(β) elementi e richiama ricorsivamente se stessa due volte, sulla metà dell'input
o Il lavoro è descritto dalla disequazioneW(C) ≤ 2∙W( C/2 )+O(β)⌈ ⌉da cui deriva W(C)=O(C∙β+r), poiché si hanno C attivazioni di complessità O(β), più i casi base di complessità O(r), ossia la computazione seriale del prodotto. Il numero C di chunk è, al più, pari a O(r/β) nel caso in cui r=O(β)
Lemma 5 (continua)
• Dimostrazione (span):o Lo span può essere descritto da S(C)=S( C/2 )⌈ ⌉
+O(β)=O(β∙log(C))+S(1)o Abbiamo che il caso base ha uno span pari O(β) sia nel
caso della moltiplicazione seriale che in quella nella chiamata CSB_BlockV
o Il caso base viene eseguito log(C) volte, con C ≤ n/βo Lo span complessivo è quindi O(β∙log(n/β))
Teorema 6
• Teorema 6: In una matrice n×n contenente r elementi non nulli, CSB_SpMV viene eseguito con un work O(r+n2/β2) e uno span di O(β∙lg(n/β)+n/β).
• Dimostrazione:o CSB_SpMV ricostruisce i chunk e avvia la funzione
CSB_BlockrowV. Il costo computazionale, per work e span deriva dal lemma precedente con l'aggiunta del costo necessario alla costruzione dei chunk.
o [...]
Teorema 6 (continua)
• Dimostrazione:o [...]o nel caso del work si aggiunge un costo pari a O(n2/β2)
dovuto all'analisi di una singola riga di blocchi di costo O(n/β) per un costo totale di O(r+n2/β2)
o nel caso dello span si aggiunge un costo pari a O(n/β) dato che è possibile parallelizzare l'operazione per ogni singola riga di blocchi, ottenendo un costo totale di O(β∙lg(n/β)+n/β)
Corollario 7 e 8
• Corollario 7: in una matrice n×n, contenente nnz ≥ n valori non nulli, scegliendo β=O(√n), CSB_SpMV lavora con un work di O(nnz) e uno span di O((√n)∙log(n)) raggiungendo un parallelismo di almeno
• O( nnz / (√n∙log(n)) )
• Corollario 8: in una matrice n×n, contenente nnz ≥ n valori non nulli, scegliendo β=O(√n), CSB_SpMV_T lavora con un work di O(nnz) e uno span di O(√n∙log(n)) raggiungendo un parallelismo di almeno
• O( nnz / (√n∙log(n)) )
Lemma 9• Lemma 9: In una matrice n×n, scegliendo β = O(√n), la
serializzazione di CSB_SpMV richiede uno spazio di O(√n∙log(n)) non contando lo spazio occupato dalla matrice stessa
• Dimostrazione: o lo spazio complessivo utilizzato è dato da due overhead
il primo, R, è l'array dei chunk, che, per ogni riga, utilizza uno spazio O(n/β). Dato che β=√n, si ha che lo spazio complessivo utilizzato è O(√n).
il secondo, z, è il vettore temporaneo di dimensione pari a β. A causa della profondità della ricorsione, lo spazio utilizzato è O(β∙log(n))=O(√n∙log(n))
la complessità finale è quindi O(√n∙log(n))+O(√n)=O(√n∙log(n))
Corollario 10
• Corollario 10: supponiamo un'esecuzione di CSB_SpMV per una matrice n×n con la scelta β=√n in un
work-stealing scheduler (preemptive round robin) con la proprietà busy-leaves. Allora l'esecuzione richiede uno spazio di O(P∙√n∙log(n)), con P pari al numero di processi utilizzati
Sperimentazione
• Scelta valore di β• Performance media di Ax e ATx• Risultati reali sulla scalabilità degli algoritmi
o matrici di medie dimensionio matrici di grandi dimensioni
Scelta del valore di β
• Si dimostra sperimentalmente che il valore ottimale di β deve rispettare la disequazione ⌈log(√n)⌉ ≤ log(β) ≤ 3+⌈log(√n)⌉
• L'utilizzo di β=√n per semplicità di calcolo rispetta tale vincolo
Performance media di Ax e ATx
• Sia CSB_SpMV che CSB_SpMV_T offrono ottime prestazioni all'aumentare del numero di processorio fino a 4 processori lo speedup cresce linearmenteo da 4 a 8 è meno che lineare, ma comunque crescenteo con più di 8, le prestazioni decadono a causa dei limiti
della memoria di sistema
Risultati sperimentali (dim media)
• I risultati sono stati condotti con matrici relative a problemi reali (e.g. elettroforesi DNA, meccanica strutturale,...)
Risultati sperimentali (dim grande)
• Per matrici grandi CSB scala in modo ottimale
CSR vs CSB
• Analizziamo il rapporto tra le performance di CSR e CSB nel caso di esecuzione parallela
• Per il confronto è stato usato l’algoritmo Star-P che usa CSR