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 giugno 2002 ASD - B-tree 3 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 giugno 2002 ASD - B-tree 4 modello di costo/2 • non ha più senso attribuire un costo approssimativamente costante ad ogni operazione eseguita • il modello basato sull’operazione dominante è inadeguato giugno 2002 ASD - B-tree 5 modello di costo/3 • l’accesso al disco avviene per blocchi (o pagine fisiche ) – tutti i blocchi hanno la stessa dimensione (da 512B ad alcuni KB) – ciascun blocco è definito da tre coordinate: cilindro (o traccia ), settore, faccia giugno 2002 cilindro faccia traccia ASD - B-tree settore 6 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 giugno 2002 ASD - B-tree 7 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 giugno 2002 • 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 8 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” giugno 2002 • 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 9 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 giugno 2002 ASD - B-tree 10 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 giugno 2002 ASD - B-tree 11 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 giugno 2002 ASD - B-tree 12 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 giugno 2002 ASD - B-tree 13 BST paginato • l’albero non è più binario • si può definire un albero di ricerca m -ario? giugno 2002 ASD - B-tree 14 B-tree di ordine m • radice con 0 o 1 < p m 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 e tutte allo stesso livello • albero di ricerca – ad ogni chiave è associato un sottoalbero destro di chiavi inferiori ed uno sinistro di chiavi superiori •giugnom2002 è il max numeroASD di- B-tree figli 15 B-tree di ordine 4 56 22 41 2 14 26 34 66 87 44 46 51 59 61 71 77 90 92 98 è anche un B-tree di ordine 3? giugno 2002 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) giugno 2002 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 giugno 2002 ASD - B-tree 18 altezza di un B-tree spoglio/2 • # chiavi in un B-tree spoglio di altezza h 1 2(q 1) 2q (q 1) 2q 2 (q 1) 2q h 2 (q 1) q h 1 1 h 2 i 1 2(q 1)i 0 q 1 2(q 1) 2q h 1 1 n q 1 n 1 h logq 1 2 p 1 1 i 0 q q 1 p D.: qual è l’altezza di un B-tree frondoso? giugno 2002 ASD - B-tree i q 19 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 – con n = 10ML chiavi h 6 – radice spesso mantenuta in RAM giugno 2002 ASD - B-tree 20 rappresentazione nodi (RAM) class BTreeNode { int m; boolean leaf; int keyTally; int keys = new int[m-1]; BTreeNode references[] = new BTreeNode[m]; BtreeNode(int key) {…} } giugno 2002 ASD - B-tree 21 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) giugno 2002 ASD - B-tree 22 operazioni su un B-tree • le tre operazioni dei dizionari – membership, inserimento, cancellazione • interrogazioni di range – dato un intervallo (range), elencare o contare le chiavi che appartengono all’intervallo giugno 2002 ASD - B-tree 23 ricerca in un B-tree • si percorre un ramo partendo dalla radice, scendendo nei sottoalberi – come in un “normale” BST – costo logaritmico Algorithm BTreeSearch(key, node) { if(node != null) { int i = 1; for(; i <= node.keyTally && node.keys[i-1] < key; i++); if((i > node.keyTally) || node.keys[i-1] > key) return BTreeSearch(key, node.references[i-1]); else return node; } else return null; } giugno 2002 ASD - B-tree 24 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 giugno 2002 ASD - B-tree 25 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 • se il genitore va in overflow si usa la stessa tecnica giugno 2002 ASD - B-tree 26 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 • dimostrazione (tratta da http://shell.uriel.net/~mozart/File/btree.h tml) giugno 2002 ASD - B-tree 27 algoritmo di inserimento Algorithm BTreeInsert(k) { trova foglia f in cui inserire k while(true) { trova posizione adeguata per k in keys if(f non è piena) { inserisci k ed incrementa keyTally return } else { suddividi f in f1 e f2 distribuisci le chiavi ed i riferimenti in modo eguale fra f1 e f2 giugno 2002 ASD - B-tree 28 algoritmo di inserimento/2 inizializza i loro keyTally k = ultima chiave di f1 if(f era radice) { crea nuova radice, genitore di f1 e f2 inserisci k e i riferimenti a f1 e f2 in radice imposta keyTally di radice a 1 return } else f = genitore(f) } // fine while(true) } giugno 2002 ASD - B-tree 29 costo inserimento • discesa radice – foglia – O(log n ) I/O (log in base m /2) • split – O(1) I/O (3 o 4) • #split – O(log n ) • costo totale: O(log n ) giugno 2002 ASD - B-tree 30 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 giugno 2002 ASD - B-tree 31 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 giugno 2002 ASD - B-tree 32 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 giugno 2002 ASD - B-tree 33 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 • dimostrazione (tratta da http://shell.uriel.net/~mozart/File/btree.h tml) giugno 2002 ASD - B-tree 34 algoritmo di eliminazione Algorithm BTreeDelete(k) { node = BTreeSearch(k, root) 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 giugno 2002 ASD - B-tree 35 algoritmo di eliminazione/2 while(true) { if(node non in underflow) return else if (c’è un fratello di node con abbastanza chiavi) { ridistribuisci chiavi fra node e fratello return } else if (genitore(node) è radice) { if radice ha una chiave fondi node, fratello e genitore, formando nuova radice giugno 2002 ASD - B-tree 36 algoritmo di eliminazione/3 else fondi node e suo fratello return else { fondi node e suo fratello node = genitore(node) } } } } giugno 2002 ASD - B-tree 37 costo eliminazione • discesa radice – foglia – O(log n ) I/O (log in base m /2) • ridistribuzione – O(1) I/O (3 o 4) • fusione – O(1) I/O (3 o 4) • #fusioni – O(log n ) • costo totale: O(log n ) giugno 2002 ASD - B-tree 38 + 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 Btree – una differenza notevole è nello split di una foglia: la chiave separatrice viene copiata (e non spostata) nel genitore giugno 2002 ASD - B-tree 39