B­alberi dizionari in memoria secondaria dizionari su memoria secondaria • la memorizzazione su memoria secondaria risponde a due esigenze – permanenza dell’informazione • la RAM è volatile – grande quantità di dati • la RAM è limitata • memoria secondaria: disco ASD ­ B­tree 2 modello di costo • il costo in termini di tempo per accedere al disco (scrittura/lettura) domina in maniera sostanziale il costo di elaborazione in RAM – parti meccaniche in movimento con costanti di tempo dell’ordine delle decine di millisecondi – una CPU esegue un’istruzione elementare in pochi colpi di clock – ad es., copia di un dato dalla RAM a un registro • supp. 10 colpi di clock, CPU a 1GHz • 10 ns per copia RAM­>registro • 20 ns per un’assegnazione ASD ­ B­tree 3 modello di costo/2 • Occorre considerare modelli di costo diversi dal modello RAM se il costo dominante della computazione è costituito dagli accessi in memoria secondaria. • Ex: # di accessi a disco x seek time + tempo di trasferimento dei dati da disco a memoria principale ASD ­ B­tree 4 modello di costo/3 • l’accesso al disco avviene per blocchi (o pagine fisiche ) cilindro faccia – tutti i blocchi hanno la stessa dimensione (da 512B ad alcuni KB) – ciascun blocco è definito da tre coordinate: cilindro (o traccia ), settore, faccia traccia ASD ­ B­tree settore 5 modello di costo/4 • tempo di accesso al disco = somma di tre componenti – seek time • per posizionare la testina sul cilindro corretto – latency time • attesa necessaria affinché il settore desiderato transiti sotto la testina – tempo di trasferimento • dati scambiati fra RAM e disco ASD ­ B­tree 6 esempio • seek time – ~15­20ms • latency time – valore atteso: 50% del tempo di rotazione • in un disco a 7200rpm, poco più di 4ms • tempo di trasferimento – velocità: alcuni MB/s • blocco di 4KB, seek 15 ms, 10000rpm, velocità di trasferimento 2MB/s • tempo (ms) di accesso al blocco ≈ 15 + 3 + 2 ms/blocco = 20ms/blocco • costo (ammortizzato) per byte ≈ 20ms/4096B ≈ 4.9µs/B ASD ­ B­tree 7 esempio/2 • nel caso di accesso a più blocchi contigui vengono pagati un solo seek e un solo latency • la convenienza aumenta all’aumentare del numero di blocchi contigui – “bulk access” vs. ”random access” • blocco di 4KB, seek 15 ms, 10000rpm, velocità di trasferimento 2MB/s • tempo (ms) di accesso a due blocchi contigui ≈ 15 + 3 + 2∙2 ms/blocco = 22ms/blocco • costo (ammortizzato) per byte ≈ 22ms/8192B ≈ 2.7µs/B ASD ­ B­tree 8 esempio/3 tempo accesso (ms) 120,00 100,00 ms 80,00 60,00 40,00 20,00 0,00 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 #blocchi contigui ASD ­ B­tree 9 ammortizzazione la contiguità premia tempo (microsec.) ammortizato per byte 6000,00 microsec. 5000,00 4000,00 3000,00 2000,00 1000,00 0,00 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 #blocchi contigui ASD ­ B­tree 10 modello di costo/5 • costo (tempo): # di I/O su disco – blocco ha dimensione B – RAM ha dimensione M – disco ha dimensione ∞ • tempo di CPU trascurabile – buona approssimazione in molti casi ASD ­ B­tree 11 dizionari in memoria secondaria idea: paginare un albero di ricerca 54 22 77 11 4 1 37 17 8 28 64 46 60 86 70 80 91 13 19 24 35 40 51 58 62 67 74 78 83 88 98 ASD ­ B­tree 12 BST paginato • l’albero non è più binario • si può definire un albero di ricerca m ­ario? ASD ­ B­tree 13 B­tree di ordine m • radice con 0 o p > 1 figli • ogni nodo interno (non radice) con k – 1 chiavi e k figli, m /2 k m – non lo stesso k per ciascun nodo! • foglie con k­1 chiavi, m /2 k m • albero di ricerca – ad ogni chiave è associato un sottoalbero destro di chiavi inferiori ed uno sinistro di chiavi superiori • m è il max numero di figli • Nota: le foglie contengono puntatori a blocchi di elementi ASD ­ B­tree 14 B­tree di ordine 4 56 22 41 2 14 26 34 66 87 44 46 51 59 61 71 77 90 92 98 / Nota: si sono indicate solo le chiavi è anche un B­tree di ordine 3? ASD ­ B­tree 15 scelte progettuali • • • • • un nodo = un blocco chiave c bit riferimento a sottoalbero r bit in ogni nodo rm + c (m ­ 1) bit m = (B + c )/(r + c ) – se B = 4KB, c = r = 32 bit m ≈ 64 • Ogni blocco di elementi è memorizzato in un blocco su disco (dim. B) – Se max. L bit per elemento al più B/L elem./blocco • Obiettivo: avere alberi di altezza piccola (es. 4) ASD ­ B­tree 16 Altezza di un B­tree • quali sono le altezze minime e massime di un B­tree di ordine m con n chiavi? – altezza max ottenuta quando ogni nodo interno ha il min numero di figli (albero spoglio) • la radice ha 2 figli ed ogni altro nodo interno ha m /2 figli – altezza min quando ogni nodo interno ha il max numero (m ) di figli (albero frondoso) ASD ­ B­tree 17 altezza di un B­tree spoglio • sia q = m /2 • 1 chiave nella radice • q ­ 1 chiavi in ogni altro nodo • livello 2: 2 nodi • livello 3: 2q nodi • livello 4: 2q 2 nodi • livello i : 2q i –2 nodi ASD ­ B­tree 18 altezza di un B­tree spoglio/2 • # chiavi in un B­tree spoglio di altezza h >= 12 q−12 q q−12 q 2 q−1⋯2 q h−2 q−1= h−2 i q h−1−1 =12 q−1 ∑i=0 q =12 q−1 =2 q h−1−1=n q−1 n1 ⇒ h=log q 1 p1 p 2 −1 i q D.: qual è l’altezza di un B­tree frondoso? ∑i=0 q =q−1 ASD ­ B­tree 19 Scelte progettuali/2 • • • • • un nodo = un blocco chiave c bit riferimento a sottoalbero r bit in ogni nodo rm + c (m ­ 1) bit m = (B + c )/(r + c ) – se B = 4KB, c = r = 32 bit m ≈ 64 – con n = 10ML chiavi h 6 E’ possibile avere alberi con altezza piccola in casi di interesse pratico – radice spesso mantenuta in RAM ASD ­ B­tree 20 rappresentazione nodi (RAM) class BTreeNode { int m; boolean leaf; /* true se nodo è foglia */ int keyTally; /* No. di chiavi presenti */ int keys = new int[m-1]; BTreeNode references[] = new BTreeNode[m]; BtreeNode(int key) {…} /* Costruttore */ } /* Nota: si assumono chiavi intere */ /* keys può essere un array di oggetti contenenti coppie <chiave. rif. a mem. secondaria> */ ASD ­ B­tree 21 Rappresentazione con riferimenti ASD ­ B­tree 22 cenno alla rappresentazione dei nodi su disco • file a sé stanti, ciascuno di un blocco – più semplice da realizzare – i riferimenti ai sottoalberi sono nomi di file – overhead per il sistema operativo • tutti in un unico file – soluzione compatta, ma più complessa – riferimenti ai sottoalberi • offset relativi all’inizio di un file (file frammentato, solo accessi random) • indirizzi assoluti (cilindro+settore+faccia, file non portatili) ASD ­ B­tree 23 ricerca in un B­tree public BTreeNode BTReeSearch(int key) { return BTreeSearch(key, root) } protected BTreeNode BTreeSearch(int key, BTreeNode node) { if (node != null) { int i=1; while ((i<=node.keyTally)&&(node.keys[i-1]< key)) { i++; if ((i>node.keyTally) || (node.keys[i-1]>key)) return BTreeSearch(key, nodereferences[i-1]; else return node; } else return null; } ASD ­ B­tree 24 Ricerca chiave di valore 51 56 22 41 2 14 26 34 66 87 44 46 51 59 61 ASD ­ B­tree 71 77 90 92 98 / 25 inserimento in B­tree • come nei BST, si effettua una ricerca della chiave da inserire • si tenta dapprima di inserire la chiave in una foglia (appropriata) – se la foglia non è piena il processo termina – se la foglia è piena (ha già m – 1 chiavi) abbiamo una situazione di overflow e possiamo scinderla in due • la scissione può determinare altre scissioni ASD ­ B­tree 26 Inserimento in foglia non piena Un albero B prima (a) e dopo (b) l’inserimento della chiave 7 in una foglia avente celle disponibili ASD ­ B­tree 27 Inserimento in foglia piena Inserimento della chiave 6 in una foglia piena ASD ­ B­tree 28 gestione degli overflow • gestione dell’overflow tramite scissione (o divisione o split) – allocazione di un nuovo nodo (foglia) – le m chiavi vengono così ripartite: (m – 1) / 2 nella foglia in overflow, (m – 1) / 2 nella nuova e una (la mediana fra le m ) viene inserita nel genitore per separare le due foglie (nell’esempio, m=5) • se il genitore va in overflow si usa la stessa tecnica ● ASD ­ B­tree 29 gestione degli overflow/2 • gli overflow si possono propagare verso l’alto fino a coinvolgere la radice • se la radice va in overflow questa deve venire scissa ed occorre creare una nuova radice, che conterrà la chiave mediana fra le m coinvolte nell’operazione – in questo caso l’albero aumenta la propria altezza • Animazione a http://shell.uriel.net/~mozart/File/btree.html ASD ­ B­tree 30 Inserimento Algorithm BTreeInsert(k) { <trova foglia in cui inserire k> /* Sia essa node */ <trova pos. adeguata per k nell’array keys> if (<nodo non pieno) { <inserisci k ed incrementa keyTally> return; } else { <suddividi node in node1 e node2>; <distribuisci chiavi e rif. equamente tra node1 e node2> <aggiorna node1.keyTally e node2.keyTally> <k = ultima chiave in node1> } /* Continua prossima slide */ ASD ­ B­tree 31 Inserimento /* Continua da slide precedente */ if (<node era la radice>) { <crea nuova radice con figli node1 e node2> <inserisci k e rif. a node1 e node2 nella radice> root.keyTally=1; return; } else <genitore di node2 = genitore di node> } ASD ­ B­tree 32 costo inserimento • discesa radice – foglia – O(logm/2 n ) I/O • split – O(1) I/O (3 o 4) • #split – O(logm/2 n ) • costo totale: O(logm/2 n ) ASD ­ B­tree 33 eliminazione da un B­tree • si effettua una ricerca della chiave da inserire • se la chiave è in una foglia, la si elimina dalla stessa e si verifica se il numero di chiavi rimanenti sia comunque non inferiore a m / 2 ­ 1 – se rimangono troppe poche chiavi si ha underflow, che richiede una gestione specifica • se la chiave è in un nodo interno la si sostituisce con il predecessore (o il successore), che è in una foglia, e ci si riconduce al caso precedente – simile alla tecnica usata nei BST ASD ­ B­tree 34 Azioni eseguite dopo l’eliminazione del numero 6 ASD ­ B­tree 35 gestione degli underflow • un nodo in underflow ha m / 2 ­ 2 chiavi • si tenta dapprima una ridistribuzione delle chiavi fra nodo e un fratello, coinvolgendo la chiave che li separa nel genitore – occorre un fratello con almeno m / 2 chiavi • se non è disponibile un fratello per operare la ridistribuzione occorre effettuare la fusione (o merge) fra nodo in underflow e un fratello – richiede una gestione specifica ASD ­ B­tree 36 fusione di nodi • due nodi fratelli possono essere fusi se uno di essi è in underflow e l’altro ha il numero di chiavi minimo m / 2 ­ 1 • la fusione consiste nell’inserire in un solo nodo tutte le chiavi presenti nei due nodi, oltre a quella del genitore che separava i due nodi – la fusione permette di liberare risorse precedentemente allocate a un nodo – richiede l’eliminazione di una chiave dal genitore, che a sua volta, può andare in underflow, divenendo oggetto di attenzione da parte del gestore dell’underflow ASD ­ B­tree 37 fusione di nodi/2 • se i nodi oggetto di fusione sono i due unici figli della radice, questa scompare e il risultato della fusione diviene la nuova radice – in tal caso vengono liberate risorse allocate a due nodi – l’albero diminuisce l’altezza • Animazione a http://shell.uriel.net/~mozart/File/btree. html ASD ­ B­tree 38 algoritmo di eliminazione Algorithm BTreeDelete(k) { node = BTreeSearch(k) if(node != null) { if(<node non è foglia>) { <trova foglia con successore s di k> <copia s su k in node> <node = foglia contenente s> <elimina s da node> } else <elimina k da node> ASD ­ B­tree 39 algoritmo di eliminazione/2 while(true) { if(<node non in underflow>) return; else if (<c’è un fratello di node con abbastanza chiavi>) { <redistribuisci chiavi fra node e fratello> return; } else if (<genitore(node) è radice>) { if (<radice ha una sola chiave>) <fondi node, fratello e genitore, formando nuova radice> ASD ­ B­tree 40 algoritmo di eliminazione/3 else <fondi node e suo fratello> return; } /* End else if */ else <fondi node e suo fratello> <node = genitore(node)> } } /* End while */ } /* End if */ } /* End algoritmo */ ASD ­ B­tree 41 costo eliminazione • discesa radice – foglia – O(logm/2 n ) I/O • redistribuzione – O(1) I/O (3 o 4) • fusione – O(1) I/O (3 o 4) • #fusioni – O(logm/2 n ) • costo totale: O(logm/2 n ) ASD ­ B­tree 42 B+­tree • chiavi solo nelle foglie • nodi interni contengono solo informazioni di “branching” e costituiscono un vero e prorpio indice • le foglie sono collegate orizzontalmente • algoritmi di gestione simili a quelli per il B­tree – una differenza notevole è nello split di una foglia: la chiave separatrice viene copiata (e non spostata) nel genitore ASD ­ B­tree 43