Your SlideShare is downloading. ×
Tesi Specialistica - Weka SMP
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Tesi Specialistica - Weka SMP

1,007

Published on

tesi specialistica su parallelizzazione multithread di algoritmi per il datamining

tesi specialistica su parallelizzazione multithread di algoritmi per il datamining

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
1,007
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
0
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. `UNIVERSITA CA’ FOSCARI – VENEZIAFacolt` di Scienze Matematiche, Fisiche e Naturali a Corso di Laurea in Informatica (Specialistica) Tesi di Laurea 11 Aprile 2008 Laureando: Fabio PustettoWekaSMP: Parallelizzazione Multi-Thread di una libreria Java per il Data MiningRelatore: Chiar.mo prof. Salvatore Orlando Anno Accademico 2006 - 2007 Via Torino 155 – 30173 Mestre - Venezia
  • 2. II
  • 3. “Se aumentiamo gradualmente il carico di una nave, possiamo anche cercare di distribuirlo in modo ottimale ma ad un certo punto la nave affonder`, a anche se avremo la consolazione di un affondamento ottimale.” Hermann Daly, economista della Sostenibilit`. aQuesta tesi ` stampata su carta riciclata in adesione alla campagna e “Scrittori per le foreste”, lanciata da III
  • 4. IV
  • 5. Indice1 Introduzione 1I Stato dell’arte 52 Data Mining 7 2.1 Regole Associative . . . . . . . . . . . . . . . . . . . . . . . . 8 2.1.1 Algoritmo Apriori . . . . . . . . . . . . . . . . . . . . 9 2.2 Clustering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.2.1 Algoritmo K-Means . . . . . . . . . . . . . . . . . . . 15 2.3 Weka . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 2.3.1 Algoritmo Apriori . . . . . . . . . . . . . . . . . . . . 20 2.3.2 Algoritmo K-Means . . . . . . . . . . . . . . . . . . . 223 Architetture e Programmazione Parallela 25 3.1 Definizioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 3.2 Architetture . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 3.3 Gestione della memoria . . . . . . . . . . . . . . . . . . . . . 31 3.3.1 Memoria Cache . . . . . . . . . . . . . . . . . . . . . . 33 3.4 Sincronizzazione . . . . . . . . . . . . . . . . . . . . . . . . . 35 3.5 Multi core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 3.5.1 Realizzazione . . . . . . . . . . . . . . . . . . . . . . . 39 3.6 Multithreading in Java . . . . . . . . . . . . . . . . . . . . . . 44 3.6.1 Processi . . . . . . . . . . . . . . . . . . . . . . . . . . 46 3.6.2 Thread . . . . . . . . . . . . . . . . . . . . . . . . . . 47 3.6.3 Sincronizzazione . . . . . . . . . . . . . . . . . . . . . 49 V
  • 6. 4 Regole Associative e Clustering: algoritmi paralleli 57 4.1 Regole associative . . . . . . . . . . . . . . . . . . . . . . . . 60 4.1.1 Versioni parallele a memoria distribuita . . . . . . . . 62 4.1.2 Versioni parallele a memoria condivisa . . . . . . . . . 66 4.1.3 Risultati sperimentali . . . . . . . . . . . . . . . . . . 67 4.1.4 Problemi Aperti . . . . . . . . . . . . . . . . . . . . . 71 4.2 Clustering . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 4.2.1 Versioni parallele a memoria distribuita . . . . . . . . 74 4.2.2 Versioni parallele a memoria condivisa . . . . . . . . . 76 4.2.3 Risultati Sperimentali . . . . . . . . . . . . . . . . . . 76II WekaSMP 795 Analisi degli algoritmi di WekaSMP 81 5.1 Regole Associative: AprioriSMP . . . . . . . . . . . . . . . . 82 5.2 Clustering: K-MeansSMP . . . . . . . . . . . . . . . . . . . . 836 Realizzazione del prototipo WekaSMP 87 6.1 Core . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 6.2 Regole Associative: AprioriSMP . . . . . . . . . . . . . . . . 89 6.3 Clustering: K-MeansSMP . . . . . . . . . . . . . . . . . . . . 967 Risultati Sperimentali 103 7.1 Preliminari . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 7.2 AprioriSMP . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 7.2.1 Dataset . . . . . . . . . . . . . . . . . . . . . . . . . . 107 7.2.2 Test contatori condivisi . . . . . . . . . . . . . . . . . 109 7.2.3 Test contatori privati . . . . . . . . . . . . . . . . . . . 113 7.2.4 Commenti . . . . . . . . . . . . . . . . . . . . . . . . . 120 7.3 K-MeansSMP . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 7.3.1 Dataset . . . . . . . . . . . . . . . . . . . . . . . . . . 125 7.3.2 Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 7.3.3 Commenti . . . . . . . . . . . . . . . . . . . . . . . . . 1318 Conclusioni 135 VI
  • 7. III Allegati 141A Script 143B Core 147 B.1 Instances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 B.2 Instance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152C AprioriSMP 155 C.1 Apriori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 C.2 CountDistribution . . . . . . . . . . . . . . . . . . . . . . . . 163D K-MeansSMP 165 D.1 SimpleKMeans . . . . . . . . . . . . . . . . . . . . . . . . . . 165 D.2 K-MeansSMP . . . . . . . . . . . . . . . . . . . . . . . . . . . 174Bibliografia 181 VII
  • 8. VIII
  • 9. Elenco delle figure 2.1 Algoritmo Apriori [AS94] . . . . . . . . . . . . . . . . . . . . 10 2.2 Algoritmo Apriori, funzione apriori gen [AS94] . . . . . . . . 10 2.3 Algoritmo Apriori, eliminzione dei non frequenti (prune) [AS94] 11 2.4 Inserimento di candidati dalla radice dell’hash tree [HKK97] . 12 2.5 Inserimento di candidati dal sottoalbero sinistro dell’hash tree [HKK97] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.6 Esempio di clusterizzazione gerarchica a dendrogramma . . . 14 2.7 Esempio di clusterizzazione partizionale . . . . . . . . . . . . 15 2.8 Algoritmo K-Means sequenziale [DM00] . . . . . . . . . . . . 17 2.9 Esempio di clusterizzazione di punti spaziali . . . . . . . . . . 18 2.10 Esempio di database in formato ARFF . . . . . . . . . . . . . 20 2.11 Output dell’algoritmo Apriori su Weka . . . . . . . . . . . . . 22 2.12 Output dell’algoritmo K-Means su Weka . . . . . . . . . . . . 24 3.1 Tassonomia di Flynn . . . . . . . . . . . . . . . . . . . . . . . 27 3.2 Architettura SIMD . . . . . . . . . . . . . . . . . . . . . . . . 28 3.3 Architettura SIMD . . . . . . . . . . . . . . . . . . . . . . . . 28 3.4 Architettura MISD . . . . . . . . . . . . . . . . . . . . . . . . 29 3.5 Architettura MIMD . . . . . . . . . . . . . . . . . . . . . . . 29 3.6 Esempio di architettura quad core: AMD opteron [amd] . . . 30 3.7 Esempio di architettura della memoria UMA . . . . . . . . . 32 3.8 Esempio di architettura della memoria NUMA . . . . . . . . 33 3.9 Intel Pentium D Smithfield, primo dual core a die singolo [int] 39 3.10 Intel Pentium D Presler, primo dual core a die doppio [int] . 41 3.11 Intel Core 2 Duo, primo dual core a monolitico [int] . . . . . 42 3.12 Intel Xeon quad core [int] . . . . . . . . . . . . . . . . . . . . 43 3.13 Confronto tra l’architettura die singolo e monolitica [int] . . . 43 IX
  • 10. 3.14 Esempio di processo multithreading su processore quad-core . 453.15 Grafo di transizione dei processi . . . . . . . . . . . . . . . . . 473.16 Sincronizzazione per l’accesso ad una risorsa . . . . . . . . . . 514.1 Struttura degli algoritmi di Data Mining pi` comuni [JA02] . u 584.2 Utilizzo della memoria nei vari schemi di Locking [JA02] . . . 594.3 Compromessi tra le varie tecniche [JA02] . . . . . . . . . . . . 604.4 Algoritmi paralleli per la scoperta di Regole Associative [Zak99] 614.5 Algoritmo Count Distribution [HKK97] . . . . . . . . . . . . 634.6 Algoritmo Data Distribution [HKK97] . . . . . . . . . . . . . 644.7 Confronto scalabilit` algortimi paralleli [HKK97] . . . . . . . a 674.8 Confronto speedup algortimi paralleli [HKK97] . . . . . . . . 684.9 Confronto tempi di risposta algortimi paralleli [HKK97] . . . 694.10 Propriet` del database per i test dell’algoritmo CCPD [ZOPL96] 70 a4.11 Speedup dell’algoritmo CCPD senza e con lettura del data- base [ZOPL96] . . . . . . . . . . . . . . . . . . . . . . . . . . 704.12 Tempi di lettura del database [ZOPL96] . . . . . . . . . . . . 714.13 Algoritmo K-Means sequenziale [DM00] . . . . . . . . . . . . 754.14 Scelta dei primi centroidi su algoritmo K-Means . . . . . . . . 764.15 Prestazioni K-Means parallelo con n = 221 , d = 8 e k = 8 [DM00] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 774.16 Prestazioni K-Means parallelo con n = 221 , d = 8 e k = 8 [JA02] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 776.1 Confronto Count Distribution SM e DM . . . . . . . . . . . . 906.2 Diagramma di flusso algoritmo AprioriSMP . . . . . . . . . . 916.3 Diagramma delle classi, algoritmo Apriori . . . . . . . . . . . 936.4 Diagramma di flusso kmeansSMP . . . . . . . . . . . . . . . . 986.5 Diagramma delle classi, algoritmo kMeansSMP . . . . . . . . 997.1 Esempio di rilevamento cache miss con VTune . . . . . . . . . 1057.2 Esempio output AprioriSMP nei test . . . . . . . . . . . . . . 1067.3 Esempio rilevamento tempi AprioriSMP nei test . . . . . . . 1077.4 Struttura del dataset Mushroom . . . . . . . . . . . . . . . . 1087.5 Descrizione . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 X
  • 11. 7.6 Tempi di risposta dell’algoritmo AprioriSMP con contatori condivisi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1147.7 Speedup dell’algoritmo AprioriSMP con contatori condivisi . 1157.8 Speedup del conteggio dei frequenti con contatori condivisi . 1167.9 Tempi di risposta dell’algoritmo AprioriSMP con contatori privati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1197.10 Speedup dell’algoritmo AprioriSMP con contatori privati . . . 1207.11 Speedup del conteggio dei frequenti con contatori privati . . . 1217.12 Confronto tempi di risposta versioni AprioriSMP . . . . . . . 1227.13 Confronto speedup versioni AprioriSMP . . . . . . . . . . . . 1237.14 Scalabilit` dell’algoritmo AprioriSMP . . . . . . . . . . . . . 124 a7.15 Esempio rilevamento dei tempi test K-MeansSMP . . . . . . 1257.16 Esempio output dei test K-MeansSMP . . . . . . . . . . . . . 1267.17 Struttura del dataset Iris . . . . . . . . . . . . . . . . . . . . 1277.18 Tempi di risposta K-MeansSMP variando il numero di pro- cessori . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1317.19 Tempi di risposta K-MeansSMP variando la dimensione del database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1327.20 Scalabilit` K-MeansSMP . . . . . . . . . . . . . . . . . . . . . 133 a7.21 Speedup K-MeansSMP . . . . . . . . . . . . . . . . . . . . . . 134 XI
  • 12. XII
  • 13. Elenco delle tabelle 2.1 Transazioni di un supermercato . . . . . . . . . . . . . . . . . 8 2.2 Simboli utilizzati per lo studio di K-Means . . . . . . . . . . . 16 2.3 Principali opzioni per l’algoritmo Apriori su Weka . . . . . . 21 2.4 Principali opzioni per l’algoritmo K-Means su Weka . . . . . 23 5.1 Simboli utilizzati nell’analisi teorica di Apriori . . . . . . . . 82 5.2 Simboli utilizzati nell’analisi teorica di K-Means . . . . . . . 83 6.1 Tipi pi` importanti nella classe Apriori u . . . . . . . . . . . . 88 7.1 Parametri test AprioriSMP . . . . . . . . . . . . . . . . . . . 105 7.2 Tempi di risposta AprioriSMP, contatori condivisi, database 50 MByte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 7.3 Indici prestazionali AprioriSMP, contatori condivisi, database 50 MByte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 7.4 Tempi di risposta AprioriSMP, contatori condivisi, database 200 MByte . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 7.5 Indici prestazionali AprioriSMP, contatori condivisi, database 200 MByte . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 7.6 Tempi di risposta AprioriSMP, contatori condivisi, database 500 MByte . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 7.7 Speedup AprioriSMP, contatori condivisi, database 500 MByte113 7.8 Tempi di risposta AprioriSMP, contatori privati, database 50 MByte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 7.9 Indici prestazionali AprioriSMP, contatori privati, database 50 MByte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 7.10 Tempi di risposta AprioriSMP, contatori privati, database 200 MByte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 XIII
  • 14. 7.11 Speedup AprioriSMP, contatori privati, database 200 MByte 1177.12 Tempi di risposta AprioriSMP, contatori privati, database 500 MByte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1187.13 Indici prestazionali AprioriSMP, contatori privati, database 500 MByte . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1187.14 Cache Miss AprioriSMP . . . . . . . . . . . . . . . . . . . . . 1237.15 Parametri test K-MeansSMP . . . . . . . . . . . . . . . . . . 1247.16 Tempi di risposta K-MeansSMP, database 25 MByte . . . . . 1287.17 Indici prestazionali K-MeansSMP, database 25 MByte . . . . 1287.18 Tempi di risposta K-MeansSMP, database 50 MByte . . . . . 1297.19 Indici prestazionali K-MeansSMP, database 50 MByte . . . . 1297.20 Tempi di risposta K-MeansSMP, database 200 MByte . . . . 1307.21 Indici prestazionali K-MeansSMP, database 200 MByte . . . 1307.22 Cache Miss K-MeansSMP . . . . . . . . . . . . . . . . . . . . 133 XIV
  • 15. SommarioL’idea base delle architetture parallele ` che diversi processori cooperino enella soluzione di un singolo problema. Il motivo che spinge ad occuparsidi calcolo parallelo ` proprio il tentativo di velocizzare il processo di calco- elo della soluzione dei problemi computazionali. In accordo con la legge diMoore ` stato ormai raggiunto il limite fisico per l’aumento della frequenza edei processori e vengono rilasciati i primi sistemi Multi-Core: pi` unit` di u acalcolo nello stesso processore che cooperano nel raggiungimento del risul-tato e una sola memoria condivisa. Per far fronte all’esigenza di sfruttare ilpatrimonio informativo contenuto nelle grandi raccolte di dati che abbiamoa disposizione, si utilizzano algoritmi di Data Mining per estrarre infor-mazione implicita da dati gi` strutturati, esplorare ed analizzare in modo aautomatico grandi quantit` di dati allo scopo di scoprire regole o insiemi asignificativi. Lo scopo di questa tesi ` studiare e fornire algoritmi di Data eMining paralleli efficienti per questo nuovo tipo di architetture, basandosisulle librerie Java del progetto Weka. L’implementazione parallela da luogoal prototipo WekaSMP, base di partenza per la parallelizzazione di tutti glialgoritmi in essa contenuti. L’analisi dei risultati, infine, evidenzia come siapossibile ridurre il tempo di computazione in modo sensibile sfruttando inmodo opportuno tutte le unit` di calcolo. a
  • 16. Capitolo 1Introduzione “Un giorno le macchine riusciranno a risolvere tutti i problemi, ma mai nessuna di esse potr` porne uno” a Albert Einstein (1879-1955), fisico tedesco. 1
  • 17. Con la disponibilit` di grandi basi di dati in molte aree applicative come abioinformatica, medicina, analisi scientifiche e finanziarie, telecomunicazio-ni, vendite al dettaglio e marketing, sta diventando sempre pi` necessario ueseguire applicazioni Data Mining in parallelo. Allo stesso tempo il progressotecnologico ha reso accessibile a tutti le macchine parallele a memoria con-divisa, sia per l’industria che per il privato. Se da un lato la profonda inno-vazione introdotta con queste architettura porta ad un effettivo incrementodelle prestazioni in ambienti multitasking, dall’altro obbliga i programma-tori a sviluppare algoritmi paralleli su misura. In questo senso la velocit` adi sviluppo di queste nuove architetture non ` stata mantenuta anche dal- el’implementazione di nuovi algoritmi. Ricordiamo una scomoda verit`: un aalgoritmo sequenziale eseguito su un processore multi-core di nuova genera-zione, basato su un’architettura parallela a memoria condivisa, non godr` adi nessun incremento di prestazioni. In questo elaborato ci si focalizza sulla parallelizzazione ad hoc di alcu-ne librerie Java per il Data Mining sviluppate in ambito accademico pressola facolt` di informatica dell’universit` neozelandese di Waikato, conosciuto a acome progetto Weka. Lo sviluppo di questo nuovo tool deve essere paralleloed efficiente su macchine a memoria condivisa, tale da garantire un incre-mento di prestazioni adeguato se confrontato con le versioni sequenziali oparallele per macchine distribuite, esistenti. Ecco come si presenta questo elaborato. Nel capitolo 2 si presentanole basi teoriche delle discipline per la scoperta di regole associative e pro-cessi di clustering sulle quali sono fondate le applicazioni di Data Miningche stiamo esplorando e un’approfondimento sul progetto Weka alla relea-se attuale. Nel capitolo 3 si presentano le innovazioni tecnologiche che cihanno spinto a sviluppare gli algoritmi qui presentati, in modo particolarele nuove architetture parallele multi-core, mentre il capitolo 4 ` dedicato ead un’ampia panoramica della letteratura allo stato dell’arte per quanto ri-guarda gli algoritmi paralleli pi` famosi per la ricerca di regole associative e udi clustering, la maggior parte dei quali per sistemi a memoria distribuita,dimostrazione del fatto che sono ancora pochi, o in stato embrionale, quellia memoria condivisa. Nella seconda parte della tesi si presenta il lavoro effettivamente svolto,dallo studio allo sviluppo ai test, per ottenere l’implementazione finale del 2
  • 18. prototipo presentato con il nome di WekaSMP (Waikato Environment forKnowledge Analysis on Shared Memory Processors). Nel capitolo 5 si stu-diano formalmente gli algoritmi da sviluppare per stimarne la complessit` atemporale e gli incrementi di prestazioni mentre nel capitolo 6 si presen-tano gli algoritmi, sviluppati in Java, includendo porzioni di pseudocodicerappresentativi del codice sorgente delle funzioni pricipali sviluppate e de-scrivendone il loro funzionamento. Gli algoritmi sviluppati vengono testatisu un computer composto da 2 processori quad-core, rilevati i tempi di ri-sposta ed elaborati in indici e statistiche, presentate nel capitolo 7 sia informa tabellare che grafica. Il capitolo 8, in fine, contiene le conclusioni egli ultimi commenti sul lavoro. L’ultima sezione ` dedicata alla stampa del codice sorgente delle funzioni eprincipali e di maggior interesse, elaborate in questo studio1 . 1 Tutti i diritti riservati. Marchi registrati e segni distintivi citati in questo elaboratosono di propriet` dei rispettivi titolari, in particolare: a • Sun, Sun Microsystems, il logo Sun, Java, Java Virtual Machine, JavaServer Pages, JavaScript, JDK, JRE, JSP, JVM, NetBeans, sono marchi o marchi registrati della Sun Microsystems, Inc. negli Stati Uniti e in altri paesi. • Intel, i386, i486, Pentium, Xeon e VTune sono marchi o marchi registrati della Intel Corporation o delle sue sussidiarie negli Stati Uniti e in altri paesi. • AMD, the AMD Arrow logo, 3DNow!, PowerNow!, Athlon, Opteron, logos and combinations thereof, are trademarks of Advanced Micro Devices, Inc. • Linux ` un marchio registrato di Linus Torvalds. e • Ubuntu e Canonical sono marchi registrati da Canonical Ltd. 3
  • 19. 4
  • 20. Parte IStato dell’arte 5
  • 21. Capitolo 2Data Mining “Il segreto degli affari ` sapere qualcosa che nessun altro sa” e Aristotelis Sokratis Onassis (1906-1975), armatore e finanziere greco. 7
  • 22. Il data mining o, come si preferisce chiamarlo di recente, la KnowledgeDiscovery in Databases (KDD), ` una disciplina nata alla fine degli anni ’80. eIl motivo alla base dell’interesse per questa disciplina risiede nel fatto chenegli ultimi decenni la capacit` dell’uomo di generare e collezionare dati ` a eincrementata notevolmente. Nelle applicazioni reali spesso ` necessario estrarre dati da diversi dipar- etimenti. Per esempio in un’applicazione di marketing si pu` voler integrare odati dall’ufficio vendite, dall’ufficio acquisti e dall’ufficio clienti ma ognunodi essi probabilmente differisce nel modo di immagazzinare tali dati, oltreche nelle convenzioni, stili, chiavi e regole. I dati devono essere assemblati,integrati e puliti. Questa idea di ampia integrazione del database aziendale` conosciuta come Data Warehousing [WF05]. Questa integrazione forni-esce un unico punto di accesso ai dati aziendali ed essi verranno poi usatiper prendere decisioni di business, eventualmente integrati con dati esterni(Overlay Data) che l’azienda non memorizza (ad esempio i dati meteo).2.1 Regole AssociativeSia T l’insieme di transazioni, dove ognuna di esse ` un sottoinsieme dell’in- esieme di articoli (item-set) I. Sia C un sottoinsieme di I allora si definiscesupporto di C rispetto a T σ(C) = |{t|t ∈ T, C ⊆ t}|.dove σ(C) ` il numero di transazioni che contengono C. Per esempio conside- erare un insieme di transazioni di un supermercato come illustrato in tabella2.1. L’insieme degli articoli I per questa transazione ` {Pane, Birra, Cola, e Tabella 2.1: Transazioni di un supermercato TID Articoli 1 Pane, Cola, Latte 2 Birra, Pane 3 Birra, Cola, Pannolini, Latte 4 Birra, Pane, Pannolini, Latte 5 Cola, Pannolini, LattePannolini, Latte} mentre il supporto di {Pannolini, Latte} ` σ(Pannolini, e 8
  • 23. Latte) = 3 ed il supporto di {Pannolini, Latte, Birra} ` σ(Pannolini, Latte, eBirra) = 2. s,α Una regola associativa ` un’espressione della forma X ⇒ Y , dove X ⊆ I e s,αe Y ⊆ I. Il supporto s della regola X ⇒ Y ` definito come σ(X ∪ Y )/|T | e la econfidenza α ` definita come σ(X ∪ Y )/σ(X). Per esempio considerando la eregola {Pannolini, Latte} ⇒ {Birra}, la presenza di pannolini e latte nellatransazione tendono ad indicare, nella stessa transazione, la presenza di bir-ra. Il supporto di questa transazione ` σ(P annolini, Latte, Birra)/5 = 40% ementre la confidenza ` σ(P annolini, Latte, Birra)/σ(P annolini, Latte) = e66%. Ovviamente regole con alta confidenza sono molto importanti poi-ch` forniscono spesso un’accurata previsione sull’associazione degli articoli enella regola. Anche il supporto di una regola ` fondamentale, considera- eto che indica quanto frequente ` la regola in una transazione. Regole con ebasso supporto non sono significative, motivo per cui alcuni algoritmi (trai quali Apriori) [AS94] tralasciano le regole che non soddisfino una condi-zione di supporto minimo per ridurre le associazioni derivate, consideratoche il numero di regole possibili ` proporzionale al numero di sottoinsiemi edell’insieme I degli articoli, come 2|I| . La scoperta di regole associative consiste nel trovare tutte le regole s,αX ⇒ Y tali che s soddisfi una soglia di supporto minimo e α soddisfi unasoglia di confidenza minima. Questo processo ` suddiviso in due parti: e 1. scoprire tutti i sottoinsiemi frequenti (insiemi candidati che hanno supporto minimo superiore alla soglia stabilita); 2. generare le regole associative da questi sottoinsiemi.La fase computazionalmente pi` costosa ` la prima e numerosi sono gli u ealgoritmi sviluppati per risolverla, tra i quali uno dei pi` conosciuti ` Apriori. u e2.1.1 Algoritmo AprioriL’algoritmo Apriori ` stato proposto nel 1994 da Rakesh Agrawal [AS94]. In efigura 2.1 sono riassunti i passi principali. Inizialmente L1 contiene tutti glioggetti (sottoinsiemi di dimensione 1) che soddisfano il supporto minimo: ilprimo passo infatti conta semplicemente le occorrenze di ogni articolo neldatabase ed elimina in L1 quelle che non soddisfano il supporto minimo. Il 9
  • 24. Figura 2.1: Algoritmo Apriori [AS94]passo successivo, detto k, consiste di due fasi: nella prima l’insieme frequenteLk−1 , trovato al passo (k-1), viene usato per trovare l’insieme candidato Ck ,usando la funzione apriori gen, descritta sotto. La seconda fase consiste nelcontare le occorrenze dei candidati in Ck nel database di transazioni pertrovarne il supporto. Infine, dall’insieme dei candidati Ck si eliminano leoccorrenze che non soddisfano il supporto minimo per trovare l’insieme deifrequenti Lk . L’unione di tutti questi insiemi ` il risultato finale. e Per generare gli insiemi candidati, la funzione apriori gen richiede iningresso l’insieme degli articoli frequenti al passo (k-1). In uscita si ha unsuperinsieme contenente gli oggetti frequenti di lunghezza k. La funzione sidivide in due fasi. Nella prima fase (join) si moltiplica per se stesso l’insiemeLk−1 , ottenendo l’insieme candidato Ck , vedi figura 2.2 Nella seconda fase Figura 2.2: Algoritmo Apriori, funzione apriori gen [AS94](prune), vedi figura 2.3, si eliminano tutte le occorrenze c ∈ Ck tali che qual- ´siasi loro sottoinsieme di dimensione (k-1) non sia in Lk−1 . E importantenotare gi` a questo punto come l’operazione pi` pesante di tutto l’algorit- a umo sia il conteggio dei supporti. L’algoritmo parallelo Count Distribution 10
  • 25. Figura 2.3: Algoritmo Apriori, eliminzione dei non frequenti (prune) [AS94](vedi paragrafo 4.1.1) si propone proprio di suddividere tra i processori que-sto compito. Un primo approccio naive per il contenggio dei supporti ` il eclassico string-matching di ogni insieme di articoli nell’insieme dei candidatinell’insieme delle transazioni ma trie [Bod03] ed hash tree [HKK97], usatianche in Weka, garantiscono performance molto pi` veloci, uHash TreeLa figura 2.4 mostra un esempio di hash tree contenente l’insieme candidatodi dimensione 3. I nodi interni hanno hash tables contenenti link ai nodifigli, i nodi foglia contengono gli insiemi candidati. La costruzione di un hash tree avviene come segue. Inizialmente l’hashtree contiene solo il nodo root, che ` anche il nodo foglia ma che non con- etiene insiemi candidati. Quando un insieme candidato viene generato, glioggetti dell’insieme vengono memorizzati in modo ordinato, cosa che per-metter` una maggiore efficienza nel generare sia l’insieme dei frequenti che ail successivo insieme candidato. Ogni insieme candidato viene inserito nel-l’albero codificando ogni oggetto successivo nei nodi interni e seguendo icollegamenti delle tabelle hash. Quando si raggiunge una foglia l’insiemecandidato viene inserito come foglia anch’esso se il numero di foglie ` mi- enore del massimo consentito, altrimenti il nodo foglia viene convertito innodo interno e vengono creati nodi figli per il nuovo nodo. Gli insiemi can-didati vengono distribuiti nei nodi foglia secondo i valori hash degli oggetticontenuti. Nell’esempio di figura 2.4, l’insieme candidato {1 2 4} ` inserito ecodificando l’oggetto 1 nel nodo root per raggiungere il figlio sinistro; co-dificando l’oggetto 2 su quel nodo si raggiunge il figlio centrale ed infinecodificando l’oggetto 4 si raggiunge il figlio sinistro, che ` una foglia. e L’operazione di subset attraversa l’albero a partire dalla radice e conogni oggetto di una transazione come possibile oggetto iniziale del candida- 11
  • 26. Figura 2.4: Inserimento di candidati dalla radice dell’hash tree [HKK97]to. Nel livello successivo, tutti gli oggetti che seguono quello iniziale dellatransazione vengono codificati nella funzione hash. Questo ` fatto ricorsiva- emente fino a che viene raggiunta una foglia. A questo punto tutti i candidatiinseriti nelle foglie vengono confrontati con la transazione ed i loro contatorivengono aggiornati. Nell’esempio di figura 2.4 l’operazione viene effettuataa partire dalla radice con la transazione {1 2 3 5 6}. L’oggetto 1 ` codifica- eto verso il figlio sinistro della radice e la transazione rimanente {2 3 5 6} ` eapplicata ricorsivamente a questo nodo. L’oggetto 2 ` codificato come figlio ecentrale del nodo e cos` via, secondo la funzione di hash specifica. ı 12
  • 27. Figura 2.5: Inserimento di candidati dal sottoalbero sinistro dell’hash tree[HKK97] La figura 2.5 mostra la stessa operazione effettuata in precedenza, sulsottoalbero sinistro. I figli 2 e 5 vengono codificati nei figli centrali del nodoe quindi le rimanenti transazioni {3 5 6} e {6} vengono applicate ricorsiva-mente a questo nodo centrale. L’oggetto 3, invece viene mappato nel figliodestro del nodo e la rimanente transazione {5 6} ` applicata ricorsivamente ea questo figlio destro.2.2 ClusteringSi definisce clustering il processo di partizionamento o raggruppamento di undato insieme di oggetti in sottoinsiemi disgiunti. Tutti gli oggetti apparte-nenti allo stesso cluster avranno caratteristiche simili tra loro e il pi` diverse upossibile rispetto agli oggetti appartenenti alle altre partizioni (massimizzarela similiarit` intra-cluster e minimizzare la similiarit` inter-cluster). a a Questo tipo di algoritmi ` usato spesso in applicazioni nei pi` svariati e u 13
  • 28. campi, dalla visualizzazione al pattern recognition, dalle applicazioni grafi-che alle reti neurali passando per intelligenza artificiale e statistica. Appli-cazioni pratiche comprendono anche classificazione unsupervised e genera-zione di tassonomie, ricerche nearest neighbor, analisi time series, analisi enavigazione di testi [Jos03]. La clusterizzazione di dati ` un ramo importante del Data Mining e molti esono gli algoritmi che si occupano di questa scienza. Essi sono suddivisi in 2categorie: algoritmi gerarchici (in figura 2.6 un esempio di rappresentazionea dendrogramma) ed algoritmi partizionali (vedi esempio di figura 2.7). Iprimi creano una decomposizione gerarchica del dataset in oggetto formandoun albero che divide ricorsivamente i dati in sottoalberi pi` piccoli; i secondi, uinvece, creano un’unica partizione dei dati in insiemi disgiunti. Figura 2.6: Esempio di clusterizzazione gerarchica a dendrogramma 14
  • 29. Figura 2.7: Esempio di clusterizzazione partizionale2.2.1 Algoritmo K-MeansK-means appartiene alla classe degli algoritmi di partizione non gerarchica.Il suo obiettivo ` quello di trovare una partizione in k cluster di n oggetti espaziali che minimizzi le differenze tra gli oggetti appartenenti alla stessaclasse e massimizzi le differenze tra gli oggetti di cluster diversi. Pi` formal- umente, supporre di avere un insieme di n dati spaziali X1 , X2 , · · · , Xn taleche ogni dato ` in Rd . La ricerca dei cluster con varianza minima di questo einsieme in k cluster ` definita come la ricerca di k punti {mj }k in Rd tale e j=1che n 1 minj d2 (Xi , mj ) (2.1) n i=1sia minimizzata, dove d(Xi , mj ) ` la distanza Euclidea tra Xi e mj . I punti e{mj }k sono i centroidi dei cluster. In modo equivalente si pu` dire che j=1 ol’obiettivo ` quello di minimizzare la varianza intra-cluster e massimizzare equella inter-cluster k V ar = (Xi − mj )2 (2.2) j=1 xi ∈Sjdove k ` il numero di cluster raggruppati nell’insieme Sj , j = 1, 2, . . . , k e emj sono i centroido dei punti xi ∈ Sj . Informalmente il problema in 2.1 e 2.2 ` quello di ricercare k centroidi edei cluster tali che minimizzino il quadrato della distanza Euclidea (cono-sciuto anche come mean squared error, MSE ) tra ogni punto nello spazio e ilsuo centroide pi` vicino. Sfortunatamente questo problema ` NP-completo u e[MRGW82]. Il classico algoritmo K-Means [Har75] fornisce una soluzione approssima-ta, semplice da implementare, scalabile e veloce, che utilizza una euristica 15
  • 30. Tabella 2.2: Simboli utilizzati per lo studio di K-Means Simbolo Definizione n numero transazioni k numero di clusters d dimensione del dominio (numero attributi) P numero processi numero di iterazioni di K-Meansiterativa di affinamento conosciuta come algoritmo di Lloyd’s, riassumibilein 4 punti: 1. Inizializzazione: Seleziona un insieme di k punti di partenza {mj }k j=1 in Rd come centroidi. La selezione pu` essere effettuata in modo o casuale o seguendo qualche euristica. 2. Calcolo della distanza: Per ogni punto Xi , 1 ≤ i ≤ n, calcola la sua distanza Euclidea con ogni centroide mj , 1 ≤ j ≤ k e trova i centroidi che minimizzano la 2.1. 3. Ricalcolo Centroidi: Per ogni 1 ≤ j ≤ k ricalcola i centroidi mj come media dei punti assegnati ad essi. 4. Condizione di Convergenza Ripeti i passi 2 e 3 fino alla conver- genza (M SE non cambia).Questo algoritmo pu` essere considerato come una procedura a gradiente di- oscendente che comincia con dei centroidi casuali e li aggiorna iterativamentead ogni passo per minimizzare la funzione obiettivo 2.1. Tuttavia si sa cheK-Means converger` sempre in un minimo locale [BB95] che dipender` dai a acentroidi scelti all’inizio. Prima della convergenza, i passi 2 e 3 dovrannoessere ripetuti volte, valore non predicibile e dipendente anch’esso dai cen-troidi iniziali. L’intero positivo ` detto numero di iterazioni di K-Means, ementre un esempio di algoritmo ` presentato in figura 2.8. e In figura 2.9 si pu` intuire facilmente come opera l’algoritmo: si comin- ocia scegliendo casulamente dei centroidi (a); ogni punto verr` associato al acentroide pi` vicino (b) e successivamente si ricalcolano i centroidi relativa- umente ad ogni cluster (c). I passi (b) e (c) si ripetono iterativamente finoalla convergenza (i centroidi rimangono identici in due successive iterazioni). 16
  • 31. Figura 2.8: Algoritmo K-Means sequenziale [DM00] L’algoritmo K-Means ` conosciuto per la sua semplicit` di realizzazio- e ane e per i buoni risultati che riesce a fornire tuttavia richiede un tempodi computazione proporzionale al prodotto numero di oggetti e numero dicluster per iterazione, tempo molto alto per database di grandi dimensio-ni. Analizzando la complessit` computazionale si rileva che ogni iterazione adell’algoritmo ` costante, composta dal calcolo delle distanze, e ricalcolo dei ecentroidi. In particolare il calcolo delle distanze ` l’operazione pi` pesan- e ute e richiede (3nkd + nk + nd) operazioni in virgola mobile per iterazionedove 3nkd si riferisce al calcolo delle distanze euclidee, nk alla ricerca delcentroide del cluster mentre n, k e d sono rispettivamente la cardinalit` adel database, il numero di cluster e il numero di dimensioni del dominio (ilnumero di attributi). Anche il ricalcolo dei centroidi richiede approssima-tivamente kd istruzioni floating point. Combinando le relazioni precedenti 17
  • 32. Figura 2.9: Esempio di clusterizzazione di punti spazialipossiamo ricavare la complessit` computazionale dell’algoritmo seriale come a O(3nkd + nk + nd + kd) · (2.3)che pu` essere ridotto alla seguente relazione nel caso in cui il n >> k, d: o O(3nkd) · (2.4)2.3 WekaIl progetto Weka (Waikato Environment for Knowledge Analysis [wek]) ` euna collezione di algoritmi autoapprendenti (machine learning) allo stato 18
  • 33. dell’arte e di strumenti di analisi dei dati. Esso fornisce strumenti per l’in-tero processo di “data mining” dalla preparazione dei dati in input allavalutazione dei dati in output, anche in forma visuale, per verificare il risul-tato della fase di learning, attraverso un’interfaccia grafica. Esso ` capace edi unire alla semplicit` della sua interfaccia un background potente ed af- afidabile, cos` da fornire agli utenti uno strumento flessibile per lo sviluppo ıe l’utilizzo di tecniche machine learning per l’estrazione di conoscenze dagrossi database, impossibili da analizzare a mano. Il sistema ` sviluppato in Java, linguaggio di programmazione orienta- eto agli oggetti disponibile per molte piattaforme, da Windows a Linux eMacintosh e distribuito con licenza GNU General Public License [gnu], checi permette di scaricare, modificare e redistribuire il codice senza scopo dilucro ed ` testato in Windows, Linux e Macintosh. Gli utenti di Weka non esono solo ricercatori o professionisti, ma anche studenti per l’apprendimentodelle tecniche di Data Mining. Il suo utilizzo ` adatto a pi` livelli: forni- e usce l’implementazione degli algoritmi allo stato dell’arte per questa scienza,consentendo a chiunque di applicarli sul proprio database; in aggiunta sonodisponibili una grande variet` di procedure per la trasformazione dei dati ain input o l’analisi dei risultati degli algoritmi. Come tutti i pacchetti opensource Weka non solo consente di visionare, modificare e distribuire i sorgen-ti ma mette anche a disposizone delle procedure che aiutano nello sviluppodi nuovi metodi: ` possibile infatti, grazie all’uso delle interfacce comuni esviluppare i propri algoritmi di associazione, clustering o associazione senzadoversi preoccupare di alcuni dettagli secondari, come per esempio leggere oscrivere dal database, implementare filtri o tool di valutazione dei risultati;Weka fornisce gi` tutte queste utilit`, oltre ad una nutrita documentazione a aonline, cartacea e Javadoc. Tutti gli algoritmi richiedono in input una tabella relazionale come in fi-gura 2.10 nel formato ARFF (Attribute-Relational File Format, vedi [WF05],che pu` esser generata da un file o da una query. o Un ultimo utilizzo di Weka consiste nell’applicare un metodo autoappren-dente ad una serie di dati, utilizzando alcuni algoritmi di classificazione. Persaperne di pi` si pu` visitare e scaricare Weka dal sito dell’universit` neo- u o azelandese di Waikato [wek] mentre il libro di Witten e Frank [WF05] dedicaun intero capitolo a questo tool, alle sue caratteristiche e come sfruttarne a 19
  • 34. Figura 2.10: Esempio di database in formato ARFFpieno le potenzialit`. a Il nucleo centrale del progetto ` il package weka.core; qui vi sono definiti ei metodi per l’elaborazione primaria dei dati, a cominciare dalla lettura deglioggetti nel database. Le classi principali in questo package sono Attribute,Instance e Instances. Un oggetto della classe Attribute rappresenta unattributo (identificato da nome, tipo e valore) ed il numero di istanze ` epari al numero di colonne del database. Un oggetto della classe Instancecontiene il valore dell’attributo di una particolare istanza; un insieme diqueste istanze (il dataset) ` rappresentato da un oggetto di tipo Instances. e2.3.1 Algoritmo AprioriWeka include l’algoritmo Apriori per generare regole associative nella clas-se weka.association.Apriori. Per testare l’algoritmo si pu` utilizzare o 20
  • 35. opzione funzione -t <training file> Specifica il file di training -N <numero di regole richieste> Specifica il numero di regole richieste -C <confidenza minima di una regola> Specifica la confidenza minima di una regola -D <delta per supporto minimo> Specifica il delta per decrescere il supporto minimo -M <limite minimo del supporto> Specifica il limite minimo richiesto dal supporto Tabella 2.3: Principali opzioni per l’algoritmo Apriori su Wekal’interfaccia grafica o da terminale digitanto il comando java weka.association.Apriori -t <<training file>>.arffdove training file ` il nome del file contenente il database in formato earff (Weka ne propone alcuni famosi in letteratura) con attributi nominali(Apriori non pu` elaborare attributi numerici). L’output di questo comando o` rappresentato in figura 2.11.e Weka tenta di generare almeno 10 regole che rispettino il supporto mi-nimo del 100%, decrescendo del 5% finch` non ci riesce o finch` raggiunge e eil 10%. La confidenza minima ` settata al 90%. Nell’esempio in figura il esupporto decresce fino al 20% prima di riuscire a generare le 10 regole e ci` orichiede 10 passaggi ma tutti questi parametri sono modificabili da partedell’utente con le opzioni di tabella 2.3.1. La regole generate vengono rappresentate nella seconda parte dell’outputdell’algoritmo, numerate da 1 a 10. Il numero che precede il simbolo ==>indica il supporto della regola, cio` il numero di insiemi di oggetti di quel etipo trovati nel database. In modo analogo il numero nella conseguenzadella regola rappresenta quante volte questa regola ` verificata nel database ementre tra parentesi c’` la confidenza della regola stessa. eAl di sopra delle regole si trovano il numero di insiemi trovati per ognilunghezza considerata; nell’esempio 6 insiemi di lunghezza 4, 39 di lunghezza3, e cos` via. ı 21
  • 36. Figura 2.11: Output dell’algoritmo Apriori su Weka2.3.2 Algoritmo K-MeansNel package weka.clusterers sono presenti alcuni degli algoritmi di clu-stering pi` conosciuti, tra i quali Cobweb, DBScan, EM e, naturalmente, uK-Means. Per il suo utilizzo si pu` utilizzare l’interfaccia grafica oppure il o 22
  • 37. opzione funzione -t <training file> Specifica il file di training -T <test file> Specifica il file di test -x <numero di cicli> Specifica il numero di cicli per il cross-validation -s <seme> Specifica il seme per i numero pseudo casuali -l <file input> Specifica il file in input come modello -d <file output> Specifica il file in output per il modello -p Trova solo le previsioni in output Tabella 2.4: Principali opzioni per l’algoritmo K-Means su Wekacomando java weka.clusterers.SimpleKMeans -t <<training file>>.arffdove training file ` il nome del file contenente il database in formato earff con attributi numerici (K-Means non pu` elaborare attributi nominali), oeventualmente integrato con le opzioni di tabella 2.3.2. La bont` dell’output a` verificata con una funzione di verosimiglianza.e L’output di questo comando ` rappresentato in figura 2.12. Si possono enotare i cluster trovati, quante istanze sono state assegnate ad ognuno diessi e statistiche dettagliate su media e la varianza dei dati. Esistono 4 modi differenti affinch` Weka elabori i cluster, a scelta dell’u- etente: • Use training set: dopo aver generato i cluster, Weka classifica le istanze di training nei cluster trovati e calcola la percentuale di istanze che cadono in ogni cluster. • Supplied test set e Percentage split: Weka clusterizza su un database separato se la rappresentazione ` probabilistica (come in EM) e • Classes to clusters: in questa modalit` Weka ignora le classi degli a attributi e genera il cluster. Durante la fase di test assegna le classi ai cluster, basando sul valore pi` grande degli attributi in ogni cluster u poi calcola l’errore di classificazione. 23
  • 38. Figura 2.12: Output dell’algoritmo K-Means su Weka Nell’esempio di figura 2.12 K-Means ha clusterizzato il dataset iris (vedidettagli nel capitolo 7) in 3 cluster, ognuno dei quali contenente 50 istanze.Per ogni attributo viene calcolata la media e la deviazione standard. 24
  • 39. Capitolo 3Architetture eProgrammazione Parallela “C’` vero progresso solo quando i vantaggi di una nuova tecnologia e diventano per tutti.” Henry Ford (1863-1947), industriale americano. 25
  • 40. 3.1 DefinizioniUno dei principali metodi per aumentare le prestazioni di un sistema d’e-laborazione dati, ` quello di aumentare il numero di processori che questo eprevede, distribuendo fra di essi il carico computazionale. Originariamentequesto problema ` stato affrontato in due modi diversi, che hanno portato eallo sviluppo di tecniche per la gestione di sistemi paralleli e sistemi di-stribuiti. Un sistema parallelo ` un sistema in cui si hanno vari processori estrettamente collegati tra di loro e che condividono la maggior parte dellerisorse presenti. Un sistema distribuito ` invece un sistema in cui si hanno evarie unit` elaborative, indipendenti tra di loro ma collegate tramite una aqualche rete. L’idea base dei computer paralleli, quindi, ` che diversi processori pos- esano cooperare nella soluzione di un singolo problema. Il motivo che spingead occuparsi di calcolo parallelo ` proprio il tentativo di velocizzare il calco- elo della soluzione dei problemi computazionali. Intuitivamente si potrebbepensare che, se un processore impiega un tempo t per eseguire un compito,allora n processori possono ultimare lo stesso in un tempo t/n. In realt` asoltanto in casi molto particolari questo incremento di velocita pu` essere oraggiunto e comunque ` compito del programmatore progettare l’algoritmo ein modo da ottenere il massimo beneficio da architetture multi processore. Definiamo allora lo speedup come tserial S= tparallelrapporto tra il tempo di esecuzione seriale e parallelo; il valore ottentuto ` eindice della bont` della parallelizzazione ottenuta, migliore quanto pi` vicino a uad n, numero di processori. L’efficienza, invece, ` il rapporto tra speedup eparallelo e numero di processori S E= ne da una stima di quanto stiamo sfruttando il potenziale offerto dalla pa-rallelizzazione. In pratica un’efficienza pari a 1, significa una riduzione deitempi di risposta dell’algoritmo parallelo di un fattore n. Amdahl indic` il limite presente nello Speedup teorico, cio` nell’aumento o e 26
  • 41. di prestazioni dovuto al parallelismo, come: 1 S≤ . α + (1 − α)/ndove α ` la quota di codice non parallelizzabile e (1-α) ` parallelizzabile su e en processori per cui, aumentando il numero n di processori, la percentuale αdi codice sequenziale ridurrebbe di molto l’aumento effetivo dello speedup.Se ad esempio α = 0, 5, lo speedup massimo raggiungibile ` 2 anche con e1000 processori.3.2 Architetture Figura 3.1: Tassonomia di Flynn La pi` famosa classificazione (tassonomia) delle architetture per i sistemi uparalleli ` quella proposta da Flynn in figura 3.1. Secondo questa tassonomia ele due pi` importanti caratteristiche di un elaboratore sono il numero di flussi udi istruzioni che esso pu` processare ad ogni istante ed il numero di flussi odi dati su cui esso pu` operare simultaneamente. Combinando queste due ocaratteristiche ` possibile ottenere le seguenti quattro classi architetturali, ealle quali se ne aggregano altre due pi` recenti: u • SISD (Single Instruction stream - Single Data stream) • SIMD (Single Instruction stream - Multiple Data stream) 27
  • 42. • MISD (Multiple Instruction stream - Single Data stream) • MIMD (Multiple Instruction stream - Multiple Data stream) • SPMD (Single Program stream - Multiple Data stream) • MPMD (Multiple Program stream - Multiple Data stream) Figura 3.2: Architettura SIMD La classe SISD, in figura 3.2, comprende l’architettura tradizionale diVon Neumann che ` quella usata da tutti i calcolatori convenzionali, in cui eil singolo processore obbedisce ad un singolo flusso di istruzioni (programmasequenziale) ed esegue queste istruzioni ogni volta su un singolo flusso di dati. Figura 3.3: Architettura SIMD Alla classe SIMD appartengono le architetture composte da molte unit` adi elaborazione che eseguono contemporaneamente la stessa istruzione malavorano su insiemi di dati diversi. In genere, il modo di implementare learchitetture SIMD ` quello di avere un processore principale che invia le eistruzioni da eseguire contemporaneamente ad un insieme di elementi dielaborazione che provvedono ad eseguirle. I sistemi SIMD sono utilizza-ti principalmente per computazioni specializzate in parallelo, grafica CAD, 28
  • 43. grafica vettoriale. Prima Intel con SSE poi AMD con 3DNow! hanno ag-giunto estensioni SIMD ai propri processori, via via sempre pi` sofisticate utanto che i pi` moderni processori di entrambe le case utilizzano l’ultima uversione denominata SSE3. La classe MISD (in figura 3.4), in cui pi` flussi u Figura 3.4: Architettura MISDdi istruzioni (processi) lavorano contemporaneamente su un unico flusso di `dati, non ` stata finora utilizzata praticamente. E da notare che, mentre nel- ela classe SIMD la granularit`, ovvero la dimensione delle attivit` eseguibili a ain parallelo, ` quella delle istruzioni, nella classe MISD e in quella MIMD la egranularit` ` quella dei processi, programmi composti da pi` istruzioni. ae u Figura 3.5: Architettura MIMD Il modello rappresentato dalla classe MIMD di figura 3.5, in cui pi` pro- u 29
  • 44. cessi sono in esecuzione contemporaneamente su pi` processori ed utilizzano udati propri o condivisi, rappresenta un’evoluzione della classe SISD. Infatti,la realizzazione di queste architetture avviene attraverso l’interconnessionedi un numero elevato di elaboratori di tipo convenzionale. I sistemi con ar-chitettura MIMD sono oggi fra quelli pi` studiati e si pu` presumere che essi u orappresentino il punto di partenza per la costruzione di macchine paralleledi tipo general-purpose. Spesso il modello SPMD utilizza il message passing tra i diversi nodidi elaborazione ed ` supportato da specifiche librerie MPI Message Passing eInterface. Un solo programma con costrutti di controllo che scelgono partidifferenti del programma a seconda del nodo su cui viene lanciato. Questasar` l’architettura obbligata per la parallelizzazione degli algoritmi in Weka, aoggetto della tesi: un solo programma lanciato in parallelo su pi` proces- usori a memoria condivisa che elaborano flussi di dati differenti. Il modelloMPMD ha i singoli nodi che eseguono programmi differenti (tipicamentemaster/slave). Figura 3.6: Esempio di architettura quad core: AMD opteron [amd] Un’altra importante differenza nei computer paralleli riguarda il modellodi memoria utilizzato. Esso pu` essere di tipo condiviso (Shared Memory) oo distribuito (Distributed Memory). Un sistema a memoria condivisa ` mo- estrato in Fig 3.6. Qui, tutti i processori hanno accesso ad una memoriacomune. Ciascun processore pu` anche avere una propria memoria locale o 30
  • 45. per il codice del programma e dati temporanei, mentre la memoria comune` utilizzata per dati e risultati necessari a pi` processori. Nel caso di AMDe uOpteron, nella foto, ogni core ha a disposizione memoria cache L1 ed L2privata ed L3 da ben 2MB condivisa tra i 4 core all’interno del package;a queste si aggiunge la memoria RAM, ovviamente condivisa e gestita dal-lo stesso controller. Tutte le comunicazioni tra i processori sono compiuteattraverso la memoria comune. Il pi` grande vantaggio dei sistemi a me- umoria condivisa ` l’alta velocit` delle comunicazione tra processori, mentre e ail grande svantaggio ` che processori diversi possono desiderare di utilizza- ere la memoria comune nello stesso istante. In tal caso uno dei processorideve attendere che sia liberata la memoria al fine di evitare conflitti (unprocessore potrebbe alterare il dato proprio mentre l’altro li sta leggendo).Questo ritardo pu` crescere al crescere del numero di processori. In genere ola memoria condivisa viene usata su sistemi con un numero di processoribasso.3.3 Gestione della memoriaLa gestione della memoria, nei sistemi a memoria condivisa non si discostadi molto da come avviene nel caso di un sistema monoprocessore ma bisognaporre particolare attenzione alla modalit` di accesso se si vogliono ottenere aconsistenza e prestazioni. Alcune delle metodologie utilizzate per risolverequesto problema sono: • Condivisione. La richiesta di accesso in lettura pu` avvenire parallela- o mente per tutti i processi mentre in modo sequenziale se l’operazione ` e di scrittura. In questo caso saranno necessari dei meccanismi di mutua esclusione, per evitare la lettura di dati sporchi, che ne rallenteranno le prestazioni • Replicazione. Con questo sistema una stessa struttura dati pu` essere o presente contemporaneamente sullo stesso spazio di memoria associato a processi diversi, disponibile sia in lettura che in scrittura. Dovendo garantire la consistenza tra le varie copie richiede la presenza di op- portuni algoritmi per la sua implementazione, simili a quelli che si uti- lizzano per la realizzazione della consistenza delle cache nei processori 31
  • 46. ed operazioni di riduzione per giungere al risultato finale dall’insieme di risultati parziali ottenuti parallelamenteDal punto di vista architetturale i sistemi multiprocessore possono esse-re caratterizzati in base all’accoppiamento tra la memoria e i processori.Nei sistemi multiprocessore a memoria condivisa, la memoria presente ` eaccessibile dai vari processori. Questi sistemi possono essere a loro voltacaratterizzati in base ai tempi di accesso alla memoria stessa. • UMA (Uniform Memory Access): sistemi multiprocessore a tempi di accesso uniforme per la memoria nei quali i tempi di accesso alla me- moria sono uguali per tutti i processori. Questa architettura pu` esse- o re realizzata semplicemente collegando i vari processori e la memoria presente nel sistema tramite un unico bus. Figura 3.7: Esempio di architettura della memoria UMA • NUMA (Non-Uniform Memory Access): sistemi multiprocessore a tempi di accesso non uniforme per la memoria, i tempi di accesso a ciascun elemento di memoria variano a seconda del processore. In que- sta architettura ogni processore ha la sua memoria locale alla quale ` e collegato direttamente. Per consentire l’accesso alla memoria apparte- nente ad un altro processore, vi sar` un qualche collegamento, formato a ad esempio da un insieme di bus che unir` tra di loro i vari moduli a di memoria. Il tempo di accesso alla memoria di un altro processore risulter` quindi essere maggiore di quello relativo alla memoria locale, a e varier` a seconda della distanza alla quale questa si trova. Sono si- a stemi che in termini di scalabilit` pongono meno problemi degli UMA. a 32
  • 47. Figura 3.8: Esempio di architettura della memoria NUMA • NORMA (NO Remote Memory Access): sistemi multiprocessore sen- za memoria condivisa, ogni processore ha la sua memoria locale, che non pu` essere direttamente acceduta dagli altri. La comunicazione o avviene in questo caso tramite l’invio di messaggi su una qualche rete, la cui topologia varia a seconda dei casi. Sono i pi` facili da realizza- u re, ma richiedono opportune tecniche per ottenere un elevato grado di parallelismo senza avere effetti negativi sulle prestazioni.3.3.1 Memoria CacheLa memoria cache ` un tipo di memoria piccola, ma molto veloce, che man- etiene copie dei dati ai quali si fa pi` frequentemente accesso in memoria uprincipale. Finch´ la maggior parte degli accessi alla memoria avviene su edati caricati nella cache, la latenza media dell’accesso alla memoria sar` api` vicina alla latenza della cache piuttosto che a quella della memoria uprincipale. Ogni locazione di memoria ha un dato (una linea di cache) e un indice,cio` un identificatore univoco utilizzato per riferirsi a quella specifica loca- ezione. L’indice di una locazione in memoria principale ` chiamato indirizzo edi memoria. Ogni locazione nella cache ha un’etichetta che contiene l’indicein memoria principale del dato ivi caricato. Nelle cache dati, questi valorisono chiamati blocchi di cache o linee di cache. Quando il processore vuole leggere o scrivere in una data locazione inmemoria principale, inizialmente controlla se il contenuto di questa loca-zione ` caricato in cache. Questa operazione viene effettuata confrontando el’indirizzo della locazione di memoria con tutte le etichette nella cache che 33
  • 48. potrebbero contenere quell’indirizzo. Se il processore trova che la locazionedi memoria ` in cache, si parla di cache hit (accesso avvenuto con successo), ealtrimenti di cache miss (fallimento d’accesso). Nel caso di un cache hit,il processore legge o scrive immediatamente il dato sulla linea di cache. Ilrapporto tra cache hit e accessi totali ` chiamato anche hit rate ed ` una e emisura dell’efficacia della cache stessa. Nel caso di un cache miss, la maggior parte delle cache crea una nuovaentit`, che comprende l’etichetta appena richiesta dal processore ed una acopia del dato dalla memoria. Un fallimento del genere ` relativamente elento, in quanto richiede il trasferimento del dato dalla memoria principale,il cui tempo di risposta ` molto maggiore di quello della memoria cache. e Per poter fare spazio a nuovi dati nel caso di un cache miss, la cachegeneralmente deve eliminare il contenuto di una delle linee. L’euristica cheutilizza per scegliere quale dato eliminare ` chiamata politica di rimpiazza- emento. Il problema fondamentale di ogni politica di rimpiazzamento ` quello edi dover predire il dato della cache che verr` richiesto nel futuro con minor aprobabilit`. Predire il futuro ` difficile, soprattutto per le cache hardware a eche devono sfruttare regole facilmente implementabili in circuiteria, perci` oesistono una serie di politiche di rimpiazzamento e nessuna di esse pu` es- osere ritenuta perfetta. Una delle pi` popolari, la LRU (dall’inglese Least uRecently Used, cio` usato meno recentemente), rimpiazza, appunto, il dato eal quale si ` fatto accesso meno recentemente. e I dati in memoria principale, dei quali esiste una copia nella cache, po-trebbero essere modificati da altre cause (evento non improbabile, ad esem-pio, in un sistema multiprocessore), perci` i dati nella cache potrebbero odiventare obsoleti. I protocolli di comunicazione tra i sistemi di gestionedelle cache che conservano la consistenza dei dati sono chiamati protocollidi coerenza.Cache MissCon cache miss (fallimento della cache) ci si riferisce ad un intento fallitonel leggere o scrivere un pezzo di dati nella cache, che ha come risultatouna latenza molto pi` lunga nell’accesso alla memoria principale. Per un ufallimento nella lettura dalla cache istruzioni, il processore deve aspettare(stallo) finch´ l’istruzione non ` caricata dalla memoria principale. Un fal- e e 34
  • 49. limento della cache causato dal caricamento di un dato pu` invece essere omeno doloroso, perch´ le altre istruzioni non correlate ad esso possono co- emunque essere eseguite, finch´ l’operazione che richiede i dati da caricare epu` essere eseguita. Comunque, i dati sono spesso usati immediatamente odopo l’istruzione di caricamento. L’ultimo caso di cache miss, cio` un fal- elimento in scrittura, ` il meno preoccupante, perch´ di solito la scrittura ` e e ebufferizzata. Il processore pu` continuare tranquillamente finch´ il buffer o enon ` pieno. (Non esiste un fallimento nella scrittura della cache istruzioni eperch´ esse sono di sola lettura). e Nei moderni processori multi-core la cache ` multilivello, talvolta fino a e3, con una cache L1 di dimensioni molto piccole (attorno ai 128 KByte) eduna L2 di dimensioni maggiori, attorno ai 2 MByte. Le cache multilivellogeneralmente operano controllando dapprima le cache a livello 1; se avvie-ne un hit, il processore procede ad alta velocit`. Se la cache pi` piccola a u“fallisce”, allora viene controllata quella pi` grande e cos` via, fino ad dover u ıaccedere alla memoria principale.3.4 SincronizzazioneLa presenza di primitive di sincronizzazione tra processi che consentano lamutua esclusione nella loro esecuzione, ` necessaria per proteggere le sezioni ecritiche, e quindi la consistenza della memoria tra di loro condivisa, oltreche per garantire una certa successione temporale nelle azioni realizzate. Inun sistema operativo multiprocessore possono essere implementate a livellodi sistema, tutte le primitive di sincronizzazione tra i processi normalmen-te presenti anche nei sistemi operativi monoprocessore, quali ad esempio isemafori e le variabili condition. In questo paragrafo affronteremo solo ilproblema della sincronizzazione dei processi che si trovino su processori di-versi: questa ` infatti la situazione che si verifica quando i vari kernel che si etrovano su processori diversi devono modificare le strutture dati tra di lorocondivise in sistemi UMA e NUMA. Un lock ` una struttura dati condivisa utilizzata per realizzare la mutua eesclusione tra i processi, in quanto ciascun lock pu` essere posseduto da oun solo processo alla volta. Su di un lock possono essere realizzate dueoperazioni, che chiameremo acquisisci lock (lock ) e rilascia lock (unlock ). 35
  • 50. La prima cerca di trasferire il possesso del lock cui fa riferimento (ci possonoessere pi` lock per ogni processo, una per ogni risorsa) al processo che la uchiama, ponendolo in qualche modo in attesa se il lock ` in quel momento eposseduto da un altro processo. La seconda rilascia il possesso del lock cuifa riferimento, consentendo l’avanzamento di uno dei processi in attesa sudi esso. Per realizzare una sezione critica, basta quindi racchiuderla tra ledue chiamate acquisisci lock e rilascia lock. Uno dei modi per realizzare queste due chiamate ` esemplificato nelle eseguenti due funzioni, associando quindi ad esempio ad un lock il valore 1se questo appartiene a qualcuno e 0 se ` libero. evoid a c q u i s i s c i l o c k ( boolean l o c k ) { while ( l o c k = 1 ) ; l o c k := 1 ;}void r i l a s c i a l o c k ( char ∗ l o c k ) { l o c k := 0 ;} Notiamo come l’operazione acquisisci lock si traduce in una sorta di at-tesa attiva se il lock non ` libero: la richiesta di acquisizione ` ripetuta e efinch´ non viene soddisfatta. Ci` presuppone ovviamente che il possesso del e olock sia di un processo che si trova su di un altro processore o almeno inun altro thread. Nel caso in cui ci` non sia vero sono possibili altre solu- ozioni che a prima vista sembrerebbero in ogni caso pi` efficienti, come ad uesempio bloccare l’esecuzione del processo in attesa sul lock, schedulandone `un altro finch´ questo non sia stato rilasciato. E possibile eventualmente eaggiungere delle piccole pause tra due tentativi successivi, onde evitare chela contesa sul bus che gli accessi al lock determina, causi un rallentamentonell’esecuzione degli altri processi e quindi eventualmente del rilascio dellock stesso. Nel caso in cui sia possibile distinguere i processi in lettori e scrittori,sono possibili altre ottimizzazioni a questo modello, consentendo ad esem-pio a vari processi lettori di ottenere contemporaneamente il possesso di unlock, fintanto che questo non appartenga a nessun processo scrittore. Unaltro modo per realizzare la sincronizzazione, ` tramite un evento. Questi evengono utilizzati principalmente per garantire una certa successione tem-porale: un processo blocca la sua esecuzione su di un evento finch´ questo e 36
  • 51. non si verifica. Il concetto di evento pu` essere utilizzato per la sincro- onizzazione tra kernel che si trovino su processori diversi in sistemi UMA eNUMA, collegati da un bus che consenta la propagazione delle interruzioni.La routine di gestione delle interruzioni di un certo kernel sar` quindi in aattesa sull’evento interruzione generato da un altro kernel, e al suo arrivopotr` prendere le opportune decisioni. In Java, presentato nel paragrafo 3.6, asono disponibili molteplici soluzioni per la concorrenza e la sincronizzazione,inclusa la gestione a eventi.3.5 Multi coreIl termine multi core si usa per descrivere una CPU composta da pi` core, uovvero da pi` “cuori” di processori fisici indipendenti, le rispettive Cache e i ucache controller in un singolo package. Questo tipo di architettura consentedi aumentare la potenza di calcolo senza aumentare la frequenza di lavoro,a tutto vantaggio del calore dissipato. I primi processori dual core prodotti sono gli IBM PowerPC del 2003 mail concetto ha assunto risonanza mondiale nel 2005, anno in cui i due mag-giori produttori di CPU al mondo, Intel e AMD, hanno messo in commercioi primi esemplari di una nuova generazione di microprocessori, basati suirispettivi prodotti esistenti, e contenenti due core. Aumentare ulteriormente la frequenza di clock di una CPU single core ` estato giudicato dai progettisti una via molto onerosa e complicata, dato che iconsumi dei processori hanno superato i 100 W e si presenta un conseguenteproblema di raffreddamento dei circuiti. Si ` deciso quindi di puntare sul pa- erallelismo in modo da poter aumentare il numero di operazioni eseguibili inun unico ciclo di clock. Tale approccio comporta anche alcuni svantaggi, inquanto i programmi devono essere ottimizzati per un utilizzo multi-threadper poter sfruttare appieno le caratteristiche di questi processori, in casocontrario impegneranno solo uno dei core presenti, lasciando gli altri presso-ch´ inutilizzati. Potrebbe addirittura verificarsi che un’applicazione risulti edi pi` lenta esecuzione su un processore dual core e, al momento del lancio, uerano veramente pochi i software gi` pronti per queste nuove architetture. a Diversi analisti prevedono che se il software pensato per un utilizzo pa-rallelo in ambito dual core venisse realizzato in maniera oculata, tenendo 37
  • 52. conto non del funzionamento su un sistema dual core, ma su un sistema api` core, non richiederebbe poi grosse modifiche per essere utilizzato su un udual core o su un quad core. Secondo altri invece, gli sforzi necessari perrealizzare software multi processore che funzioni in maniera ottimale satu-rando tutti i core e non occupandoli in modo disomogeneo saranno esageratiin relazione con i reali vantaggi, soprattutto di impatto di costi. I primi processori dual core commerciali sono sostanzialmente quelli dellaprecedente generazione di processori di Intel e AMD montati su un unicopackage, tuttavia esistono differenti approcci attraverso i quali si possonorealizzare chip dual core sulla realizzazione fisica di queste CPU e Dualcore (gestione della cache), per considerazioni sulla fruizione della cache daparte di ciascun core). Al momento del lancio dei primi processori dellanuova generazione dedicati al settore desktop, vale a dire gli Intel PentiumD (basati sul progetto Smithfield ), le frequenze dei processori erano minoririspetto a quelle dei rispettivi processori monoprocessore AMD. I primi Pentium D erano formati appunto da 2 core Prescott montatisullo stesso package; successivamente sono arrivati anche quelli basati sucore Presler, divenuti la seconda generazione di processore Pentium D (incui erano sempre presenti due core uguali affiancati, anche se non sullostesso package per poter cos` aumentare le rese produttive), con processo ıproduttivo a 65 nm. Dopo Presler ` stata la volta del Core 2 Duo Conroe earrivato sul mercato a luglio 2006. Intel ha inoltre progressivamente esteso l’utilizzo di questa tecnologia atutti i segmenti di mercato, inclusi quello mobile, di cui il primo esponente` stato il Core Duo Yonah e successivamente Merom, entrambi evoluzioniedel Pentium M, e ovviamente al settore server. In questa categoria i primidual core sono Montecito evoluzione del processore a 64 bit Itanium 2 concache L3 che raggiunge in alcune versioni i 24 MB di capacit`, mentre per i aserver di fascia bassa basati sul processore Xeon hanno visto la luce Dempsey(formato da due core Irwindale), e Paxville, nomi in codice di queste CPU,costruite con processo produttivo a 65 nm e destinate, rispettivamente, asistemi workstation e server a 2 e 4 processori. Da Conroe ` derivato anche elo Xeon Woodcrest, per sistemi server di fascia entry level dotati di duesocket. Se Intel ha avuto il merito di rendere questi processori disponibili pra- 38
  • 53. ticamente a tutti, i primi processori multi core presentati sul mercato sonostati, verso la fine del 2005, il Cell, CPU sviluppata congiuntamente da IBM,Sony e Toshiba per il mercato console/blade server e composto da 8 corenon omogenei, e l’UltraSPARC T1, processore a 8 core omogenei sviluppatoda Sun Microsystems per il mercato server (In particolare per Web servercon numerosi accessi) capace di gestire 32 thread simultanei. Intel ha stimato che nel corso del 2007 la percentuale di processori do-tati di architettura quad core in commercio sia pari al 3% della produzionecomplessiva. Una percentuale di questo tipo pu` sembrare limitata, ma al- ola luce del totale di processori commercializzati nel 2007 (stimato in circa230 milioni) e della quota di mercato del 75% circa detenuta da Intel, se nericava un totale di almeno 3 milioni di processori quad core [Cor].3.5.1 RealizzazioneAl momento, esistono 3 metodi differenti per creare un chip dual core: DieSingolo, Die Doppio e Die Monolitico. Il Die ` il blocco di silicio al centro edi un processore che contiene il cuore elaborativo della CPU, il core.Die singoloFigura 3.9: Intel Pentium D Smithfield, primo dual core a die singolo [int] L’unico processore ad utilizzare tale approccio ` stato il Pentium D Smi- e `thfield e consiste nel combinare 2 core su un singolo die. E l’approccio sen-z’altro pi` semplice, e quindi pi` economico rispetto agli altri, per realizzare u uun chip dual core, ma ovviamente ` anche pi` limitante per quanto riguarda e u 39
  • 54. le prestazioni e la resa produttiva. Infatti, prendendo ad esempio proprio ilcaso di Smithfield, che ` sostanzialmente formato da 2 core Prescott (sin- egle core), ` possibile osservare, ovviamente in maniera molto semplicistica, ecome per realizzarlo sia sufficiente utilizzare la stessa maschera litograficadisegnata per il processore Prescott, e “stamparla” 2 volte sul wafer di siliciointerconnettendo i due core cos` realizzati. Rimane comunque il problema ıche se anche solo uno dei 2 core stampati ` difettoso, tutto il chip diventa einutilizzabile, ovvero non potr` essere un Pentium D, e potrebbe quindi es- asere rivenduto come semplice Pentium 4 Prescott dopo aver disabilitato unodei due core. L’evoluzione delle tecniche produttive probabilmente ha gi` decretato la a“morte” di tale approccio, che sebbene semplice da realizzare pu` risultare oindirettamente costoso per la probabilit` di non avere molti core attigui sul awafer perfettamente funzionanti. Intel stessa infatti ha realizzato tutti glialtri progetti dual e multi core utilizzando gli altri approcci e AMD invecenon ha mai utilizzato tale approccio. La tecnica pi` sofisticata attualmente uin uso, il die monolitico, pur risolvendo altri problemi, di fatto soffre anch’es-sa dello stesso problema: si devono avere i due core vicini funzionanti. Daparte sua per` si avvantaggia del fatto di essere progettato tenendo conto odi questi fattori, mentre i core utilizzati nel die singolo non erano progettatiper essere multi core. Ognuno dei due core comunque, manteneva la propria indipendenza equindi anche le cache L2 erano sdoppiate e ogni core aveva la “propria” cacheL2, quindi solo con questa poteva avere una sorta di accesso privilegiato.Proprio per questo motivo, per Smithfield, non si parlava di una cache L2 da2 MB ma di 2x1MB. Affinch´ un core potesse accedere ai dati memorizzati enella cache dell’altro era necessario trasferire i dati attraverso il BUS disistema, con il rischio di saturarlo. La scelta di questa implementazione inSmithfield, penalizzante dal punto di vista delle prestazioni, era giustificataproprio dalla sua relativa semplicit` realizzativa e progettuale. Solo con al’avvento del die monolitico si riuscir` a porre rimedio a questo collo di abottiglia, condividendo l’intera cache L2 tra i core in modo da liberare ilbus di sistema e consentire la condivisione dei dati in memoria a libello dicache. 40
  • 55. Die doppio Figura 3.10: Intel Pentium D Presler, primo dual core a die doppio [int] Tale metodo consiste nel posizionare 2 die, fisicamente separati, su ununico package e collegarli successivamente con collegamenti esterni. Il primoprogetto Intel ad avvantaggiarsi di questo approccio ` stato il Pentium D ePresler. Tale metodo, seppure leggermente pi` complesso nella realizzazione udi quello a Die singolo, in quanto collegare i core solo in un secondo temporichiede maggiore tempo che realizzare i collegamenti direttamente sul silicio,risulta per` complessivamente il metodo pi` economico dal punto di vista o udel produttore, infatti ` possibile “scegliere” quali devono essere i due core eche andranno poi collegati insieme, facendo cadere la necessit` dei due core aaffiancati e massimizzando la resa produttiva. Soprattutto agli albori dell’utilizzo di quest’approccio i chip che supe-ravano i test, venivano valutati sui margini di clock e tensioni; i modelliche tolleravano frequenze di clock elevate potevano essere marchiati comeveloci Pentium 4 single core, mentre gli altri potevano essere accoppiati perrealizzare i modelli dual core Pentium D che generalmente funzionavano aclock inferiori. C’` tuttavia un grande svantaggio nell’inserire due core indipendenti in eun solo package. Quando un core accede ai dati, anche l’altro far` lo stesso, autilizzando risorse non necessarie. La separazione fisica dei core compor-ta due inconvenienti: la necessit` di collegamenti esterni e, ovviamente la aseparazione della cache che non ` pi` una scelta progettuale ma uno scot- e uto da pagare. Il primo inconveniente in realt` ` comunque relativamente a eeconomico da superare, mentre il secondo rappresenta comunque un limiteintrinseco di questo approccio. 41
  • 56. Die monolitico Figura 3.11: Intel Core 2 Duo, primo dual core a monolitico [int] L’approccio a die monolitico ` certamente quello pi` sofisticato da rea- e ulizzare ma ovviamente ` anche quello che garantisce le migliori prestazioni edi una CPU multi core. Tale approccio deve essere preventivato fin dalleprime fasi della progettazione del processore ed ` stato utilizzato per la pri- ema volta da Intel per i Core Duo Yonah e gli Itanium 2 Montecito. Il suopi` grande pregio consiste nell’offrire ai progettisti l’opportunit` di condivi- u adere alcune unit` del processore; nel caso pi` semplice, tale condivisione si a ulimita alla cache che viene realizzata in un unico blocco condiviso tra tuttii core (in processori che utilizzano gli altri approcci costruttivi, la cache ` enecessariamente equamente divisa tra i core e l’accesso diretto a ciascunacache ` riservato esclusivamente al rispettivo core, il quale per accedere alle ealtre deve far transitare i dati sul BUS). In casi pi` complessi le unit` con- u adivise possono essere anche altre, come il memory controller della RAM (almomento solo nei processori AMD) gli scheduler che ripartiscono il caricotra i vari core, ecc. A questo punto ` infatti ormai chiaro che la pi` grossa lacuna delle prime e uversioni di processori dual core risiedeva proprio nella esclusivit` della cache aL2 e la necessit` di far transitare i dati sul gi` trafficato BUS di sistema. a aIl primo processore a colmare questa lacuna ` stato il Core Duo Yonah una eCPU mobile dual core in cui la cache L2 era condivisa tra i 2 core. Ognicore quindi poteva accedere alla totalit` della cache lasciando libero il BUS adi sistema che poteva quindi limitarsi a far transitare solo i dati da, e per,la memoria RAM. 42
  • 57. Figura 3.12: Intel Xeon quad core [int] Appare quindi evidente come tutti i pi` recenti progetti di processo- uri multi core puntino soprattutto all’utilizzo di quest’ultimo approccio co-struttivo, riservando gli altri (soprattutto quello a Die Doppio), per risolverespecifiche esigenze. Figura 3.13: Confronto tra l’architettura die singolo e monolitica [int] In figura 3.13, di propriet` di Intel R , un confronto tra due architetture adual core: la prima immagine rappresenta la prima serie a die singolo mentrela a destra a die monolitico. Appare evidente subito il diverso utilizzo dellacache di secondo livello che nel primo caso ` separata per ogni core mentre nel enei monolitici ` condivisa. Quest’ultimo approccio velocizza le performance edei processori che consentono di accedere indistintamente ai dati di ognisingolo core. 43
  • 58. 3.6 Multithreading in JavaCon l’avvento delle nuove architetture parallele la semplice programmazio-ne sequenziale a singolo processo non era adeguata per sfruttare le risorse adisposizione. La necessit` di poter eseguire pi` programmi contemporanea- a umente ` antecedente all’avvento dei multiprocessori, gia Windows 3.0 (che epochi di noi si ricorderanno) era multi-tasking, anche se i job non erano ef-fettivamente paralleli ma si suddividevano il tempo di calcolo della cpu perpoter simulare le architetture recenti. Oggi i programmi pi` sofisticati suddividono i loro job in unit` pi` pic- u a ucole, processi o thread, che possono avanzare nel tempo parallelamente inmodo reale (se ci sono pi` cpu che processi) o simulato (suddividento il tem- upo di cpu tra i thread se necessario). I thread multipli hanno origine dallostesso processo e condividono lo spazio di memoria, il codice eseguibile e idati globali, potendo in questo modo suddividere il lavoro a pi` processori uin modo efficiente. Il costo da pagare ` una notevole complessit` che pu` e a oessere spesso difficile da gestire. Il problema maggiore ` la sincronizzazione: espesso un thread ha bisogno dei risultati di un altro, oppure deve sovra-scrivere i dati di un altro ma non prima di un dato momento che possonocausare inconsistenza dei dati o stalli, risolvibili con vari approcci di mutuaesclusione. I moderni processori multi-core aumentano le loro prestazioni all’aumen-tare del grado di parallelismo proprio perch` raggiungono il risultato in modo ecooperativo rispetto al singolo flusso di dati. Dal punto di vista del sistemaoperativo una cpu di questo tipo si avvantaggia dal fatto di poter dedicareunit` di calcolo separate per pi` programmi in esecuzione, ma dal punto a udi vista algoritmico (di interesse in questa tesi), lo stesso algoritmo svilup-pato per processori a core singolo non si avvantaggeranno dell’uso dei coremultipli. Essi infatti utilizzeranno un solo core per tutti i calcoli, lasciandogli altri potenzialmente inutilizzati, ecco perch` ` necessario riprogettare e eeriscrivere interamente gli algoritmi in modo parallelo, in modo diverso ovvia-mente dagli algoritmi per sitemi distribuiti, potendo contare sulla memoriacondivisa. Java ` nato da subito con l’ambizione di poter essere facilmente utiliz- ezato con la programmazione multi-thread, grazie all’implementazione della 44
  • 59. Figura 3.14: Esempio di processo multithreading su processore quad-coresua Virtual Machine, e si sposa particolarmente bene con le nuove archi-tetture multiprocessore poich`, come in figura 3.14, ` possibile generare pi` e e uprocessi che condividono lo stesso spazio di memoria, cos` come pi` core la ı ucondividono nello stesso processore multi-core. Generalmente, le classi de-finite in Java sono “multi-threaded”, ovvero sono potenzialmente in gradodi gestire l’invocazione dei propri metodi da parte di pi` thread in contem- uporanea (fanno eccezione tutti i metodi caratterizzati dalla parola riservatasynchronized che rende tali metodi accessibili solo in maniera sequenziale);se cos` non fosse, thread paralleli potrebbero accodarsi per accedere allo ıstesso metodo, inficiando i benefici dei flussi di istruzioni paralleli. Eccoperch` Weka, sviluppato in Java, si ` prestato in modo naturale al lavoro di e eparallelizzazione proposto in questa tesi. Se gi` prima dell’avvento dei multi-core si riteneva necessario ricorrere aall’uso dei thread, magari per poter avviare pi` task contemporaneamen- ute, oppure pi` istanze dello stesso algoritmo con dati diversi, oggi il multi- uthreading ` d’obbligo: senza di esso infatti la ridondanza di processori sareb- ebe inutile. A ci` si aggiunga il beneficio di poter contare su un parallelismo oreale in modo che due thread paralleli possano contare sull’uso esclusivo didue unit` di calcolo concorrenti (un dual core che elabora un programma acon due thread produttore-consumatore ne ` un esempio). e 45
  • 60. 3.6.1 ProcessiIn origine i primi calcolatori erano single-tasking poich` potevano eseguire eun solo job alla volta in modo batch; in seguito i sistemi operativi diven-nero sempre pi` raffinati con l’introduzione di funzionalit` multitasking in u acui esisteva l’effettiva capacit` di di eseguire pi` job contemporaneamente. a uSu un sistema desktop, ad esempio, possibilit` di avere pi` programmi in a uesecuzione contemporaneamente (ad esempio Firefox per il web, Openofficeper la scrittura, un file manager e magari un Solitario in esecuzione). I principali sistemi multitasking sono anche multithreading. Per multi-threading si intende la possibilit` di avere pi` flussi di controllo nell’ambito a udello stesso processo. Su un sistema desktop, un programma pu` eseguire opi` compiti contemporaneamente, basti pensare all’utilizzo delle schede nei urecenti browser. Possiamo allora definire come processo (o task ) un pro-gramma in esecuzione, con il suo spazio di indirizzamento ed il suo stato;definiamo come thread un singolo flusso sequenziale di controllo all’internodi un processo. Un processo pu` contenere pi` thread ed ognuno di essi o ucondividono lo stesso spazio di indirizzamento. Per questo motivo i threadsono detti anche light-weight process (processo leggero). L’utilizzo di thread permette l’esecuzione parallela di pi` processi sia usu sistemi paralleli che a singolo processore. Nel primo caso ` probabile eche processi diversi vengano mappati su processori diversi consentendo unparallelismo reale dell’esecuzione con pi` thread che avanzano insieme. Se uil sistema ` un semplice monoprocessore il parallelismo ` solo apparente in e equanto si suddivide il tempo di CPU in quanti (slice) e si suddividono perogni thread. Allo scadere di ogni unit` di tempo, il sistema operativo opera aun cambio di contesto (context switch). La figura 3.15 rappresenta come avvengono le transazioni di stato inun processo. Appena creato, il processo ` collocato nella coda dei processi epronti, dalla quale ` fatto passare in esecuzione dal sistema operativo. Dallo estato di esecuzione il processo pu`: terminare, venendo rimosso dalla lista dei oprocessi; venire sospeso a seguito di una richiesta di I/O oppure di accessoad altra risorsa e venire inserito nella coda dei processi in attesa sino aquando tale risorsa non sar` disponibile; venire sospeso su richiesta di altro aprocesso o del sistema operativo ed essere trasferito nella coda dei processi 46
  • 61. Figura 3.15: Grafo di transizione dei processipronti sino a quando non verr` richiesta la sua riattivazione. Dalla coda di aattesa il processo viene rimosso in seguito al completamento dell’operazionedi I/O oppure per essersi liberata la risorsa richiesta, venendo inserito nellalista dei processi pronti per continuare l’esecuzione [AKD01].3.6.2 ThreadIn Java i processi vengono chiamati thread e costituiscono dei flussi di esecu-zione. La JVM ` basata su un’architettura multi-thread per la cui gestione e` fornita la classe Thread afferente al package java.lang. Ogni thread siepu` trovare nei seguenti stati: attivo nel caso in cui sia stata lanciata la sua oesecuzione e questa non si ` ancora completata; ` definito inattivo in caso e econtrario; sospeso nel caso in cui la sua esecuzione sia stata temporaneamen-te interrotta; lo stato di sospensione potr` essere successivamente eliminato aripristinandone l’esecuzione. Tutte le trasformazioni di stato di un thread sono effettuate a seguitodell’attivazione di opportuni metodi della classe Thread. Le funzionalit` pi` a usignificative sono: • allocazione e inizializzazione: coincide con la creazione dell’oggetto della classe Thread che dovr` contenere il modello computazionale del a 47
  • 62. thread e con la definizione del contesto in cui dovr` essere eseguito il a processo; in tale fase il thread ` inattivo; e• attivazione: causa l’inizio della computazione di pertinenza del thread; il thread diviene attivo;• sospensione: causa una (temporanea) interruzione dell’esecuzione del thread; il thread passa nello stato di thread attivo ma sospeso;• ripristino: causa la riattivazione dell’esecuzione del thread sospeso; il thread ritorna nello stato attivo;• terminazione forzata: causa una fine anomala nell’esecuzione del th- read; il thread ritorna nello stato inattivo;• distruzione: causa la deallocazione del thread, senza che ci` comporti o necessariamente il rilascio delle risorse da esso impegnate.I metodi principali per l’utilizzo dei Thread sono:• Thread(): costruttore, alloca un nuovo thread assegnandogli un iden- tificatore.• run(): permette di specificare il modello computazionale del thread. Se il thread si origina da una sottoclasse di Thread, tale sottoclasse deve riscrivere il metodo run() della classe Thread.• start(): induce l’esecuzione del thread portandolo nello stato di th- read attivo. La macchina virtuale Java richiama il metodo run() del thread.• currentThread(): restituisce un riferimento al thread correntemente in esecuzione.• yield(): causa la temporanea sospensione del thread corrente, per- mettendo ad altri thread in attesa di divenire attivi.• sleep(): causa la sospensione del thread per il numero di millisecondi indicato dal parametro millis.• join(): induce il completamento del thread corrente prima di rendere possibile l’attivazione del thread successivo. 48
  • 63. I metodi suspended(), resume() e stop() che in molti conoscono non sonopi` utilizzabili poich` il loro utilizzo poteva provocare stalli. La creazione u edi un thread pu` avvenire seguendo due metodologie: per ereditariet` dalla o aclasse Thread o implementando l’interfaccia Runnable. La scelta di unasoluzione o dell’altra ` dettata da preferenze personali e dalle caratteristiche edell’applicazione. Per questo elaborato si ` scelto di estendere la classe eThread svolgendo semplici operazioni: 1. costruire una sottoclasse della classe Thread; 2. sovrascrivere il metodo run(). Il metodo run() della sottoclasse fornisce in tal modo il corpo del nuovothread. Una volta generata la sottoclasse, ` possibile creare un thread per eistanza della sottoclasse di pertinenza ed eseguire il thread per chiamata delmetodo start(), il quale invoca a sua volta il metodo run(). Eventualmentesi pu` gestire il funzionamento con i metodi sleep() e join(). o3.6.3 SincronizzazioneIn un sistema multi-thread coesistono in generale molti thread attivi, i qua-li costituiscono processi in grado di evolvere autonomamente e capaci diinoltrarsi reciprocamente messaggi rendendo possibili situazioni in cui po-trebbe essere richiesta la contemporanea esecuzione di due o pi` metodi di uuno stesso thread oppure l’accesso contemporaneo allo stesso oggetto. En-trambe le situazioni possono dimostrarsi estremamente critiche causandol’inconsistenza dei dati o dei risultati. Java mette a disposizione nativamente varie tecniche di sincronizzazione,ed altre librerie sempre pi` sofisticate si aggiungono con il passare del tempo. uI due idiomi principali sono synchronized methods e synchronized statementsche si occupano di tutte le questioni legate all’acquisizione e rilascio dei lockmentre librerie come java.util.concurrent forniscono strumenti a bassolivello per poter sincronizzare solo quegli oggetti che necessitano di accessiesclusivi, che se ben gestiti garantiscono prestazioni superiori a causa delleridotte sezioni sequenziali. 49
  • 64. Sincronizzazione implicitaSincronizzare i metodi significa garantire che l’esecuzione di ciascuno di essinon venga interrotta dall’esecuzione di altri, neppure in caso di temporaneasospensione del thread a cui i metodi appartengono. In Java la sincronizza-zione dei metodi ` ottenuta mediante l’uso del modificatore synchronized e(Synchronized Methods). Qualora i metodi di un thread siano dichiaratisincronizzati, l’attivazione di uno di essi blocca le richieste di esecuzione deimetodi rimanenti quando inoltrate alla stessa istanza di thread. Il modifica-tore synchronized pu` essere applicato anche ai metodi di classe statici di ouna stessa classe causandone l’esecuzione seriale, in modo che nessuno di essipossa essere interrotto dall’esecuzione dei rimanenti, nel caso che venganochiamati da thread diversi. Eccone un semplice esempio tratto da [jav]:public class S y n c h r o n i z e d C o u n t e r { private int c = 0 ; public synchronized void i n c r e m e n t ( ) { c++; } public synchronized void decrement ( ) { c −−; } public synchronized int v a l u e ( ) { return c ; }} Il modificatore synchronized pu` essere utilizzato anche per sincroniz- ozare porzioni di codice (Synchronized Statements) o addirittura una solarisorsa, aumentando il grado di parallelismopublic class MsLunch { private long c1 = 0 ; private long c2 = 0 ; private O b j e c t l o c k 1 = new O b j e c t ( ) ; private O b j e c t l o c k 2 = new O b j e c t ( ) ; public void i n c 1 ( ) { synchronized ( l o c k 1 ) { c1++; } } 50
  • 65. public void i n c 2 ( ) { synchronized ( l o c k 2 ) { c2++; } }}Sincronizzazione esplicitaIn Java ` possibile sincronizzare pi` thread utilizzando una sincronizzazione e uesplicita, nella quale si indica un oggetto sul quale acquisire il lock. Questooggetto (anche un semplice java.Object) ` una risorsa software che viene econdivisa fra i thread operanti lo scambio dati, in modo da forzare gli stessia sincronizzarsi. A differenza dei metodi sincronizzati, in questo modo ` epossibile eseguire lo stesso metodo da parte di pi` thread in modo parallelo ued accedere in modo sequenziale agli sugli oggetti sensibili. Questa sincro-nizzazione a grana fine consente un aumento del livello di parallelismo equindi delle prestazioni. Nell’esempio di figura 3.16 si cita il caso in cui pi` thread si debbano usincronizzare per l’accesso ad un oggetto. Prima di potrevi leggere o scrivereogni thread deve acquisire il lock esplicito sulla risorsa. Figura 3.16: Sincronizzazione per l’accesso ad una risorsaI Lock messi a disposizione dalla libreria java.util.concurrent sono moltosemplici, altri pi` sofisticati si possono trovare in java.util.concurrent.locks. uEssi lavorano in modo simile ai lock impliciti forniti da synchronized: an- 51
  • 66. ch’essi consentono che un solo thread alla volta possa acquisire il Lock sullarisorsa ma in pi` offrono il supporto alle primitive wait/notify. u La classe Object fornisce gi` dei metodi primitivi per la sincronizzazione: avoid w a i t ( )native void w a i t ( long t i m e o u t )native void n o t i f y ( )native void n o t i f y A l l ( ) I metodi wait() sospendono il thread correntemente in esecuzione, fa-cendogli perdere il controllo della risorsa condivisa in quel momento pos-seduta. I thread sospesi e in attesa del rilascio di una risorsa condivisapossono essere risvegliati da altro thread operante sulla stessa risorsa con ilmetodo notify() oppure con il metodo notifyAll(). In Java il metodowait(timeout) pu` limitare la sospensione del thread corrente al tempo in- odicato dal parametro timeout mentre il wait() attende all’infinito il risveglioda parte di un altro processo. Il metodo notify() risveglia un singolo thread che ` stato sospeso in eattesa del rilascio di una risorsa condivisa. Il thread pu` ottenere il possesso odella risorsa eseguendo un metodo sincronizzato appartenente alla risorsastessa. Il metodo notifyAll() risveglia tutti i thread che sono stati sospesiin attesa del rilascio di una risorsa condivisa. I metodi risvegliati compe-tono per riottenere il controllo della risorsa in base alla loro priorit` e/o al amomento della loro sospensione. In questo caso l’uso delle semplici primitive wait/notify ` estremamente einefficiente: un thread pu` chiamare il metodo wait() per bloccarsi e lasciare ol’uso delle risorse agli altri processi, in attesa di essere risvegliato attraversouna chiamata notify() o notifyAll(). Il processo che riceve una notificaper` non ha la garanzia di avere l’accesso alla risorsa condivisa (nel caso di omonitor) poich` ` la JVM a sceglierne uno casualmente e potrebbe dover e etornare in attesa: ogni processo ha una variabile condizione generica sullaquale porsi in attesa. L’utilizzo di variabili condizione specifiche permettono di svegliare soloil processo interessato che trover` la risorsa sicuramente libera senza do- aver concorrere ulteriormente e magari dover tornare in attesa. L’interfacciaCondition fa parte del package java.util.concurrent.locks. In detta-glio, esse permettono di sospendere l’esecuzione di un thread (wait) fino 52
  • 67. alla notifica da parte di un altro thread nel caso in cui sia verificata unaspecifica condizione. Anche in questo caso l’accesso alle risorse ` protetto e esincronizzato tramite un lock associato alla condizione. Il comportamentodel comando wait ` simile a quello della classe object: atomicamente rilascia eil lock associato e sospende il thread corrente. Ogni istanza di variabile condizione ` intrinsecamente legata ad un lock. ePer ottenerne un’altra per una particolare istanza di Lock si deve usare ilmetodo newCondition(). Prendiamo come esempio il classico problema del produttore/consuma-tore, nel quale un metodo put riempie un buffer limitato ed un metodo takeestragga da questo buffer. Se il metodo take legge il buffer vuoto, esso do-vr` sospendersi finch` un oggetto nel buffer si renda disponibile; in modo a eanalogo se il metodo put incontra il buffer pieno, il thread associato dev’es-sere sospeso fino alla disponibilit` di spazio. Il modo migliore per risolvere aquesto conflitto ` tenere i thread produttori separati dai thread consumatori ein insiemi wait-set separati, in modo tale da poter risvegliare all’occorenzasolo un singolo thread quando diventa disponibile dello spazio su buffer ouna risorsa da estrarre. Questo ` possibile solamente utilizzando sue istanze edi variabili condizione. Ecco un esempio dell’implementazione di questo problema nella classeArrayBlockingQueue. class BoundedBuffer { final Lock l o c k = new ReentrantLock ( ) ; final C o n d i t i o n n o t F u l l = l o c k . newCondition ( ) ; final C o n d i t i o n notEmpty = l o c k . newCondition ( ) ; final O b j e c t [ ] i t e m s = new O b j e c t [ 1 0 0 ] ; int p u t p t r , t a k e p t r , count ; public void put ( O b j e c t x ) throws I n t e r r u p t e d E x c e p t i o n { lock . lock ( ) ; try { while ( count == i t e m s . l e n g t h ) notFull . await ( ) ; items [ putptr ] = x ; if (++p u t p t r == i t e m s . l e n g t h ) p u t p t r = 0 ; ++count ; notEmpty . s i g n a l ( ) ; } finally { lock . unlock ( ) ; } 53
  • 68. } public O b j e c t t a k e ( ) throws I n t e r r u p t e d E x c e p t i o n { lock . lock ( ) ; try { while ( count == 0 ) notEmpty . a w a i t ( ) ; Object x = items [ t ake pt r ] ; if (++t a k e p t r == i t e m s . l e n g t h ) t a k e p t r = 0 ; −−count ; notFull . signal ( ) ; return x ; } finally { lock . unlock ( ) ; } } }java.util.concurrentIl package java.util.concurrent [Lea05], introdotto da J2SE 5, ` una enuova collezione di metodi per la sincronizzazione, nato dal fatto che java,nonostante prevedesse dei meccanismi per la programmazione concorrentesin dalla nascita, non ha da allora fatto significativi passi in avanti in questocampo, potendo contare solo su meccanismi primitivi e rigidi. I package diprincipale interesse al suo interno sono: • java.util.concurrent.locks: contiene gli usuali strumenti a sup- porto dei problemi di competizione e cooperazione. Al suo interno sono presenti i classici metodi lock() e unlock() che bloccano e ri- lasciano la risorsa ma anche trylock() che acquisisce il lock solo se libero in quel momento. • java.util.concurrent.atomic: contiene operazioni atomiche del ti- po TestAndSet e strutture dati tipo AtomicInteger o AtomicBoolean che consentono l’accesso atomico ed esclusivo implicitamente, attra- verso dei metodi sincronizzati.Il protocollo di utilizzo di Lock ` il seguente: eLock l = una i m p l e m e n t a z i o n e d i Lock ;l . lock ( ) ;try { 54
  • 69. regione c r i t i c a sulla risorsa condivisa}finally { l . unlock ( ) ;} All’interno di java.util.concurrent.locks sono presenti anche deilock asimmetrici: ReadWriteLock. Si tratta di una coppia di lock asso-ciati tra loro uno, detto lock di lettura (read lock), consente ai thread chelo possiedono di fare letture sulla risorsa protetta l’altro, detto lock di scrit-tura (write lock), consente ai thread che lo possiedono di fare scritture sullarisorsa protetta. Essi sono utili per proteggere l’accesso a strutture condi-vise evitando fenomeni di interferenza e lunghe attese (passive) senza com-promettere eccessivamente il grado di parallelismo poich` molteplici thread epossono ottenere il read lock concorrentemente mentre un solo thread allavolta pu` ottenere il write lock. Se un thread chiede il lock di lettura, pu` o oprocedere anche se altri thread gi` lo possiedono ma solo se nessuno gi` a apossiede il lock in scrittura. Se un thread chiede il lock in scrittura, pu` oprocedere solo quando ottiene l’accesso in mutua esclusione alla risorsa; inquesto caso tutti gli altri thread non possono disporre n´ del lock di scrittura en´ di quello in lettura. Rispetto alla semplice mutua esclusione, il livello di eparallelismo raggiungibile dipende fortemente dalla frequenza e dal tipo diaccessi dei quali ` oggetto la risorsa protetta. e 55
  • 70. 56
  • 71. Capitolo 4Regole Associative eClustering: algoritmiparalleli “Il successo deriva da un’idea semplice eseguita magistralmente bene.” Anonimo. 57
  • 72. L’introduzione di sitemi paralleli a memoria condivisa ha conosciuto lasua massima espansione solo di recente, per questo ci sono ancora pochialgoritmi ottimizzati a questo scopo e pochi sono gli studi completi di que-sto argomento, ma proprio per questo l’interesse ` crescente sia in ambito eindustriale che accademico, si vedano [ZOPL04] [ZOPL96] [Zak99] [LOP07][CHX98] [ELICICRC] [JA02]. Figura 4.1: Struttura degli algoritmi di Data Mining pi` comuni [JA02] u Si noti come gli algoritmi principali per il Data Mining abbiano la stessastruttura in comune, riassunta in figura 4.1. L’elemento della riduzione ` eun oggetto aggiornato all’interno del ciclo locale in ogni processo (conteg-gio supporti per Apriori o calcolo distanze per K-Means). Nonostante ladimensione di questo oggetto in molti casi pu` essere molto grande, dovuta oa una grossa quantit` di oggetti da pochi byte, il tempo di esecuzione totale a` dovuto in gran parte dalla funzione process. La sfida principale che ci siepone nel parallelizzare tali algoritmi in modo efficiente consiste nella cor-rettezza derivante dalle cos` dette race conditions ovvero consistenza degli ıstati degli oggetti quando pi` processi scrivono nello stesso oggetto. Si deve ucercare il giusto compromesso tra replicazione di codice e ridondanza di dati,condizioni bloccanti e riduzione di dati. Se si usa uno schema completamen-te bloccante i ritardi dovuti alle sincronizzazioni potrebbero essere troppoelevati, compromettendo l’aumento di prestazioni cercato. In particolare Gagan Agrawal nel suo studio di tecniche di parallelizza-zione di algoritmi di Data Mining per sistemi a memoria condivisa [JA02],discute su come sia fondamentale approfondire l’uso di lock per le risorsecondivise. Sono state sviluppate cinque tecniche, vedi figura 4.2: • Replicazione completa: la via pi` semplice per evitare la concor- u renza ` creare una copia di tutti gli oggetti condivisi per ogni processo; e 58
  • 73. Figura 4.2: Utilizzo della memoria nei vari schemi di Locking [JA02] ognuno di essi aggiorner` la propria copia ed alla fine tutte vengono a ridotte con opportune operazioni in una unica globale. Il problema maggiore ` l’occupazione di memoria. e• Locking completo: la strada opposta alla precedente consiste di sincronizzare ogni accesso alle risorse con dei semafori. In questo modo nessun oggetto dovr` essere replicato ma sono possibili grossi ritardi a se il numero di conflitti ` elevato, oltre all’occupazione di memoria e dovuta al grosso numero di lock. In questo caso si avrebbero anche dei ritardi indotti di cache-missed, ovvero se una risorsa cercata non ` e nella cache si devono cercare sia i lock che i dati.• Locking completo ottimizzato: basato sul precedente, evita la dop- pia cache missed allocando elemento e lock su locazioni consecutive.• Locking fisso: per evitare i cache missed dovuti al grosso numero di lock, se ne allocano un numero l prefissato in modo che l’elemento i acceda al lock i mod l.• Locking sensibile alla cache: combinazione delle ultime due tec- niche, utilizza un singolo lock per ogni oggetto allocato nello stesso blocco di memoria 59
  • 74. Figura 4.3: Compromessi tra le varie tecniche [JA02] La figura 4.3 aiuta nel momento in cui si debbono compiere delle sceltesu quale aspetto favorire rispetto agli altri, tra velocit` e semplicit`, occu- a apazione di memoria o di processore: il programmatore ` responsabile della ecreazione e dell’inizializzazione degli oggetti condivisi o replicati e della lororiduzione finale. Questo schema spazia dalla salvaguardia della memoria allivello di parallelismo raggiunto, considerando aspetti pi` approfonditi come ul’utilizzo della cache. Nel valutare quale sia la tecnica pi` adatta alle nostre esigenze, si deve utener conto anche di un altro aspetto: le dimensioni degli oggetti da ridurrepotrebbero essere molto grandi, con un utilizzo di memoria non sottovaluta-bile. Ci sono, tuttavia, due valide ragioni per le quali gli oggetti in confittodebbano essere separati dalle altre strutture dati: eliminazione delle falsecondivisioni e, per la programmazione estrema, la possibilit` di ottimizzare al’uso di cache e locks. Si pensi ad esempio come nella versione parallela diApriori sia necessario replicare i contatori pur mantenendo un solo hash treecondiviso.4.1 Regole associativeUno dei pi` grandi problemi del Data Mining ` la ricerca delle regole asso- u eciative da database di transazioni [HKK97], dove ogni transazione contie-ne un insieme di articoli. La maggior parte del tempo di elaborazione inquesti processi ` dovuto al calcolo dei sottoinsiemi di articoli frequenti nel edatabase di transazioni (chiamati candidati ). Se il database ` considerevol- emente grande, cos` come il numero di articoli, il numero totale di canditati ı` esponenzialmente elevato (vedi capitolo 2). Tuttavia le attuali tecnicheedi ricerca di regole associative [AS94] [HS95] cercano di ridurre lo spazio 60
  • 75. di ricerca (prune) richiedendo un supporto minimo (numero di occorrenzedei candidati nel database di transazioni) ai candidati in esame. Lo statodell’arte di questo tipo di algoritmi ` rappresentato da Apriori [AS94]. Esso eelimina in modo aggressivo il set di candidati di dimensione k, seguendo laseguente osservazione: un candidato di dimensione k ` frequente (soddisfa il supporto e minimo) solo se ogni suo sottoinsieme ` frequente. ePer rendere questo processo efficiente, l’algoritmo mantiene tutti i potenzialicandidati di dimensione k in un hash tree, vedi paragrafo 2.1.1. Questoalgoritmo non richiede che l’intero database stia in memoria, ma che ci stiaalmeno l’hash tree; in caso contrario l’albero dovr` essere partizionato. a Mohammed Zaki nel suo “viaggio attraverso gli algoritmi paralleli e di-stribuiti allo stato dell’arte per la scoperta di regole associative” [Zak99],dedica una sezione agli algoritmi a memoria condivisa, che riporta anchesuoi lavori in [ZOPL04], e che per sua stessa ammissione non hanno ancoraavuto la necessaria attenzione. In figura 4.4 ` rappresentata la tassonomia eFigura 4.4: Algoritmi paralleli per la scoperta di Regole Associative [Zak99]degli algoritmi paralleli per la scoperta di regole associative, dividendo in duemacro aree: bilanciamento del carico statico e dinamico. Gli algoritmi mag-giormente sviluppati sono del tipo statico e da qui di suddividono ancora per 61
  • 76. tipologia di memoria: distribuita, condivisa e gerarchica. Ognuna di questearee ` suddivisa a loro volta per metododologia di parallelizzazione: per dati eo per processi. Count Distribution, per esempio, ha un bilanciamento delcarico statico, a memoria distribuita e a parallelizzazione di dati.4.1.1 Versioni parallele a memoria distribuitaPur essendo Apriori l’algoritmo sequenziale per la ricerca di regole associati-ve pi` famoso, varie sue implementazioni parallele sono state presentate nel upassato: una versione sviluppata dal dipartimento di informatica dell’univer-sit` di Little Rock (Arkansas) [Chi06] si basa sull’utilizzo di trie piuttosto di ahash tree, come suggerito da Bodon nelle sue ricerche [Bod03]. Ye e Chiangpropongono di partizionare il database delle transazioni (T DB) in N par-tizioni disgiunte T DB1 , T DB2 , · · · , T DBn da distribuire a N nodi diversidove ognuno ricerca localmente i propri k-itemset candidati, li invia al nodo0 ed esso calcola la somma di tutti i k-itemset candidati ed elimina quelliche non soddisfano il supporto minimo. Per trovare gli 1-itemset frequenti,ogni nodo conta i suoi 1-itemset locali ed invia il risultato al nodo 0; quandoesso riceve i supporti da tutti gli altri N-1 nodi, genera il 1-itemset frequenteglobale, L1 . Lo stesso processo ` applicato per trovare i 2-itemset frequenti, ebasandosi su L1 e cos` via finch` non ` pi` possibile generare candidati. ı e e u Sono sempre basate su Apriori anche le proposte di Han, Karypis eKumar, Count Distribution (CD) e Data Distribution (DD) [AS96], tra iprimi algoritmi di questo tipo ad avere una buona rilevanza.Count DistributionCome si nota in figura 4.5, Count Distribution si basa sul principio dellecomputazioni ridondanti in parallelo piuttosto che processori in attesa, perevitare le comunicazioni. Il primo passo ` speciale. ePasso 1: • Ogni processore P i genera dinamicamente il suo insieme candidato i locale C1 sulla base degli oggetti presenti nella sua porzione locale di transazione, Di . L’insieme C1 pu` esser diverso per ogni processore, i o bisogner` quindi prestare la massima attenzione nello scambio delle a frequenze. 62
  • 77. Figura 4.5: Algoritmo Count Distribution [HKK97]Passo k>1 : 1. Ogni processore P i genera l’insieme Ck completo, usando l’intero in- sieme Lk−1 creato alla fine del passo k − 1. 2. Ogni processore P i conta il supporto del proprio insieme candidato Ck sulla sua partizione locale Di . 3. Ogni processore P i scambia il conteggio dei supporti di Ck con ogni altro processore per avere un supporto globale. In questo passo viene forzata la sincronizzazione. 4. Ogni processore P i calcola l’insieme dei frequenti Lk a partire dall’in- sieme Ck dei candidati, appena costruito. 5. Ogni processore P i decide indipendentemente se terminare la compu- tazione oppure se fermarsi. I passi 1-2 e 4-5 sono simili all’algoritmo seriale, mentre il passo 3 defi-nisce come avviene lo scambio dei contatori locali per ottenere l’insieme Ckcompleto. Se ogni processore ha lo stesso Ck , la somma dei conteggi pu` oavvenire come somma di array in parallelo, che richiede O(log(n)) passi dicomunicazione. 63
  • 78. L’algoritmo Count Distribution ` scalabile linearmente ed ha un ottimo spee- edup ma ha due difetti: non parallelizza la costuzione dell’hash tree (che co-munque richiede una piccola parte del tempo totale di esecuzione) e se questonon pu` esser contenuto interamente in memoria sono necessari molti accessi oa disco.Data Distribution Figura 4.6: Algoritmo Data Distribution [HKK97] Data Distribution risolve questi problemi partizionando l’insieme di can-didati ed assegnandone una partizione ad ogni processore, esplorando inmodo pi` efficiente il quantiativo di memoria aggregata all’aumentare dei uprocessori. Tuttavia anche questo algoritmo soffre di alcune inefficienze.Passo 1: • Ogni processore P i genera dinamicamente il suo insieme candidato i locale C1 sulla base degli oggetti presenti nella sua porzione locale di transazione, Di . L’insieme C1 pu` esser diverso per ogni processore, i o bisogner` quindi prestare la massima attenzione nello scambio delle a frequenze.Passo k>1 : 1. Ogni processore P i genera l’insieme candidato Ck dall’insieme di fre- quenti Lk−1 . Esso mantiene solo 1/N-esimo degli insiemi che formano 64
  • 79. i Ck che dovr` contare. Quali insiemi siano mantenuti dipende dall’id a del processore e possono essere elaborati senza comunicazioni con gli altri processori. Una possibilit` ` assegnare gli insiemi con scheduling ae i round-robin. Gli insiemi Ck hanno intersezione vuota tra loro, mentre la loro unione da l’insieme originale, Ck . 2. Ogni processore P i conta i supporti per gli oggetti nel suo insieme i candidato locale Ck usando sia i suoi dati locali che quelli che riceve dagli altri processori. Per fare ci` dovr` allocare P buffer (uno per o a ogni processore) che immagazzini le transazioni che riceve dagli altri processori. 3. Ogni processore P i calcola Li usando l’insieme candidato locale Ck . k i Anche tutti gli insiemi Li sono disgiunti e la loro unione da l’insieme k dei frequenti totale Lk . 4. I processori scambiano tra loro l’insieme locale Li cosicch` ognuno di k e loro possa avere l’insieme globale Lk per generare Ck+1 . Questo passo richiede una sincronizzazione forzata. A questo punto ogni processore decide se continuare o terminare (scelta identica per tutti). Le performance di Data Distribution sono peggiori rispetto a quelle diCount Distribution, dalle 10 alle 20 volte pi` lento, come riportato in [AS96] ue riportato nel paragrafo 4.1.3. La causa principale ` la mole di comuni- ecazioni e le operazioni ridondanti che si devono effettuare, che portano a 3effetti: un alto tasso di ritardo introdotto dalle frequenti comunicazioni traprocessori; la possibilit` di avere dei processori in attesa forzata; la necessit` a adi elaborare ogni transazione in hash tree multipli causando computazioniridondanti. Per ovviare alle inefficienze di questi algoritmi ne sono stati presentatidegli altri, tra i quali Candidate Distribution [AS96], Intelligent Data Distri-bution [HKK97], sviluppato a partire da Data Distribution minimizzando iritardi dovuti alle frequenti comunicazioni, alle inutili attese ed eliminandole computazioni ridondanti. Hybrid Distribution [HKK97] unisce i vantag-gi di Count Distribution e Intelligent Data Distribution raggruppando di-manicamente i processori e partizionando l’insieme dei candidati in modotale da mantenere un buon bilanciamento. Tutti questi algoritmi paralleli 65
  • 80. sono riconducibili al primo Apriori, ma altri ancora ne sono stati propo-sti, come NPA, Non Partitioned Apriori, SPA, Simply Partitioned Apriori,HPA, Hash Partitioned Apriori e HPA-ELD, Hash Partitioned Apriori withExtremely Large Itemset Duplication in [SK96].4.1.2 Versioni parallele a memoria condivisaNonostante le infrastrutture a memoria condivisa (Shared Memory) esista-no da tempo, le soluzioni domestiche si stanno espandendo solo da pochisemestri e la loro diffusione ` ancora limitata a dual core e quad core, per ei pi` esigenti, e ad una piccola percentuale del parco macchine mondiale. uL’introduzione dei processori con pi` unit` di calcolo al loro interno (multi u acore) ha portato ad indubbi vantaggi sulle applicazioni multi tasking, chepossono sfruttare tutte le unit` di calcolo parallelamente. a Al contrario, pu` accadere che applicazioni singole non siano sviluppa- ote utilizzando i thread, con il risultato che un singolo core sia abilitato alpuro lavoro di calcolo. Solo recentemente, quindi, c’` un interesse crescente eper gli algoritmi ottimizzati per le macchine a memoria condivisa. Esegui-re su processori multi core algoritmi sequenziali non ` quindi efficiente, ma ealtrettanto si pu` dire per algoritmi paralleli sviluppati per sistemi con me- omoria distribuita che sfruttano magari le primitive MPI per le operazioni didistribuzione e riduzione dei risultati.Common Candidate Partitioned DatabaseTra gli algoritmi basati su Apriori uno dei primi algoritmi proposti fu Com-mon Candidate Partitioned Database [ZOPL96]. Esso utilizza un approccioData Parallel, partizionando il database logicamente in porzioni uguali, edogni processore elabora in modo sincrono un hash tree canditato globale. Inquesto modo ` parallelizzata anche la costruzione dell’insieme di candidati: eogni processore ne genera una porzione disgiunta e l’unione di tali insiemi ne` il risultato. Per costruire l’hash tree in parallelo si associa un lock ad ognienodo foglia. Quando un processore vuole inserire un candidato sull’albero,comincia dalla radice e codifica gli oggetti uno alla volta fino a raggiunge-re la foglia; ne acquisisce il lock ed inserisce il candidato. Per contare ilsupporto, ogni processore calcola le frequenze dalla propria partizione logi- 66
  • 81. ca. A questo algoritmo di base, si possono apporre ulteriori modifiche perottimizzarnel’uso dei processori o della memoria.Partitioned Candidate Common DatabaseNell’algoritmo Partitioned Candidate Common Database [ZOPL96] i proces-sori costruiscono insiemi candidati disgiunti e scandiscono l’intero databaseper calcolarne i supporti. Tuttavia i ritardi introdotti dai continui I/O edaccessi al disco sono inaccettabili e rendono l’algoritmo troppo lento.4.1.3 Risultati sperimentaliVersioni parallele a memoria distribuitaIn [HKK97] sono stati testati gli algoritmi citati nel paragrafo 4.1.1, eviden-ziando come il semplice Count Distribution sia in realt` molto efficiente. I atest, ovviamente, sono stati effettuati su macchine parallele e non su pro-cessori multi-core, scopo di questa tesi, utilizzando le librerie MPI (MessagePassing Interface) [VKK94] per le comunicazioni. Per questi test ` stato eutilizzato un nodo Cray T3E con 128 processori Dec Alpha a 600MHz e512MByte di memoria interconnessi tra loro con una rete da 430MByte/se-condo e comunicazioni MPI. Han, Karypis e Kumar hanno svolto numerosi Figura 4.7: Confronto scalabilit` algortimi paralleli [HKK97] a 67
  • 82. test dai quali si evince che Count Distribution ` la base migliore su cui la- evorare, infatti risulta pi` veloce rispetto agli altri anche all’aumentare del unumero di processori (vedi figura 4.7). Il test di scalabilit` ` stato svolto a econ 50 mila transazioni per processore. Sia Ts E= P × Tpdove E ` l’efficienza, P il numero di processori, Tp il tempo di esecuzione eparallelo e Ts il tempo di esecuzione dell’algoritmo seriale. Un algoritmo siconsidera scalabile se l’efficienza ` mantenuta costante al crescere del nume- ero di processori e della dimensione del problema. In altre parole, se P × Tpe Ts rimangono dello stesso ordine anche al variare del numero di processorie di transazioni. Hybrid Algorithm e Intelligent Data Distribution, moltopi` sofisticati rispetto a Count Distribution, non sempre sono nettamente upi` veloci. Come previsto, Data Distribution ` poco scalabile. La figura u e Figura 4.8: Confronto speedup algortimi paralleli [HKK97]4.8 mette a confronto gli speedup di Count Distribution, Intelligent DataDistribution e Hybrid Algorithm, al crescere di P da 4 a 64, un numerodi transazioni di 1,3 milioni e 0,7 milioni di candidati. Count Distributionha un buon speedup fino a 32 processori poi subentrano i limiti dei tem-pi dovuti ai colli di bottiglia conosciuti, costruzione seriale dell’hash tree el’operazione di somma dei conteggi (che in MPI ` un’operazione di global e 68
  • 83. reduction). Con 4 processori il tempo di costuzione dell’hash tree ` di so- elo il 3,1% del tempo totale di esecuzione, che cresce fino al 24,8% con 64processori. Nell’esperimento di figura 4.9 si misura il tempo di esucuzione Figura 4.9: Confronto tempi di risposta algortimi paralleli [HKK97]degli stessi algoritmi fissando il numero di processori (64) e di canditati (0,7milioni), al crescere del numero di transazioni: da 1,3 milioni a 26,1 milioni.Anche in questo caso Count Distribution scala molto bene mentre Intelli-gent Data Distribution soffre di ritardi dovuti alla difficolt` di bilanciare in amodo corretto il numero di candidati per processore all’aumentare di P (cheimplica una diminuzione proporzionale della dimensione dei candidati perogni processore).Versioni parallele a memoria condivisaGli algoritmi paralleli per sistemi a memoria condivisa sono ancora pochi,giovani e immaturi, come si dimostrer` nel paragrafo 4.1.4. In [ZOPL96] asono stati proposti e testati gli algoritmi discussi nel paragrafo precedente eloro ottimizzazione, presentando i risultati solo per Common Candidate Par-titioned Database, tralasciando Partitioned Candidate Common Databaseper le prestazioni al di sotto delle aspettative. Gli esperimenti, in questo caso, sono stati fatti su una macchina SGIPower Challenge a con 256MByte di memoria condivisa e 12 processori MIPS 69
  • 84. Figura 4.10: Propriet` del database per i test dell’algoritmo CCPD a[ZOPL96]a 100MHz. La figura 4.10 rappresenta i database utilizzati per le prove, condimensioni (Total Size) da 2,6 a 69,8MByte, numero di transazioni D da100 mila a 1,6 milioni, dimensione media delle transazioni (T ) da 5 a 20 emassima dimensione potenziale dell’insieme dei frequenti (I ) da 2 a 6. Ilnumero massimo di insiemi dei frequenti potenziali (L) ` di 2000 mentre il enumero di oggetti (N ) ` pari a 1000. eFigura 4.11: Speedup dell’algoritmo CCPD senza e con lettura del database[ZOPL96] La figura 4.11 presenta gli speedup ottenuti dall’algoritmo Common Can-didate Partitioned Database con database differenti al crescere del numero 70
  • 85. di processori, per testare la bont` della parallelizzazione. Nel grafico di si- anistra si presenta lo speedup senza prendere in considerazione il tempo dilettura del database iniziale, mentre nel grafico di destra viene preso in con-siderazione anche questo tempo (vedi 4.12 per i tempi e le percentuali ditempo occupate). Come si nota gli speedup migliori si ottengono con grandidatabase. Figura 4.12: Tempi di lettura del database [ZOPL96]4.1.4 Problemi ApertiNonostante le recenti proposte di algoritmi altamente performanti in questocampo, ci sono ancora parecchi problemi che richiedono una certa attenzione.Alta dimensionalit` aGli attuali algoritmi possono elaborare solo pochi migliaia di oggetti. Ilproblema ` il seguente: La seconda iterazione che conta i supporti di tutti egli insiemi di dimensione 2 (2-itemset) ha complessit` quadratica poich` a edobbiamo considerare tutte le coppie e non ` ancora possibile nessun pruning ein questa fase. In genere gli algoritmi non hanno complessit` lineare nel anumero di dimensioni, ecco perch` c’` la necessit` di nuovi algoritmi scalabili e e ain questo senso.Dimensione del databaseMentre i database continuano a crescere di dimensione, gli attuali algoritmipossono elaborare solo poche decine di gigabytes di dati, ben lontani dal li- 71
  • 86. mite dei terabytes. Molti algoritmi scandiscono addirittura l’intero databasepi` volte: trovare tutti gli insiemi frequenti in un solo passo ` un problema u eaperto. Un ulteriore fattore che ne limita la scalabilit` ` il dover mante- a enere gli insiemi candidati in memoria e per grandi moli di dati, certamentenon potranno essere contenuti interamente portando a lunghe e lente fasidi lettura e scrittura su disco. Si stanno perci` studiando delle approcci di opartizionamento verticale e tecniche dagli algoritmi parallel-join.Localizzazione di datiAl giorno d’oggi i grandi database sono distribuiti logicamente o fisicamente;le organizzazioni che sono geograficamente distribuite necessitano di approc-ci decentralizzati per poter attuare con profito tecniche di ricerca delle rego-le associative. In questo caso i dati potrebbero essere partizionati in modoorizzontale (dove siti diversi hanno transazioni diverse) o verticale (dove sitidiversi hanno oggetti diversi). Gli studi attuali vanno nella direzione di unapproccio orizzontale.Asimmetria dei datiUno dei problemi che affligge gli algoritmi di questa rassegna ` la sensibilit` e aall’asimmetria dei dati. Molti algoritmi partizionano i database orizzontal-mente in blocchi di uguali dimensione, tuttavia il numero di insiemi frequentigenerato da ognuno di questi blocchi pu` essere molto diverso, il che implica oun carico di lavoro oneroso per alcuni processori e leggero per altri. Ci` oporta a tempi di attesa per alcuni processori con relativo allungamento deitempi di computazione totale.Un altro tipo di asimmetria dipende se gli insiemi sono frequenti in moltiblocchi o solo in pochi. La maggior parte degli algoritmi richiede una bassaasimmetria per un buon bilanciamento; un’alta asimmetria, al contrario, ` enecessaria per applicare con successo la fase di pruning per ridurre l’insieme.Queste due esigenze contrastanti devono essere conciliate.Bilanciamento del carico dinamicoTutti gli attuali algoritmi esistenti utilizzano un bilanciamento del caricostatico, basato sul partizionamento dei dati nella fase iniziale, assumendo 72
  • 87. un ambiente dedicato ed omogeneo, ma questo ` lontano dalla realt`, so- e aprattutto nei sistemi distribuiti, dove per esempio un database server hamolti utenti diversificati tra loro. Il bilanciamento dinamico ` cruciale an- eche in ambienti eterogenei, dove si va dal mainframe al supercomputer finoal piccolo desktop.Scoperta di regole associativeFino ad ora la parallelizzazione ha riguardato la scoperta degli insiemi fre-quenti e non della generazione di regole. La ragione sta nel fatto che laprima fase ` molto onerosa e da luogo a pochi insiemi che vengono elabo- erati in breve tempo dalla seconda ma questa affermazione non ` vera per i edatabase di grandi dimensioni, dove si possono estrarre milioni di insiemifrequenti. La complessit` della fase di generazione di regole ` dell’ordine di a eO(r · 2l ), dove r ` il numero di insiemi di frequenti ed l ` la lunghezza della e esequenza pi` lunga. uSistema di gestione dei database paralleli e dei file systemsTutti gli algoritmi proposti partizionano il database, seppur in modi diversi,ma nessuno ha mai studiato l’opportunit` di usare file systems paralleli per agestire le varie partizioni del database.Generalizzazione di regole associativeIl problema proposto fino ad ora ` quello di fare un’associazione binaria: eun oggetto ` presente oppure assente da una transazione ma non pensiamo ealla quantit` di questi oggetti presenti e al loro peso. In generale abbiamo aentit` che prendono il loro valore da un dominio continuo (attributi nume- arici) ed entit` che hanno valori finiti non numerici. Per applicare le regole aassociative ad alcune di queste categorie sono necessarie delle discretizza-zioni per racchiudere i valori continui in un range. Un’altra estensione diquesti algoritmi ` chiamata scoperta di regole associative generalizzate dove esi scoprono regole non solo nelle foglie ma anche in tutti i livelli dell’albero.Ovviamente la computazione di entrambe le specie ` molto onerosa ma la estrada della parallelizzazione consentir` prestazioni ragionevoli. a 73
  • 88. 4.2 ClusteringLa disciplina del clustering deriva dal bisogno di tecniche automatizzateper l’esplorazione dei dati che consentano l’analisi a gruppi o ad albero didocumenti non strutturati. Il clustering aiuta a scoprire concetti nascostisu grosse moli di dati, raggrupparli in insiemi etichettati di oggetti simili,aiuta nella ricerca su collezioni di testi e costruzione di ontonogie; per unadiscussione dettagliata della teoria di veda il paragrafo 2.2. In questa tesi ciconcentriamo su uno dei principali algoritmi di clustering, K-Means, che inpassato ` gi` stato oggetto di parallelizzazioni sia distribuite che a memoria e acondivisa.4.2.1 Versioni parallele a memoria distribuitaL’algoritmo K-Means si presta in modo naturale alla parallelizzazione gra-zie alla natura spaziale dei suoi dati; gli algoritmi che lo parallelizzano sibasano sul modello SPMD (Single Program Multiple Data) e comunicazionimessage-passing con MPI. Tutti gli algoritmi proposti fanno calcolare adogni processore, in modo indipendente, il cluster pi` vicino per ogni oggetto ulocale. Dopo aver associato ogni oggetto, il valore medio delle distanze localideve essere globalizzato. Alcuni algoritmi paralleli, legati dalla stessa filosofia di figura 4.13, sipossono trovare in [DM00], [SB00], [JMJ98], [Jos03], Le prestazioni variano a seconda del numero e della dimensione dei do-cumenti e del numero di cluster (n, d, k). Come dimostrato nel paragrafo5.2 la mole di elaborazioni pi` pesanti si hanno nel calcolo delle distanze, uripetuti dk volte. L’idea principale si basa sulla suddivisione del dataset trai processori per un calcolo pi` veloce delle distanze. Tuttavia nel caso di usistemi distribuiti si devono considerare anche i tempi di comunicazione cheper database piccoli possono inficiare i miglioramenti ottenuti con la parel-lizzazione. La lista di centroidi consiste di k vettori di lunghezza d mentreil dataset consiste di n vettori di lunghezza d. Per cominciare ogni processo ottiene l’intera lista di centroidi elaboratadal processo principale per poi calcolare le distanze di ogni punto del propriodataset locale con ogni centroide. Alla fine ogni punto viene associato al clu-ster pi` vicino ed ogni processo raccoglie la somma di tutti i punti assegnati u 74
  • 89. Figura 4.13: Algoritmo K-Means sequenziale [DM00]ad un dato cluster e ne calcola la media. Queste operazioni sono calcolateper ogni cluster per ottenere un nuovo insieme di centroidi ed ogni puntopu` essere riassegnato ai nuovi cluster cos` prodotti. Ogni processo pu` cal- o ı ocolare anche lo scarto quadratico medio (MSE) per la propria porzione didati ed inviarlo agli altri per ottenere il valore complessivo. Riassumendoper punti: 1. processo principale calcola i centroidi iniziali e li invia agli altri 2. ogni processo calcola la distanza di ogni punto da ogni centroide 3. assegna i punti al centroide pi` vicino e calcola MSE u 4. processo principale riceve le distanze per calcolare i nuovi centroidi e MSE globale 75
  • 90. Per il calcolo dei primi centroidi esistono pi` euristiche: la pi` banale ` il u u evalore casuale per tutti i centroidi (come su Weka), altre calcolano casual-mente il primo poi cercano di scegliere gli altri il pi` distante possibile, come uin [Jos03], riportata in figura 4.14. Figura 4.14: Scelta dei primi centroidi su algoritmo K-Means4.2.2 Versioni parallele a memoria condivisaLe versioni a memoria distribuita di K-Means possono essere adattate facil-mente per sistemi a memoria condivisa; Gagan Agrawal in [JA02] si foca-lizza su questo tipo di parallelizzazione degli algoritmi di Data Mining conle tecniche di parallelizzazione degli oggetti condivisi presentati all’inizio diquesto stesso capitolo mentre la stesura dell’algoritmo ricalca quella a me-moria distribuita di figura 4.13 ove al posto delle comunicazioni MPI ci sonole tecniche di riduzione scelte.4.2.3 Risultati SperimentaliVersioni parallele a memoria distribuitaIn letteratura ` possibile trovare sperimentazioni della versione K-Means eparallela. Ad esempio in [DM00] ` stato sperimentato su un IBM SP2 con e16 nodi IBM POWER a 160 MHz e 256 MByte di memoria, variando ilnumero di transazioni n da 213 a 221 , il numero di attributi d da 2 a 8 e ilnumero di cluster k da 2 a 16. 76
  • 91. Figura 4.15: Prestazioni K-Means parallelo con n = 221 , d = 8 e k = 8[DM00] Lo speedup ricavato ` ottimo con n = 221 , d = 8 e k = 8 su 16 processori, epari a 15.42 ma diminuendo la dimensione del dataset lo speedup scende finoa 6 su 16 processori, come si evince dai risultati in figuraVersioni parallele a memoria condivisaFigura 4.16: Prestazioni K-Means parallelo con n = 221 , d = 8 e k = 8[JA02] Anche in questo caso, ovviamente, lo speedup si avvicina ai valori ideali,vedi figura 4.16, con ognuna delle tecniche presentate. In particolare ` im- eportante dichiarare il sistema utilizzato per gli esperimenti, dichiarata comelo stato dell’arte in questo campo: SunFire 6800 con 24 processori Ultra-Sparc III a 900 MHz e 64 bit, 64 KByte di cache L1, 8 MByte di cacheL2 e 24 GByte di memoria totale. Il dataset ` di 200 MByte con punti etridimensionali (d = 3) e valore di k = 1000. Nella figura di destra, invece, 77
  • 92. le prestazioni sono state misurate su una macchina Sun Microsystem UltraEnterprise 450 con 4 processori a 250 MHz Ultra II e 1 GByte di memoria. Questi risultati sono possibili grazie soprattutto all’enorme quantitativodi memoria a disposizione che consente di non dover mai leggere da disco,operazione che rallenta di molto il tempo di esecuzione degli algoritmi pa-ralleli a memoria distribuita, se gli oggetti replicati sono molti. Ecco, infattiche i risultati migliori si registrano per la tecnica a completa replicazione. 78
  • 93. Parte IIWekaSMP 79
  • 94. Capitolo 5Analisi degli algoritmi diWekaSMP “Non fidatevi troppo dei risultati di un esperimento, a meno che non siano confermati dalla teoria” Arthur Eddington (1882-1944), astrofisico e matematico inglese. 81
  • 95. Per tutti i successivi algoritmi la correttezza non ` sufficiente: essi do- evranno fornire dei tempi di risposta adeguatamente inferiori rispetto allalibreria sequenziale di base, all’aumentare dei processori. La correttezzaintesa come “dare la risposta giusta” non sar` oggetto di studio teorico per- ach` Weka oramai ` un progetto ben avviato ed affidabile, si suppone che e el’output sia corretto: baster` allora confrontare i risultati ottenuti dagli al- agoritmi paralleli con i relativi ottenuti con Weka In questo capitolo si cercadi studiare i comportamenti teorici degli algoritmi paralleli che si andrannoa sviluppare, discussi nel capitolo 6. I risultati teorici ottenuti si potrannoconfrontare con i risultati sperimentali del capitolo 7.5.1 Regole Associative: AprioriSMP Tabella 5.1: Simboli utilizzati nell’analisi teorica di Apriori Simbolo Definizione N numero transazioni P numero processi M numero di candidati k numero di passi in Apriori I numero medio di oggetti in una transazione S numero medio di candidati in una foglia C numero medio di potenziali candidati in una transazione L numero medio di foglie nell’hash tree per Apriori seriale ttravers costo dell’attraversamento dell’hash tree per potenziali candidati tcheck costo per confrontare le foglie con S candidati Vi,j numero previsto di foglie visitate con i potenziali candidati e j foglie La dimensione del problema in Apriori seriale ` proporzionale al nume- ero di transazioni e al numero di candidati. Considerando una transazione Icon I oggetti, al k-esimo passo dell’algoritmo la transazione ha C = kpotenziali candidati che devono essere cosiderati. Sia VC,L numero previstodi foglie visitate, O(M ) il tempo di costruzione dell’hash tree e il tempo dicomputazione per visitare l’hash tree per ogni transazione sia: Ttrans = C × ttravers + VC,L × tcheck 82
  • 96. allora il tempo di computazione di Apriori seriale per computare N transa-zioni sar` dato dal tempo di subset + la costruzione dell’hash tree. a serial Tcomp = N × Ttrans + O(M ) (5.1) = N × C × ttravers + N × VC,L × tcheck + O(M ) Nell’algoritmo Count Distribution implementato [HKK97] l’intero set dicandidati viene replicato per ogni processo quindi il numero medio di nodifoglia nell’hash tree locale ` L = M/S, come nell’algoritmo seriale. Ogni eprocessore, per`, elabora solo N/P transazioni, allora il tempo di esecuzione odi Count Distribution sar` dato dal tempo impiegato dalla funzione subset api` la costruzione hash tree pi` riduzione globale. u u parallel N Tcomp = × Ttrans + O(M ) P (5.2) N N = × C × ttravers + × VC,L × tcheck + O(M ) P P Confrontando le 5.1 e 5.2 si nota come Count Distribution non aggiungacomputazioni ridondanti e scali di un fattore P sia nell’attraversamento del-l’albero che nel tempo di confronto delle foglie mentre il costo di costruzione e CDdell’hash tree ` uguale all’algoritmo seriale. Quindi P × Tcomp avr` comples- asit` O(P M ), proporzionalmente ad O(M ), assunto che M sia contenuto in amemoria, altrimenti si debbono aggiungere i tempi di latenza del disco.5.2 Clustering: K-MeansSMP Tabella 5.2: Simboli utilizzati nell’analisi teorica di K-Means Simbolo Definizione n numero transazioni k numero di clusters d dimensione del dominio (numero attributi) P numero processi Xi porzione di dati associato al processo i numero di iterazioni di K-Means T f lop tempo di esecuzione di un’istruzione floating point Come visto nel paragrafo 2.2.1 l’algorimo K-Means ha una complessit` acomputazionale O(3nkd) · . Utilizzando gli stessi simboli di tabella 5.2, 83
  • 97. considerando che ognuna di queste operazioni sia in floating point di durataT f lop , l’algoritmo K-Means avr` un tempo di esecuzione a T1 ∼ (3nkd) · · T f lop (5.3) Con l’implementazione della versione parallela a memoria condivisa, K-MeansSMP, utilizzando P processori l’ideale sarebbe ridurre il tempo dicomputazione di un fattore P . Si osservi innanzitutto che il calcolo delledistanze (righe 14-21 in figura 4.13) ` di tipo data-parallel quindi ` bana- e ele parallelizzarlo tramite suddivisione dei dati e successiva riduzione con leoperazioni adeguate, consentendone un’esecuzione asincrona. Essendo il da-tabase composto da n punti, ad ogni processore ne spetteranno n/P . Ora,per semplicit`, assumiamo che P divide n. Per ogni µ = 0, 1, · · · , P − 1, aassumiamo che ogni processore identificato da µ abbia accesso alla porzionedi dati {Xi , i = (µ)∗(n/P )+1, · · · , (µ+1)∗(n/P )}. Ogni processore, a que-sto punto, ha accesso all’oggetto condiviso contenente i centroidi {mj }k j=1di partenza e pu` effettuare il calcolo delle distanze in modo asincrono ri- ospetto agli altri processori. Alla fine tutte le distanze locali dovranno essereridotte con opportune operazioni in una unica globale per ogni centroide.Per questo motivo ci aspettiamo di ottenere un tempo ridotto di un fattore compP . Il tempo di computazione TP sar`: a comp T1 (3ndk) · · T f lop TP = ∼ (5.4) P P Tuttavia, oltre a questo tempo di puro calcolo, bisogna tener conto anchedelle comunicazioni tra processi, gestione dei thread, attese sui lock e tempidi riduzione. Prima di ogni iterazione di K-Means il processo principaledovr` ricevere da tutti i processi le distanze calcolate per poter generare i anuovi centroidi {mj }k . Ovviamente in questo punto ci dovr` essere una j=1 asincronizzazione forzata. I dati da ridurre sono d · k · P (numero di attributiper numero di cluster per ogni thread), quindi possiamo stimare il tempodi comunicazione parallela per memoria condivisa in modo simile a quantovisto per la versione a memoria distribuita in [DM00]: comm reduce TP ∼d·k· · TP (5.5) reduce in questo caso altro non sar` che un ciclo for annidato per ladove TP asomma delle medie delle distanze. 84
  • 98. Riassumendo, ogni iterazione del nostro algoritmo parallelo ha una fasedi calcolo asincrona ed una fase di comunicazione sincrona; proprio per que-s’ultima fase la 5.4 non pu` essere rispettata. Combinando le relazioni 5.4 e o5.5 possiamo stimare la complessit` computazionale dell’algoritmo parallelo acome: comp comm (3ndk) · · T f lop reduce TP = TP + TP ∼ +d·k· · TP (5.6) PAppare evidente come la soluzione parallela convenga solo su grandi molidi dati, sia in questa versione a memoria condivisa che in quella ancora pi` uinfluenzabile dai tempi di comunicazione, a memoria distribuita ma anche inquesto caso, se la partizione di database associata ad ogni processo non pu` oessere contenuta in memoria, sono possibili rallentamenti dovuti alle latenzedel disco. 85
  • 99. 86
  • 100. Capitolo 6Realizzazione del prototipoWekaSMP “Tutti sanno che una cosa ` impossibile da realizzare, e finch´ arriva uno sprovveduto che non lo sa e la inventa” e Albert Einstein (1879-1955), fisico tedesco. 87
  • 101. Tipo Descrizione Instances Contiene il database di transazioni Instance Una riga del database (Instances) ItemSet Insieme di articoli di lunghezza k AprioriItemSet Estensione di ItemSet per l’algoritmo Apriori FastVector Estensione di java.Vector Tabella 6.1: Tipi pi` importanti nella classe Apriori u Per rendere computazionalmente pi` efficienti gli algoritmi attuali con ule nuove architetture multiprocessore si deve ricorrere all’utilizzo di threadper suddividere il carico di lavoro tra le unit` di calcolo e lock per gestire al’accesso concorrente agli oggetti condivisi. Come gi` descritto nel capi- atolo 3, Java mette a disposizione pi` metodi per gestire Thread e Lock. uPer questo progetto si ` scelto di gestire i Thread estendendo l’interfaccia ejava.lang.Thread mente per i lock si sono utilizzati sia metodi e oggettisincronizzati con il classico synchronize, che con le pi` recenti funzionalit` u adella libreria java.util.concurrent, introdotta dalla versione 5 di Java,per l’utilizzo di operazioni atomiche o con accesso esclusivo. Il modello che si ` seguito per sviluppo e implementazione ` SPMD, Sin- e egle Program Multiple Data, in cui ogni processore esegue lo stesso program-ma su dati differenti; i risultati ottenuti con flussi paralleli asinconi verran-no unificati con opportune istruzioni quali somme, medie o altre operazionimatematiche o insiemistiche. Il numero di processi creati ` flessibile in base al sistema su cui Weka eviene avviata. Noi assumiamo di generare un processo per ogni processore(o core) disponibile, rilevato con il metodo java.lang.Runtime.getRuntime().availableProcessors()ma modificando il codice ` possibile generare pi` o meno processi, anche in e umodo statico e predefinito. In tabella 6.1 sono evidenziati i tipi pi` importanti che incontreremo nel ucodice. 88
  • 102. 6.1 CoreLe classi principali di Weka sono raggruppate nel package core che forniscealgoritmi e strutture per l’elaborazione dei dati, necessarie per poterli utiliz-zare negli algoritmi di Data Mining implementati. Weka utilizza databasein formato ARFF, come visto in figura 2.10 ma come questo viene codifica-to negli algoritmi? Per prima cosa si legge il file e lo si carica in memoriamantenendone il formato con il costruttore Instances (vedi allegato B.1),in seguito si verifica se i dati caricati possono essere elaborati dallo speci-fico algoritmo con il metodo testWithFail dello stesso package. La classeInstances fornisce molti altri metodi per la manipolazione delle istanze,viste come insieme di oggetti, accedibili come un vettore. Istanze posso-no essere copiate, private di elementi o ampliate. Metodi accessori comemeanOrMode, usato in K-Means, forniscono statistiche sull’insieme di istanze.Alcune classi sono state modificate o riscritte per l’utilizzo multi-thread.6.2 Regole Associative: AprioriSMPCome visto, in figura 4.4 la tassonomia degli algoritmi paralleli per la sco-perta di regole associative ` suddivisa in due macro aree: bilanciamento edel carico statico e dinamico. Attualmente tutti gli algoritmi sono di tipostatico e da qui si suddividono ancora per tipologia di memoria: distribui-ta, condivisa e gerarchica. Ognuna di queste aree ` suddivisa a loro volta eper metododologia di parallelizzazione: per dati o per processi. Count Di-stribution, per esempio, ha un bilanciamento del carico statico, a memoriadistribuita e a parallelizzazione di dati. L’algoritmo che si propone in questolavoro, invece, ottimizza Count Distribution nell’area a memoria condivisa,ponendosi al fianco di Common Candidate Partitioned Database. Si ` scelto di parallelizzare l’algoritmo Apriori a partire dall’algoritmo eCount Distribution sia perch` questo garantisce buone prestazioni con un enumero di unit` di calcolo inferiore a 30, sia perch` ` l’algoritmo che pi` a e e usi avvicina all’originale ed ` stato il punto di partenza di Common Candi- edate Partitioned Database, presentato nel paragrafo 4.1.2. Scopo di questatesi infatti ` parallelizzare gli algoritmi esistenti bilanciando la semplicit` di e asviluppo a partire dagli algoritmi seriali con la necessit` di ottenere buoni a 89
  • 103. speedup, un buon bilanciamento tra le unit` di calcolo ed una buona effi- acienza generale, senza stravolgere il codice originale. Prima di cominciarela parallelizzazione si sono effettuati dei test per verificare il tempo di cpunecessario per ogni metodo nell’algoritmo. Come si intuir` dai risultati dei atest, il conteggio dei supporti in una base di dati da 500 MByte ` responsa- ebile del 95% del tempo impiegato dall’intero algoritmo buildAssociations,ecco perch` si sceglie di privilegiare l’esecuzione parallela di questa fase e enon del resto per non introdurre ritardi dovuti alla costruzione dei threadche andrebbero a peggiorare le prestazioni. Figura 6.1: Confronto Count Distribution SM e DM In figura 6.1 si vede com’` stata progettata l’architettura dell’algoritmo e ´proposto, in confronto con Count Distribution. E fondamentale ricordare chel’algoritmo originale ` strutturato per funzionare su sistemi con memoria di- estribuita e librerie MPI, mentre l’algoritmo proposto in questo paragrafo ` ea memoria condivisa. In Count Distribution tutti i processori costruisconolo stesso hash tree per poi effettuare il conteggio degli insiemi nel databaseed effettuare una somma generale, a carico di un solo processore. Nel nostrocaso, invece, solo il thread 0 si occupa di costruire l’insieme dei frequenti e 90
  • 104. Figura 6.2: Diagramma di flusso algoritmo AprioriSMPl’hash tree per poi renderlo accessibile in copia a tutti gli altri processi, inmodo da non avere computazioni ridondanti e lasciare liberi pi` processori upossibili per eventuali altri processi (Virtual Machine o sistema operativo,ad esempio). Questo aspetto, visibile in figura 6.2 consente anche di man-tenere un certo bilanciamento del carico di lavoro poich` tutti i processi edevono verificare la frequenza di tutti gli insiemi candidati su una porzione 91
  • 105. di database di dimensioni uguale tra i processi. AprioriSMP ` stato implementato in due varianti, secondo due diverse estrategie: oggetti condivisi e replicati. Nonostante in entrambi i casi ildatabase delle transazioni sia condiviso, perch` in sola lettura, nel primo ecaso ogni thread fa riferimento ad un solo insieme dei frequenti costruitodal processo principale per incrementare il contatore dei supporti. In questomodo si assicura un utilizzo di memoria pari a quello utilizzato dalla versioneseriale ed in pi` un incremento delle prestazioni dovuto al concorrere dei uprocessori nel conteggio dei supporti. Per contro l’utilizzo di risorse condivisein scrittura impone dei metodi per sincronizzare gli accessi concorrenti inlettura/scrittura, in modo da garantire sempre l’atomicit` delle operazioni ae la consistenza dei dati. Il caso in cui le risorse non siano condivise perogni processo, implica che nello stesso spazio di memoria ci siano n replichedegli stessi oggetti, uno per ogni processo, ove ognuno ha accesso esclusivoin lettura e scrittura in modo da eliminare i tempi di latenza indotti daiconflitti. Questa soluzione ` utilizzata giocoforza sui sistemi a memoria edistribuita, che possono contare su uno spazio di memoria proporzionale alnumero di unit` di calcolo ma garantisce prestazioni ottime anche in questo acaso di studio, obbligando per` l’estensione della memoria heap durante ol’esecuzione, come si vedr` nel paragrafo 7.2. a La figura 6.3 rappresenta schematicamente l’architettura generale: laclasse Apriori estende la classe astratta Associator (definisce lo schema ge-nerale per tutti gli algoritmi per generare regole associative) ed implementale interfacce OptionHandler, CARuleMiner e TechnicalInformationHandler.Le classi riporate alla base dell’immagine sono 3 tra le pi` importanti uti- ulizzate dall’algoritmo. FastVector implementa una classe che utilizza deivettori veloci per la memorizzazione dei dati. Questa classe rimpiazzajava.Vector e non ` sincronizzata. Instances gestisce gli insiemi ordi- enati di istanze (il nostro database) della classe Instance che conterr` una ariga della tabella. AprioriItemSet estende ItemSet ed ` una classe d’ap- epoggio che immagazzina insiemi di oggetti in ordine lessicografico e forniscealla classe Apriori metodi per costruire gli insiemi frequenti e le regole asso-ciative. La classe CountDistribution ` in appoggio ad Apriori e si occupa edi parallelizzare il conteggio dei supporti con l’utilizzo di thread. 92
  • 106. Figura 6.3: Diagramma delle classi, algoritmo AprioribuildAssociationLa classe buildAssociation genera tutti gli insiemi frequenti, utilizzandol’algoritmo scelto mediante interfaccia grafica o linea di comando, che rispet-tino il supporto minimo e da questi tutte le regole associative che rispettanola confidenza minima. Con le impostazioni predefinite Weka tenta di gene-rare almeno 10 regole con supporto minimo del 100%, decrescendo del 5%finch` non ci riesce o fino a raggiungere il 10%. La confidenza minima ` e esettata al 90%, perci` sono possibili pi` chiamate al metodo Apriori da o uparte della classe BuildAssociation.function b u i l d A s s o c i a t i o n s { do { f i n d L a r g e I t e m S e t s (); f i n d R u l e s Q u i ck l y (); } while ( numRules > minRules ) dec ( minSupport );} 93
  • 107. BuildAssociation chiama quindi findLargeItemSets e findCarRulesQuicklyfinch` le regole trovate non rispettano i parametri (numero, supporto e con- efidenza). Il nostro interesse, per il momento, si limita alla prima delle duefunzioni che porta con se il cuore dell’algoritmo e le maggiori modifiche perla parallelizzazione e che contiene le maggiori modifiche.findLargeItemSetsfunction f i n d L a r g e I t e m S e t s { kSets = 1 - itemsets ; // avvio i Thread per distribuire i conteggi for each Processors j cdVector = new C o u n t D i s t r i b u t i o n ( instances , kSetsVector ). start // unisco i kSets creati dai processi kSets = m e r g e _ k S e t s V e c t o r ( kSetsVector ); // elimino gli item non frequenti kSets = delet eItemSe ts ( kSets , support ); if ( kSets . size () == 0) return ; do { // moltiplico kSets per se stesso per creare Lk da Ck -1 kMinusOneSets = kSets ; kSets = m e r g e A l l It e m S e t s ( kMinusOneSets ); // inserisci l ’ itemsets nell ’ hashtree hashtable = getHashtable ( kMinusOneSets ); // elimino i kSets generati da kSets non frequenti kSets = pruneItemSets ( kSets , kMinusOneSets ); // avvio i Thread per distribuire i conteggi for each Processors j cdVector = new C o u n t D i s t r i b u t i o n ( instances , kSetsVector ). start // unisco i kSets creati dai processi kSets = m e r g e _ k S e t s V e c t o r ( kSetsVector ); // elimino gli item non frequenti kSets = delet eItemSe ts ( kSets , support ); } while ( kSets . size () > 0);} Come si intuisce dallo pseudocodice findLargeItemSets ` il vero e pro- eprio algoritmo Apriori sul quale sono state aggiunte le funzionalit` per ren- aderlo Multi-Thread. Innanzi tutto il vettore cdVector ha dimensione parial numero di unit` di calcolo del processore e contiene il riferimento ai th- a 94
  • 108. read creati di tipo CountDistribution, la classe che effettua il conteggio deisupporti degli insiemi candidati. L’algoritmo si pu` ridurre in pochi passi: ok=1 1. converti il database di transazioni nell’ 1-itemset candidato C1 2. avvia parallelamente i conteggi ed attendi che tutti terminino 3. riduci tutti gli insiemi candidati elaborati in uno solo 4. elimina gli insiemi di articoli che non soddisfano il supporto 5. termina se l’insieme rimasto ` vuoto, altrimenti ek>1 1. aggiungi l’insieme trovato in una collezione (che servir` per estrarre a tutte le regole) 2. crea l’insieme dei candidati Ck da Lk−1 3. crea l’hash tree ed effettua il prune 4. conta i supporti in parallelo ed attendi che tutti i processi terminino 5. riduci tutti gli insiemi candidati elaborati in uno solo 6. elimina gli insiemi di articoli che non soddisfano il supporto 7. termina se l’insieme rimasto ` vuoto, altrimenti ripeti dal punto 1. eCountDistributionLa classe CountDistribution ` stata creata per effettuare il conteggio dei esupporti in modo parallelo. Estende Thread ed utilizza gli stessi metodidella versione originale, modificati in modo da poter sfuttare a pieno lepotenzialit` del multi-thread, illustrati in seguito. afunction c o u n t D i s t r i b u t i o n { // inizializza indici di inizio e fine lettura database set first / last index ; // duplica l ’ insieme dei candidati duplicate ( kSets ); 95
  • 109. // aggiorna i contatori sull ’ insieme dei candidati privato // attraversando solo la propria porzione di database upDat eCounte rs ( kSets , instances , first , last );} Nel metodo f indLargeItemSets si crea un vettore di kSets ed un vettoredi thread. Si creano un numero di nuove classi CountDistribution quanti sonoi processori, rilevati con il metodo java.lang.Runtime.getRuntime().availableProcessors()e ad ognuno si assegna una porzione di database. Il metodo upDateCountersdella classe ItemSet ` stato modificato per gestire l’accesso ad una sola por- ezione di database (suddiviso quindi in modo logico) ed l’accesso concorrentealle variabili contatore. Molte altre variabili all’interno delle classi d’appog-gio sono state rese statiche sia per velocizzarne l’utilizzo che per renderleuniche nell’utilizzo con i thread.function upDat eCounte rs ( kSets , instances , first , last ){ for each instances i in [ first , last ) // per la porzione di assegnata // se questo itemset e ’ contenuto nell ’ istanza del db if ( containedBy ( instances ( i ))) synchronized ( lock ){ m_counter ++; // incrementa il contatore } }}Il metodo synchronized classico ` stato preferito alla classe java.util.concurrent epoich` nei test ` risultato pi` veloce (le innovazioni introdotte non erano e e unecessarie, in questo caso).6.3 Clustering: K-MeansSMPL’idea principale ` quella di suddividere i dati spaziali in partizioni da as- esegnare ad ogni processo; ognuno di essi calcoler` in modo asincrono le adistanze e la posizione locale di ogni centroide per poi fondere tutti i risul-tati nel processo principale che si occuper` di calcolare i risultati globali da acondividere per le successive iterazioni. La lista di centroidi ` un vettore econdiviso tra i processi di lunghezza k contenente le istanze di ogni puntoa cui si fa riferimento. La condivisione avviene in sola lettura e solo il pro- 96
  • 110. cesso principale inserir` di volta in volta i nuovi centroidi, alla fine di ogni aiterazione. Il vantaggio di questa soluzione ` duplice: da un lato si ottiene la stes- esa occupazione di memoria della versione sequenziale, dall’altro si riesce araggiungere una velocit` di esecuzione molto maggiore, potendo sfruttare a apieno tutte le unit` di calcolo. Con questa filosofia si renderanno ridondanti asolo gli oggetti condivisi in scrittura che possono dar luogo a conflitti o a de-generazioni di prestazioni causati da stalli (lock) che verranno poi ridotti inun unico oggetto con opportune operazioni da parte del processo principale,come in figura 6.4 Le strutture dati globali principali dovranno contenere: • numero di cluster • coordinate centroidi • deviazione standard dai centroidi In figura 6.5 ` rappresentata l’architettura del package clusterers che econtiene l’algoritmo kmeansSMP, richiamato dall’algoritmo originale di Weka,SimpleKMeans. Quest’ultimo contiene le strutture dati principali ed estendela classe astratta RandomizableClusterer che si occupa di fornire dei meto-di di utilit` ed implementa due interfacce, NumberOfClustersRequestable, aWeightedInstancesHandler. Analizzando in dettaglio l’algoritmo, i primi centroidi sono generati ca-sualmente dal processo principale e condivisi con tutti gli altri processi chefaranno riferimento a questi per calcolare le distanze dei punti della pro-pria porzione di database, di dimensione n/P . Le distanze calcolate da ogniprocesso in modo asincrono e parallelo vengono salvate su un array a tredimensioni (cubo) di dimensione d × k × P (numero attributi × numero clu-sters × numero processi) condiviso tra i processi ed ognuno scrive solo sulledue dimensioni d × k nello spazio ad esso assegnato seguendo l’indice Pi delnumero di processo. Alla fine di ogni ciclo il processo principale attende laconclusione di tutti i thread e riduce il cubo calcolandone media e coorinatedei centroidi globali e lo scarto quadratico medio.function build Cluster er testWithFail ( data ); instances = new ( Instances ( data ); 97
  • 111. Figura 6.4: Diagramma di flusso kmeansSMPclusters = new Instances ( numClusters );for each clusters i c lu st er C en tr o id [ i ] = random ;// cicla fino alla convergenza dei centroidiwhile (! converged ){ // inizia la parte parallela 98
  • 112. Figura 6.5: Diagramma delle classi, algoritmo kMeansSMP for each Processors j kVector [ j ] = new kMeansSMP ( core , instances , tempI ). start // ricalcola centroidi for each Processors j tempI = mergeLocal ( tempI ); for each clusters i for each attributes t vals [ t ] = tempI [ i ]. mean ( t ); c lu st er C en tr oi d . add ( vals );}// calcola scarto quadratico medio per ogni attributo di ogni istanza// per ogni clusters . for each clusters i for each attributes t vals2 [ j ] = tempI . sqrt ( variance ( t )); 99
  • 113. Riassumento per punti, il metodo buildClusterer si comporta comesegue: • il processo principale calcola i centroidi iniziali e li condivide • attende che ogni processo calcoli le distanze locali e la media delle distanze dai centroidi • riduce i risultati ottenuti • calcola i nuovi centroidi e l’errore quadratico medio (MSE).K-MeansSMPLa classe K-MeansSMP si occupa dei calcoli massivi all’interno dell’algoritmo;estende la classe java.lang.Thread e rende asincroni i calcoli delle distanzeprima sequenziali; per questo motivo necessita di tutte le attenzioni per potersvolgere in modo corretto tutte le operazioni senza influire negativamentesulle prestazioni. A tale scopo i lock sono stati ridotti al minimo, grazieanche alla natura spaziale dei dati dell’algoritmo, mentre sono state replicatesolo le variabili altrimenti condivise in scrittura.function kMeansSMP extends Thread { first , last = calcola porzione database da elaborare ; // clusterizza ogni istanza della partizione nel modo migliore for each instances i in [ first , last ] // per la porzione di assegnata for each clusters j { dist = distance ( instance , c l u s t e r C e n t r o i d s . instance ( j )); if ( dist < minDist ) { minDist = dist ; bestCluster = i ; } } if ( o l d C l u s t e r s C e n t r o i d != c l us te rC e nt ro id ) SimpleKMeans . converged = false ; // associa ad ogni centroide le istanze for each instances i in [ first , last ] // per la porzione di assegnata tempI [ c l u s t e r A s s i g n m e n t s [ i ]]. add ( instance ( i )); // crea i nuovi centroidi e calcola le medie sul cubo condiviso for each clusters i for each attributes t vals [ j ][ numThread ][ i ] = tempI [ i ][ numThread ]. meanOrMode ( j ); 100
  • 114. return ;} Il costruttore si occupa di settare opportunamente i riferimenti agli og-getti condivisi passati come parametro dal processo principale e determinaregli indici per accesso al database. Il metodo run() di ogni thread, avviato implicitamente dalla chiamataThread.start() da parte del processo principale, si occupa di assegnareogni punto del database ai centroidi attuali (e condivisi da tutti i processi) ese ci sono variazioni di stato. In caso positivo si deve ricorrere al calcolo delledistanze di tutti i punti rispetto al centroide ad essi associato. I risultati sisalvano su un’area privata di un array condiviso con gli altri processi. Infinesi restituisce il controllo al processo principale che sommer` i risultati di atutti i thread per ottenere le coordinate dei nuovi centroidi. 101
  • 115. 102
  • 116. Capitolo 7Risultati Sperimentali “Le statistiche si citano per aver ragione, pur non essendone convinti” Anonimo. 103
  • 117. 7.1 PreliminariLo sviluppo dell’applicazione ` avvenuta su un pc con processore Intel R ePentium R 1,6 GHz e 2 GByte di memoria RAM. Il framework utilizzato TM` Netbeans R 5.5 su piattaforma Javae Sun JDK 1.5.0 14. I test sonostati eseguiti su una macchina Linux di propriet` dell’Istituto di Scienza e aTecnologie dell’informazione (ISTI) del Consiglio Nazionale delle Ricerche(CNR) di Pisa, gentilmente concessa da Claudio Lucchese, ricercatore edottorando di questa facolt`. L’unit` monta due processori Intel R Xeon R a aE5335 quad-core da 2 GHz con 4 MByte di memoria cache e 4 GByte dimemoria RAM condivisa. La libreria utilizzata per l’esecuzione del progetto TM` Javae Sun JDK 1.6.0 04. Per eseguire i test si usa uno script bash cheavvia per 12 volte lo stesso algoritmo con gli stessi parametri, scaricando lecache ed attendendo 4 secondi tra ognuno. Tutti i risultati vengono elaboratiper calcolarne media, varianza ed indici di interesse. Le variabili in giocosono il numero di processi e la dimensione del database. Intel offre per i suoi clienti un tool molto interessante e potente per moni-torare un’emorme quantit` di parametri che riguardano il lavoro del proces- a TMsore: Intel R VTune Performance Analyzer 9.0 for Linux. In questo casoci siamo interessati a valutare il numero di accessi alla cache di secondo livel-lo (condivisa sul processore Xeon dei nostri test) in rapporto con il numerodi fallimenti, conosciuto come Cache Miss Rate. L’installazione deve avve-nire da parte dell’amministratore sulla directory /usr/local/vtune/bin/,mentre il suo utilizzo ` possibile sia attraverso una complessa interfaccia egrafica che da linea di comando, molto pi` veloce e flessibile. Per avviare la uprocedura di acquisizione dati da linea di comando si deve specificare un’at-tivit` (sampling, nel nostro caso), un tempo di acquisizione, il parametro da avalutare (il numero di richiesta sulla cache L2 da parte di tutti i core) ed ilprocesso da valutare (AprioriSMP, nell’esempio) con il seguente comando:$ vtl activity -d 10000 -c sampling -o ’’- ec en = L2_LD . BOTH_CORES . DEMAND . I_STATE ’ ’ - app ./ AprioriSMP . sh run$ vtl view - guiIl comando view -gui attiva la visualizzazione dei risultati in una sempliceinterfaccia grafica, come in figura 7.1, dove sono presenti tutti i processi 104
  • 118. attivi in quel momento ed i parametri rilevati per la cache L2, in modo det-tagliato e per ogni processo: numero di cache miss, percentuale di richiestealla cache dovute a quel processo, numero di richieste alla cache. Figura 7.1: Esempio di rilevamento cache miss con VTune7.2 AprioriSMPAprioriSMP, come detto nel paragrafo 6.2, ` stato implementato in due evarianti (contatori condivisi e privati). I test prevedono le stesse modalit` aper entrambe le tipologie: 10 run per ogni parametro. In tabella 7.1 sonoesposte le variabili in gioco. Numero Core Dimensione Database 1 50 MByte 2 200 MByte 4 500 MByte 8 Tabella 7.1: Parametri test AprioriSMP Le due versioni sono state lanciate con script bash molto simili; il listato ` evisibile nelgli allegati. L’istruzione java -Xmx2048m weka.associations.Apriori 105
  • 119. Figura 7.2: Esempio output AprioriSMP nei test-t ../mushroom500.arff, ad esempio, avvia l’algoritmo AprioriSMP uti-lizzando come dataset mushroom500, una versione del dataset mushroom uti-lizzato in letteratura (vedi [fim]) portata a 500 MByte. L’opzione -Xmx2048mconsente all’interprete di assegnare 2 GByte di spazio per la memoria heap.In figura 7.2 si pu` vedere come vengono esposti i risultati di un’iterazione odell’algorito AprioriSMP con database Mushrooms500 eseguito su 8 proces-sori, mentre in figura 7.3 c’` un esempio di come vengono presentati a video ei tempi rilevati. Si noti come possono cambiare i tempi di conteggio dei sup-porti sugli 8 processori; il tempo di esecuzione sar` pari al tempo di questi api` alto pi` qualche millisecondo per la gestione dei thread (tempo di join). u u Come ulteriore prova finale si sono voluti mettere a confronto i tempi 106
  • 120. Figura 7.3: Esempio rilevamento tempi AprioriSMP nei testdell’algoritmo originale e la versione parallela utilizzando per` un solo th- oread. Se questo test pu` sembrare banale, in realt` si pensi all’occupazione o adi processore dovuto ad altri processi attivi del sistema operativo: suppostoche tali processi influenzino tutti i test allo stesso modo (considerato anchela modalit` di trattamento dei dati), allora ` vero che su macchine a singolo a eprocessore i tempi sono uguali. Su macchine parallele, invece, si dedica unprocessore al solo conteggio dei supporti (nel caso di AprioriSMP), lascian-do gli altri liberi di elaborare processi esterni. Ne risulta un incremento diprestazioni minimo, ovviamente, ma che ha raggiunto il 3% con il databaseda 500 MByte.7.2.1 DatasetI dataset utilizzati sono delle estensioni a 50, 200 e 500 MByte di mushroom,conosciuto in letteratura e reperibile presso il repository di machine learning 107
  • 121. databases di UCI e FIMI [uci] [fim]. Costituito dalla Audubon Society FieldGuide to North American Mushrooms nel 1981 da Alfred Knopf, contienele descrizioni ipotetiche di 23 speci di funghi delle speci Agaricus e Lepio-ta. Ogni specie ` definita come edible (commestibile), definitely poisonous e(certamente velenoso), unknown edibility (commestibilit` sconoscituta), not arecommended (non raccomandato). La struttura del database ` rappresenta- eta in figura 7.4; i due colori rappresentano le classi di appartenenza, velenosie commestibili. Nelle ordinate sono presenti le informazioni sugli attributi enelle ascisse di ogni istogramma c’` la frequenza. Gli attributi sono 22 e de- escrivono la struttura, il colore, il profumo, l’habitat ed altre caratteristicherappresentative. La descrizione di ogni attributo ` rappresentata in figura e7.5 Figura 7.4: Struttura del dataset Mushroom 108
  • 122. Figura 7.5: Descrizione7.2.2 Test contatori condivisiL’implementazione a contatori condivisi di AprioriSMP consiste nella paral-lelizzazione full lock suggerita da Gagan Agrawal in [JA02], fondata nel con-dividere tutti gli oggetti senza alcuna replicazione riducendo cos` lo spazio ıdi memoria occupato. In questa implementazione di AprioriSMP i conta-tori corrispondenti ad ogni itemset sono condivisi per ogni processo, sceltache porta alla non ridondanza dei dati, alla soppressione delle operazioni diriduzione dei dati parziali ma anche all’introduzione di conflitti nell’accessoai contatori stessi. Come scaturir` da questi test, nel caso pessimo 8 pro- acessi concorrono per l’incremento dei contatori condivisi; questa operazione 109
  • 123. consiste ` divisa in 5 fasi: e 1. acquisizione lock sulla risorsa 2. lettura valore contatore 3. incremento di 1 valore letto 4. scrittura valore sul contatore 5. rilascio lock sulla risorsaOvviamente se il lock ` gi` assegnato, il thread si deve mettere in attesa. e aPi` processi concorrono nell’acquisizione del lock, maggiori sono i tempi di uattesa. I dati raccolti dimostreranno la difficolt` di questa scelta nell’otte- anere risultati soddisfacenti: se da un lato lo speedup aumenta leggermen-te all’aumentare del numero di processi coinvolti, dall’altro lato l’efficienzadiminuisce di molto, segnale che se le prestazioni tendono ad aumentare,l’incremento di processi aumenta i conflitti nell’acquisizione del lock per icontatori (uno per ogni itemset) e non si ottengono gli incrementi cercati. Per ogni tipologia di test, si sono svolte 12 prove, si sono scartate la pi` ulenta e la pi` veloce e si ` calcolata la media dei tempi. In seguito si sono u ecalcolate deviazione standard e speedup. Le tipologie sono 12, come prodottodi 3 valori di dimensione del database (50, 200 e 500 MByte) per 4 insiemidi processi attivi per volta (1, 2, 4, 8). Nella prima tabella di ogni gruppo sono presenti i tempi medi di rispo-sta rilevati espressi in millisecondi: intera esecuzione dell’algoritmo Apriori-SMP nella prima colonna, conteggio dei supporti nella seconda e deviazionestandard rispetto ai tempi di esecuzione rilevati nella terza. Nella seconda tabella di ogni gruppo si presentano lo speedup ed efficien-za dell’intero algoritmo e della sola parte relativa al conteggio dei supporti,quella con l’effettiva parallelizzazione. Nel tempo di esecuzione del conteg-gio in parallelo si tiene conto anche del tempo di creazione dei thread, delpartizionamento dei dati e della riduzione finale dei contatori.Database di 50 MByteNelle tabelle 7.2 e 7.3 sono esposti i rilevamenti effettuati e gli indici calcolati.Si noti come lo speedup sia molto basso in tutti i casi, dovuto all’elevata 110
  • 124. concorrenza nell’accesso esclusivo alle variabili coinvolte nel conteggio deisupporti per ogni insieme dei frequenti. Tempo di risposta AprioriSMP Supporti Dev. St. originale 5171 4479 32 2 core 3542 4358 245 4 core 2421 3369 33 8 core 1703 2716 57Tabella 7.2: Tempi di risposta AprioriSMP, contatori condivisi, database 50MByte Indici Speedup Efficienza AprioriSMP Supporti AprioriSMP Supporti 2 core 1,20 1,29 0,60 0,65 4 core 1,56 1,88 0,39 0,47 8 core 1,93 2,68 0,24 0,34Tabella 7.3: Indici prestazionali AprioriSMP, contatori condivisi, database50 MByte In questo come negli altri test per questo algoritmo, le prove con 2 pro-cessi offrono i risulti pi` ambigui: se da un lato l’efficienza ` buona, la u edeviazione standard nei valori rilevati ` sempre la pi` elevata, di molte volte e usuperiore agli altri test, pari addirittura al 7% del risultato, contro il 3% delvalore pi` critico, quello con 8 processi. Considerando il solo conteggio dei usupporti, lo speedup raggiunto con 8 processi ` di solo 2,68 con un’efficienza epari a 0,34, ben lontani dai valori attesi per questo algoritmo.Database di 200 MByteNelle tabelle 7.4 e 7.5 sono esposti i rilevamenti effettuati e gli indici calcolati.Anche in questo caso lo speedup non ` soddisfacente e la deviazione standard edei test con 2 core risulta ancora la pi` elevata, pari addirittura al 5% del urisultato. Probabilmente a causa della dimensione del dataset, i risultati di que-sto test sono i pi` bassi. In questo caso lo speedup con 8 core ` inferiore u e 111
  • 125. Tempo di risposta AprioriSMP Supporti Dev. St. originale 20916 19116 355 2 core 17431 15504 880 4 core 13652 11158 379 8 core 10981 8274 392Tabella 7.4: Tempi di risposta AprioriSMP, contatori condivisi, database200 MByte Indici Speedup Efficienza AprioriSMP Supporti AprioriSMP Supporti 2 core 1,20 1,23 0,60 0,62 4 core 1,53 1,71 0,38 0,43 8 core 1,90 2,31 0,24 0,29Tabella 7.5: Indici prestazionali AprioriSMP, contatori condivisi, database200 MBytesia al relativo valore con dataset da 50 che da 500 MByte e la spiegazionepu` risiedere nel fatto che l’insieme dei frequenti da elaborare ` di dimen- o esioni maggiori rispetto alla cache e perci` pi` oneroso da gestire, dovendo o uutilizzare anche la memoria ram per lo scambio dei dati.Database di 500 MByteNelle tabelle 7.6 e 7.7 sono esposti i rilevamenti effettuati e gli indici calcolati.Anche in questo caso lo speedup non ` soddisfacente, anche se risulta il pi` e ualto del gruppo di test per questo algoritmo. Tempo di risposta AprioriSMP Supporti Dev. St. originale 49577 46690 2433 2 core 38239 35102 5735 4 core 30025 24561 1928 8 core 25292 19529 1853Tabella 7.6: Tempi di risposta AprioriSMP, contatori condivisi, database500 MByte 112
  • 126. Indici Speedup Efficienza AprioriSMP Supporti AprioriSMP Supporti 2 core 1,30 1,33 0,65 0,67 4 core 1,65 1,90 0,41 0,48 8 core 1,96 2,39 0,25 0,30Tabella 7.7: Speedup AprioriSMP, contatori condivisi, database 500 MByte Anche in questo caso l’efficienza migliore si ottiene con 2 soli processi;tale risultato si spiega dal basso numero di conflitti indotti dall’uso dei con-tatori condivisi quando due soli processi concorrono per l’acquisizione deirelativi lock. Introducendo ulteriori processi e partizionando di conseguenzai dati, l’incremento di prestazioni ottenuto ` inferiori alle aspettative e con- esente tempi di risposta ovviamente inferiori ma con un efficienza (e quindiun uso delle risorse) nettamente inferiore, pari e solo lo 0,25, considerandol’intero algoritmo.Risultati graficiIn questa sezione presentiamo in forma grafica tutto ci` che ` stato esposto o ein queste pagine in forma tabellare, come supporto alla valutazione deglialgoritmi. Come si nota dalle rappresentazioni precedenti, lo speedup non si pu` oritenere soddisfacente ne al crescere della dimensione del database ne alcrescere del numero di processori. Ci` ` dovuto all’alto numero di conflitti oenell’accesso alle risorse: in questo caso solo un processo alla volta pu` leggere oe scrivere. Nel caso ottimo ogni processo accede a risorse diverse, nel casopessimo tutti i processi accedono contemporaneamente alla stessa risorsa,ottenendo lo stesso tempo di un algoritmo seriale. Dai grafici 7.7 e 7.10 sivede come l’introduzione di nuovi processi, e relativi processori dedicati peril calcolo, non porti quei banefici che ci si aspetterebbe dal partizionamentodei dati e dal calcolo cooperativo.7.2.3 Test contatori privatiL’implementazione a contatori privati di AprioriSMP consiste nel replicaretante volte quanti sono i processi coinvolti le variabili intere che conteg- 113
  • 127. Figura 7.6: Tempi di risposta dell’algoritmo AprioriSMP con contatoricondivisigiano i supporti degli insiemi candidati. Questa implementazione rientranell’ottica full-replication [JA02] che dovrebbe garantire un ottimo grado diparallelizzazione eliminando le variabili condivise, quindi i conflitti, e ridu-cendo il risultato solo alla fine, con una somma generale di tutti i contatoriparziali. Questa tecnica, per`, richiede anche uno spazio di memoria diret- otamente proporzionale al numero di processi attivi che talvolta pu` indurre oall’utilizzo di memorie lente (ram o peggio disco fisso) per lo scambio deidati. Anche in questo caso per ogni tipologia di test si sono svolte 12 prove,come prodotto di 3 valori di dimensione del database (50, 200 e 500 MByte)per 4 insiemi di processi attivi per volta (1, 2, 4, 8). Successivamente si sonoscartate la pi` lenta e la pi` veloce e si ` calcolata la media dei tempi. In u u eseguito si sono calcolate deviazione standard e speedup. Nella prima tabella di ogni gruppo sono presenti i tempi medi di rispo-sta rilevati espressi in millisecondi: intera esecuzione dell’algoritmo Apriori- 114
  • 128. Figura 7.7: Speedup dell’algoritmo AprioriSMP con contatori condivisiSMP nella prima colonna, conteggio dei supporti nella seconda e deviazionestandard rispetto ai tempi di esecuzione rilevati nella terza. Nella seconda tabella di ogni gruppo si presentano lo speedup ed efficien-za dell’intero algoritmo e della sola parte relativa al conteggio dei supporti,quella con l’effettiva parallelizzazione. Nel tempo di esecuzione del conteg-gio in parallelo si tiene conto anche del tempo di creazione dei thread, delpartizionamento dei dati e della riduzione finale dei contatori.Database di 50 MByteNelle tabelle 7.8 e 7.9 sono esposti i rilevamenti effettuati e gli indici calcola-ti. Si noti come lo speedup sia molto pi` elevato rispetto al caso precedente, uottenuto grazie all’eliminazione dei conflitti nell’accesso alle variabili, repli-cate per ogni processo ed unite alla fine di ogni passaggio, ove ` necessaria ela sincronizzazione. In questo caso il risultato migliore lo si ottiene con 2 processi e la spiega-zione pi` verosimile ` che con un database cos` snello i contatori degli insiemi u e ı 115
  • 129. Figura 7.8: Speedup del conteggio dei frequenti con contatori condivisi Tempo di risposta AprioriSMP Supporti Dev. St. originale 5251 4557 121 2 core 2880 2272 55 4 core 1940 1354 42 8 core 1475 972 71Tabella 7.8: Tempi di risposta AprioriSMP, contatori privati, database 50MBytecandidati possono essere tutti contentuti nella memoria cache, molto veloce,ed anche le operazioni di riduzioni non sono onerose e possono attingere dadati in cache, ecco come si spiega lo speedup super-lineare (anche se di poco)ed un’efficienza cos` elevata (1 ` il valore massimo). Aumentando il numero ı edi processi lo speedup aumenta ma non in modo lineare, sintomo di una nonperfetta scalabilit` ma consente comunque di incrementare la velocit` del a aconteggio dei supporti di un fattore 4,91. 116
  • 130. Indici Speedup Efficienza AprioriSMP Supporti AprioriSMP Supporti 2 core 1,82 2,01 0,91 1,01 4 core 2,71 3,37 0,68 0,84 8 core 3,56 4,91 0,45 0,61Tabella 7.9: Indici prestazionali AprioriSMP, contatori privati, database 50MByteDatabase di 200 MByteNelle tabelle 7.10 e 7.11 sono esposti i rilevamenti effettuati e gli indici cal-colati. Analogamente al caso dell’implementazione con contatori condivisi,anche ora il dataset da 200 MByte da luogo alle rilevazioni peggiori. Tempo di risposta AprioriSMP Supporti Dev. St. originale 20916 19116 355 2 core 12481 10713 458 4 core 7915 6775 558 8 core 5672 4183 516Tabella 7.10: Tempi di risposta AprioriSMP, contatori privati, database 200MByte Indici Speedup Efficienza AprioriSMP Supporti AprioriSMP Supporti 2 core 1,68 1,78 0,84 0,89 4 core 2,64 2,82 0,66 0,71 8 core 3,69 4,57 0,46 0,57Tabella 7.11: Speedup AprioriSMP, contatori privati, database 200 MByte Le prestazioni infatti, pur essendo generalmente migliorate rispetto alcaso con contatori condivisi, non mantengono la stessa efficienza rispettoal caso precedente con questa implementazione segno che le repliche oraincidono notevolmente sui tempi di esecuzione, caso in cui non tutte riesconoad essere contenute in cache causando numerose letture e scritture anchenelle memorie pi` lente (solo ram, probabilmente), ecco perch` lo speedup u e 117
  • 131. con 2 processi scende a 1,68 ma sale a 3,69 con 8 core attivi, incrementandoanche l’efficienza di un decimo, rispetto al caso precedente.Database di 500 MByteNelle tabelle 7.12 e 7.13 sono esposti i rilevamenti effettuati e gli indicicalcolati. Questo ` il test con il database di dimensioni maggiori che consente edi valutarne le prestazioni al di fuori delle influenze della memoria cache. Tempo di risposta AprioriSMP Supporti Dev. St. originale 49577 46690 2433 2 core 27510 25761 3030 4 core 17012 15895 2632 8 core 9228 8300 919Tabella 7.12: Tempi di risposta AprioriSMP, contatori privati, database 500MByte Indici Speedup Efficienza AprioriSMP Supporti AprioriSMP Supporti 2 core 1,80 1,81 0,90 0,91 4 core 2,91 2,94 0,73 0,74 8 core 5,37 5,63 0,67 0,70Tabella 7.13: Indici prestazionali AprioriSMP, contatori privati, database500 MByte Nonostante sia impossibile pareggiare l’efficienza di 1,01 con 2 core e da-taset da 50MB, con 8 processi e 8 core attivi questa implementazione riescead ottenere uno speedup di 5,63 per il conteggio dei supporti con un’efficien-za di 0,70. Confrontando questo risultato con la letteratura, soprattutto[HKK97], si percepisce la bont` di questa implementazione, considerando asempre che si tratta di prototipo.Risultati graficiIn questa sezione presentiamo in forma grafica tutto ci` che ` stato esposto o ein queste pagine in forma tabellare, come supporto alla valutazione degli 118
  • 132. algoritmi.Figura 7.9: Tempi di risposta dell’algoritmo AprioriSMP con contatoriprivati In questo caso le prestazioni sono assolutamente soddisfacenti, parago-nabili a quelle degli algoritmi pi` sofisticati in letteratura che si attestano uattorno ad uno speedup pari a 6 su 8 processori considerando la sola parteparallela e poco meno di 4 considerando anche la lettura del database, vedigrafico 4.11, come per Common Candidate Partitioned Database, algoritmoparallelo a memoria condivisa presentato nel paragrafo 4.1.2. Lo speedupdi AprioriSMP, vedi grafico 7.10, si attesta infatti al 5,37 su 8 processo-ri considerando l’intera esecuzione dell’algoritmo (caricamento del databaseda disco escluso) e 5,63 considerando la sola parte effettivamente paralleliz-zata, il conteggio dei supporti. Si ritiene corretto considerare il valore ancheall’interno del contesto contenuto: l’ambiente Java, maestro di portabilit`, aastrazione, design ma sicuramente non all’altezza di linguaggi di program-mazione compilati come lo storico C, in fatto di efficienza di esecuzione,sia dal punto di vista di occupazione del processore che dal punto di vista 119
  • 133. Figura 7.10: Speedup dell’algoritmo AprioriSMP con contatori privatidell’occupazione della memoria (non dimentichiamo che il Garbage Collec-tor pu` intervenire durante l’esecuzione di qualsiasi task e che la macchina ovirtuale Java ha una consistente occupazione di memoria e di cpu, durantel’esecuzione).7.2.4 CommentiSi ` deciso di sviluppare AprioriSMP in due versioni con dinamiche diffe- erenti: una per garantire minore occupazione di memoria e l’altra la miglioreperformance temporale. Naturalmente la scelta di utilizzare contatori condi-visi ha influito negativamente sulle prestazioni, ma il gap tra le due versioni` decisamente elevato, come si intuisce dal grafico di figura 7.13, ove ancheela prestazione migliore dell’algoritmo a contatori condivisi ` pi` lenta rispet- e uto alla prestazione peggiore dell’algoritmo con contatori privati e successivariduzione. Impietoso il confronto tra le due versioni in configurazione 8 pro-cessori e database da 500 MByte: volendo calcolare uno speedup particolare 120
  • 134. Figura 7.11: Speedup del conteggio dei frequenti con contatori privatitra le due versioni, ne risuletebbe un incremento di velocit` di quasi 3 volte. a In modo simile nel grafico di figura 7.12 si intuisce come evolvono i tempidi risposta delle varie configurazioni al crescere della dimensione della base didati. Anche in questo caso risulta confortante il risultato della configurazionea contatori privati e 8 processori attivi, se posta a confronto con la versionesequenziale originale con un solo processore attivo. Lineare, infine lo speedup al crescere delle dimensioni del database; sinoti come con 2 e 4 processori rimanga costante mentre utilizzando tuttala potenza di calcolo a disposizione abbia addirittura una crescita notevolecon database di grandi dimensioni. Probabilmente la lettura pi` corretta ` u eopposta: con database piccoli non si riescono a sfruttare completamente gli8 core che soffrono di maggiori ritardi dovuti alla generazione dei thread,alla suddivisione delle partizioni e alla successiva aggregazione dei risultati,oltre al fatto che il numero di repliche ` in funzione del numero di processi eattivi e di conseguenza scende la probabilit` di poter ospitare tutto in cache. a 121
  • 135. Figura 7.12: Confronto tempi di risposta versioni AprioriSMP Per quanto riguarda il rilevamento dei cache miss, c’` da sottolineare la ecapacit` di ridurre la percentuale di fallimenti di AprioriSMP rispetto alla alibreria originale (vedi tabella 7.14). Notevole il fatto che con database didimensioni maggiori l’algoritmo possa godere di cache miss meno frequenti,forse grazie all’addestramento che riceve il prefetching della cpu. In en-trambe le versioni di AprioriSMP i cache miss non si discostano di moltopoich` la cache L2 ` condivisa tra i core ed anche nella versione con conta- e etori condivisi non ci saranno incoerenze di cache; il miglioramento rispettoalla versione con contatori privati probabilmente ` dovuto al fatto che ogni eprocesso potr` contare su un numero maggiore di contatori in cache, poich` a eil numero totale di essi ` di 1/8 rispetto all’altra versione ma i ritardi dovuti eagli stalli sui lock per accedere ai contatori superano i benefici dovuti allacache. 122
  • 136. Figura 7.13: Confronto speedup versioni AprioriSMP Cache Miss Dataset Richieste Fallimenti Percentuale orig 50 MByte 40 · 106 80 · 103 0,20 condivisi 50 MByte 101 · 106 82 · 103 0,08 privati 50 MByte 41 · 106 65 · 103 0,15 orig 500 MByte 712 · 106 798 · 103 0,09 condivisi 500 MByte 1227 · 106 758 · 103 0,06 privati 500 MByte 859 · 106 700 · 103 0,08 Tabella 7.14: Cache Miss AprioriSMP7.3 K-MeansSMPA differenza di Apriori, l’algoritmo K-Means non necessita di tecniche so-fisticate per la partizione dei dati e la parallelizzazione, grazie alla naturaspaziale dei suoi dati. La maggior parte degli algoritmi paralleli esistenti inletteratura si basano sul partizionamento statico dei punti del database daassegnare ad ogni processo, il calcolo delle distanze e delle medie in modo 123
  • 137. Figura 7.14: Scalabilit` dell’algoritmo AprioriSMP aasincrono e la sucessiva riduzione da parte del processo principale. Anche in questo caso uno script ha avviato 12 run di K-MeansSMP perogni dataset e per ogni configurazione; la prova migliore e la peggiore sonostate scartate mentre sono state effettuate le statistiche sulle rimanenti. Ilcomando usato per l’esecuzione ` a linea di comando e contentuto in uno escript (vedi allegati) simile a: java -Xmx2048m weka.clusterers.SimpleKMeans-N 150 -t iris200.arff. Le variabili in gioco sono esposte in tabella 7.15 Numero Core Dimensione Database Numero clusters 1 25 MByte 150 2 50 MByte 4 200 MByte 8 Tabella 7.15: Parametri test K-MeansSMP I risultati ottenuti vengono salvati su un file ed elaborato con un fogliodi calcolo per ottenere le statistiche desiderate. In figura 7.15 c’` un esempio e 124
  • 138. di come i tempi vengono presentati a video. Vi sono dei rilevamenti per ogniporzione dell’algortimo. Si pu` ricondurre ogni istruzione dello pseudocodice opresentato nel paragrafo 6.3 ad un rilevamento tempi, in special modo per ilcalcolo delle distanze, la porzione di codice pi` pesante computazionalmen- ute. In figura 7.16 invece si vede come K-MeansSMP suddivide in cluster ildatabase e calcola la deviazione standard. In questo caso il valore ` pari a e0 poich` vi sono 146 tipologie di dato e 146 cluster prodotti. e Figura 7.15: Esempio rilevamento dei tempi test K-MeansSMP7.3.1 DatasetI dataset utilizzati sono delle estensioni a 25, 50 e 200 MByte di iris, unodei pi` famosi dataset, reperibile presso il repository di machine learning udatabases di UCI e FIMI [uci] [fim]. Il dataset consiste di 3 classi equamentesuddivise e 4 attributi numerici. Ogni classi prende il nome dalla piantache rappresenta: Iris Setosa, Iris Versicolor, Iris Verginica. Le misure siriferiscono a lunghezza e larghezza di sepali (foglie) e petali. La struttura del 125
  • 139. Figura 7.16: Esempio output dei test K-MeansSMPdatabase ` rappresentata in figura 7.17. Come si nota il database ` suddiviso e ein 3 classi di uguale entit`. In origine ogni classe conta 50 oggetti mentre anei database dei test molti di pi` ma la suddivisione rimane invariata, con il u33% degli oggetti per ognuna. Le prime 4 immagini del grafico mostrano ladistribuzione di ognuna delle tre classi nei confronti delle misure effettuate;ad esempio 126
  • 140. Figura 7.17: Struttura del dataset Iris • Iris Setosa (blu): tutti i rappresentanti hanno lunghezze dei petali comprese tra 1 e 1,5; • Iris Versicolor (rosso): 3 di loro hanno petali lunghi tra 1,4 e 2,8cm, 32 tra 2,8 e 4,2 mentre i restanti 15 hanno misure tra 4,2 e 5,6cm. • Iris Verginica (azzurro):34 hanno petali di lunghezza tra 2,8 e 4,2cm; 47 tra 4,2 e 5,6 mentre i restanti 16 tra 5,6 e 6,9cm.e cos` via per tutti gli attributi. ı7.3.2 TestPer ogni dataset si presentano 2 tabelle: la prima espone le medie dei tem-pi rilevati e la deviazione standard calcolata; la seconda presenta speeduped efficienza ottenuti. Come previsto gli speedup sono pi` alti rispetto ad uAprioriSMP per la natura stessa dell’algoritmo, molto pi` facilmente pa- urallelizzabile ma non ancora cos` vicino al valore ideale. Probabilmente in ıfuturo sar` possibile ottimizzare questi risultati con l’utilizzo di strutture api` performanti o magari ottimizzando il codice ma ricordiamo che Java u 127
  • 141. ` un ambiente molto particolare: la sua gestione del garbage collector, adeesempio, pu` disturbare il rilevamento dei tempi, cos` come le chiamate ai o ımetodi e la creazione di thread richiedono tempi di esecuzione non marginali.Tutti i dati rilevati sono in millisecondi.Database di 25 MByteQuesto ` il dato relativo al dataset pi` piccolo. A differenza di AprioriSMP e unon ` necessario avere grossi moli di dati per avere dei miglioramenti signi- eficativi, infatti gi` in questo caso lo speedup ` vicino al 6 con 8 processori, a econ un’efficienza di 0,75. La deviazione standard ` abbastanza elevata ma, ecome anticipato, pu` dipendere dal lavoro di fondo che Java deve effettuare oper la gestione della macchina virtuale. 25 MByte Tempo di risposta Dev. St. originale 60936 236 2 core 33431 1385 4 core 18147 880 8 core 10128 43 Tabella 7.16: Tempi di risposta K-MeansSMP, database 25 MByte 25 MByte Speedup Efficienza 2 core 1,82 0,91 4 core 3,36 0,84 8 core 6,02 0,75 Tabella 7.17: Indici prestazionali K-MeansSMP, database 25 MByteDatabase di 50 MByteAnche in questo caso la deviazione standard risulta abbastanza elevata, maci` non ci impedisce di rilevare il buon risultato di K-MeansSMP, con 8 oprocessori ha uno speedup di 7,44 con un’efficienza del 93%. Queso risultatosi pu` gi` ritenere di ottima rilevanza, molto vicino sia allo speedup ideale o a(8 con 8 processori) che allo speedup rilevati dagli algoritmi presentati inletteratura ed esposti nei capitoli precedenti, anche’essi molto vicini al limite 128
  • 142. ideale. A questo si deve aggiungere che grazie alla struttura dell’algoritmonon ` necessario duplicare fisicamente l’insieme dei cluster, come avveniva eper l’insieme dei frequenti di AprioriSMP, il che consente di accogliere inmemoria RAM tutte le strutture dati necessarie, senza ricorrere a costosiaccesso a disco. 50 MByte Tempo di risposta Dev. St. originale 123506 6685 2 core 64883 5675 4 core 33297 763 8 core 16591 762 Tabella 7.18: Tempi di risposta K-MeansSMP, database 50 MByte 50 MByte Speedup Efficienza 2 core 1,90 0,95 4 core 3,71 0,93 8 core 7,44 0,93 Tabella 7.19: Indici prestazionali K-MeansSMP, database 50 MByteDatabase di 200 MByteL’ultima prova conferma la bont` dell’algoritmo K-MeansSMP, che sostan- azialmente pareggia il risultato precedente e conferma uno speedup vicino al7,5 con 8 processori. Anche in questo caso la deviazione standard ` alta, ea rappresentare la distanza, in termini temporali, tra una prova e l’altranonostante siano stati presi degli accorgimenti per arginare questo fenome-no: tra un run ed il successivo si svuotava il buffer di lettura/scrittura e siattendevano 5 secondi. La macchina non aveva shell grafica attivata e l’usoera esclusivo per queste prove, nessun altro aveva accesso e nessun altroprocesso (se non quelli del sistema operativo e la virtual machine Java) eraattivo. 129
  • 143. 200 MByte Tempo di risposta Dev. St. originale 534517 11803 2 core 267512 9740 4 core 143558 1372 8 core 71946 1995 Tabella 7.20: Tempi di risposta K-MeansSMP, database 200 MByte 200 MByte Speedup Efficienza 2 core 2,00 1 4 core 3,72 0,93 8 core 7,43 0,93 Tabella 7.21: Indici prestazionali K-MeansSMP, database 200 MByteRisultati graficiIn questa sezione presentiamo in forma grafica tutto ci` che ` stato esposto o ein queste pagine in forma tabellare, come supporto alla valutazione deglialgoritmi. Il grafico di figura 7.18 riporta i tempi di esecuzione dell’algoritmo al va-riare del numero di processori per i 3 set di database da 25, 50 e 200 MByte.L’informazione che scatena al primo impatto ` la drastica diminuzione dei etempi di risposta passando da 1 a 8 processori, soprattutto per grandi basidi dati, da circa 54 a 7 secondi. Immaginiamo che un istituto debba elabo-rare continuamente grosse quantit` di dati: in questo modo pu` rendere pi` a o uefficiente il suo lavoro, migliorando la produzione o riducendo le ore lavoro. In figura 7.19, invece, i tempi di risposta di K-MeansSMP sono visua-lizzati al variare della dimensione del database per evidenziare la linearit` adell’algoritmo. La crescita ` costante e proporzionale ai dati da elaborare, eil che presagisce una buona scalabilit`, come in figura 7.20 a L’ultima figura 7.21, infine, dimostra come le prestazioni ottenute sianodi buon livello: lo speedup ` il rapporto tra tempo sequenziale e tempo eparallelo; lo speedup ideale ` uguale al numero di processori su cui viene efatto girare l’algoritmo e tanto pi` lo speedup si avvicina al limite teorico, uquanto migliore ` il livello di parallelizzazione ottenuto. In questo caso con e8 processori e database di grandi dimensioni ci si avvicina al valore di 7,50. 130
  • 144. Figura 7.18: Tempi di risposta K-MeansSMP variando il numero diprocessori7.3.3 CommentiPer quanto riguarda K-MeansSMP ` stato relativamente semplice ottenere edelle buone prestazioni, considerata la natura dei suoi dati facilmente parti-zionabili tra i processi, in modo che ognuno di loro possa lavorare in modoasincrono per poi ridurre i risultati parziali nell’output finale. Ovviamentenon ` semplice ottenere speedup lineari: si debbono considerare i tempi di egestione dei thread, di suddivisione del database e successiva riduzione deidati, istruzioni sequenziali eseguite dal processo principale. Lo speedup si avvicina sempre al valore ideale e talvolta lo raggiunge:con 2 core e 200 MByte di dataset lo speedup ` 2 mentre con 8 core ` pari a e e7,43, segno che l’efficienza scende a causa, probabilmente del maggior lavoroimposto per la riduzione dei valori al crescere del numero di core. Se da un lato lo speedup con database da 200 MByte ` soddisfacen- ete, dall’altro non soddisfa la deviazione standard rilevata, nonostante l’usoesclusivo della macchina per i test e la scelta di non usare shell grafica. Va- 131
  • 145. Figura 7.19: Tempi di risposta K-MeansSMP variando la dimensione deldatabaselori cos` elevati si possono attribuire a due cause: la virtual machine di Java ıche imprevedibilmente lavora in background e il fatto che il risultato dell’al-goritmo K-Means dipende dal valore dei cluster iniziali scelti casualmente, eche si ` scelto di mantenere inalterato per avere una media pi` veritiera dei e utempi di risposta dell’algoritmo (scegliendo maliziosamente i cluster inizialiavremmo potuto aumentare in modo significativo le prestazioni). Per quanto riguarda il rilevamento dei cache miss, anche in questo caso ivalori diminuiscono sensibilmente utilizzando la versione SMP (vedi tabella7.22), facilitando l’incremento di prestazioni. 132
  • 146. Figura 7.20: Scalabilit` K-MeansSMP aCache Miss Dataset Richieste Fallimenti Percentuale orig 25 MByte 109 · 106 429 · 103 0,39 8 core 25 MByte 131 · 106 387 · 103 0,30 orig 200 MByte 1697 · 106 5370 · 103 0,32 8 core 200 MByte 1492 · 106 3438 · 103 0,23 Tabella 7.22: Cache Miss K-MeansSMP 133
  • 147. Figura 7.21: Speedup K-MeansSMP 134
  • 148. Capitolo 8Conclusioni “Ci sono 10 tipi di persone al mondo: chi capisce il codice binario e chi no.” Anonimo. 135
  • 149. In questo elaborato si ` dimostrato che ` necessario ripensare gli odierni e ealgoritmi in un’ottica di architettura parallela per poter essere utilizzati inmodo efficace dai moderni processori con tecnologia multi-core. Questi pro-cessori ottengono le prestazioni migliori se possono raggiungere la soluzionegrazie al contributo di tutti i core che ne fanno parte e possono sfruttare ibenefici della memoria condivisa (ram e cache L2 in alcuni casi). In particolare si ` scelto di riprogettare due algoritmi di Data Mining einclusi nella libreria Weka, sviluppata in Java dall’universit` di Waikato: aAprioriSMP per la ricerca di regole associative e K-MeansSMP per il clu-stering, dimostrando come ` possibile ridurre anche di molto il tempo di ecomputazione utilizzando un’architettura parallela rispetto all’esecuzione se-quenziale classica. Per ottenere il prototipo WekaSMP, si ` reso necessario euno studio della letteratura che affrontava problemi simili dalla generazionedegli algoritmi originali ai tentativi di parallelizzazione, a memoria condivisao distribuita, infine entrambi gli algoritmi sono stati progettati, implemen-tati e testati su una macchina con 8 core per verificarne tempi di esecuzione,speedup, efficienza e scalabilit`. a L’algoritmo Apriori non si presta nativamente alla parallelizzazione mavi sono varie implementazioni possibili in letteratura. AprioriSMP si basa suCount Distribution ed ottiene uno speedup di 5,37 con 8 processori ed un’ef-ficienza dello 0,67 con il database di maggiori dimensioni. K-MeansSMP,invece, si presta ad una parallelizzazioe semplice ed efficace grazie al fattoche i suoi dati possono essere posti su un piano cartesiano e considerati comepunti nello spazio. Il database pu` essere suddiviso equamente tra i processi oche cooperano in modo ottimale al raggiungimento del risultato. Lo speedupottenuto su 8 core ` di 7,43 con database da 200 MByte con un’efficienza epari a 0,93. Rispetto ai lavori presentati in letteratura le prestazioni sono pressoch` eequivalenti per entrambi gli algoritmi ma non vi sono analoghi riscontri perstudi su piattaforma Java. WekaSMP risulta perci` il primo prototipo di olibreria Java per il Data Mining che consente un utilizzo efficace anche suiprocessori multi-core, adattando il numero di processi gestiti sull’elaboratoreche lo ospita. 136
  • 150. Sviluppi futuriI test svolti su entrambi gli algoritmi hanno evidenziato quanto sia influentela gestione della memoria e di come si potrebbero migliorare i risultati po-tendo gestire la memoria cache in modo pi` efficiente, facendo attenzione ad uottenere il maggior numero di successi cache per non dover ricorrere a costoseletture da ram o peggio, da disco (nel caso di database di grosse dimensioni).Un possibile sviluppo futuro ` proprio quello di generare algoritmi in grado edi utilizzare in modo coerente la memoria cache a disposizione, o comunqueriuscire ad ottere una certa localit` spaziale con i dati in modo da ridurre ain modo consistente gli scambi tra cache e le altre memorie. Attualmente AprioriSMP con contatori condivisi cerca di ottenere il locksul contatore da incrementare; se libero acquisisce il lock, legge il valore elo incrementa per poi rilasciare il lock, ma se ` occupato rimane in atte- esa passiva riducendo drasticamente lo speedup. Le nuove librerie Java perla concorrenza mettono a disposizioni molteplici soluzioni molto pi` elasti- uche; una di queste permette di acquisire il lock solo se ` libero, altrimenti epassare ad altro. In questo senso se un contatore risulta occupato si pas-serebbe a quello successivo e cos` via fino a quello libero, per poi ritornare ısuccessivamente sugli altri. Grazie all’architettura a memoria condivisa, le comunicazioni tra i pro-cessi risultano infinitamente pi` rapide rispetto alle relative nei sistemi di- ustribuiti; un successivo passo ` quello di implementare anche gli altri algo- eritmi paralleli per la generazione di regole associative basati su Apriori epresentati nello stato dell’arte che hanno dato buoni risultati. K-MeansSMP, invece, pu` essere migliorato raffinando le tecniche di di- ovisione e riduzione dei dati ma non si riusciranno comunque ad ottenereindici di prestazioni di tanto migliori poich` gi` vicini al valore ideale. e a Il prototipo presentato, infine, ` possibile estenderlo con gli altri al- egoritmi presenti, sia di regole associative che di clustering, ma anche diclassificazione, qui tralasciate. 137
  • 151. 138
  • 152. RingraziamentiGiunto ormai al termine di questa avventura, non mi resta che ringraziare dicuore tutti quelli che mi hanno affiancato in questo lavoro e, pi` in generale, umi hanno sostenuto durante questi anni di studio: l’intero dipartimento diinformatica che mi ha accolto per sette lunghi anni, dalla portineria allasegreteria, dal ced all’immancabile bar di facolt` e a tutti i docenti dai aquali ho potuto imparare questa scienza e che mi hanno aperto un orizzonteinfinito verso il quale ora dovr` scegliere la mia strada. o Per avermi guidato verso la realizzazione del prototipo e aiutato nellastesura della tesi, ringrazio il prof. Salvatore Orlando e il dott. ClaudioLucchese che mi ha messo a disposizione il multi-core sul quale sono statieffettuati i test e si ` dimostrato sempre disponibile a darmi delle spiegazioni. e Tutti gli amici e compagni di corso dal primo all’ultimo anno, con i qualiho condiviso i miei momenti pi` importanti, sia dal punto di vista scolastico uche umano, che mi hanno aiutato nello studio e nello svago, tra i quali Luigi,Sergio, Elisa, Diego, Andrea, Michele e tutti i ragazzi della compagnia. L’ultimo ringraziamento, quello pi` importante per la mia esistenza, ` u eper la famiglia sulla quale posso sempre contare: nonna, zii, cugini. Infinela punta di diamante: i miei genitori che sono riusciti a condurmi a questotraguardo, che mi hanno cresciuto nella giusta direzione, sostenuto e spro-nato, che hanno riposto in me tutta la fiducia necessaria e che ora spero diripagare; a mia madre, che con la sua pazienza mi ha fatto superare anchei momenti pi` difficili e a mio padre che da lass`, sono sicuro, dopo avermi u useguito per tutto questo tempo, star` gi` versando una lacrima, vantandosi a aorgogliosamente di suo figlio, con i suoi colleghi angeli. 139
  • 153. 140
  • 154. Parte IIIAllegati 141
  • 155. Appendice AScript# !/ bin / bashcd / home / fabiopustetto / wekacore /1 corefor (( a =1; a <= 12; a ++))do java weka . associations . Apriori -t ../ mushroom50 . arff >> t est5 0 _1 co re _ VS . txt sync sleep 5doneecho fine test 1 core 50 MBcd / home / fabiopustetto / wekacore /2 corefor (( a =1; a <= 12; a ++))do java weka . associations . Apriori -t ../ mushroom50 . arff >> t est5 0 _2 co r e_ VS . txt sync sleep 5doneecho fine test 2 core 50 MBcd / home / fabiopustetto / wekacore /4 corefor (( a =1; a <= 12; a ++))do java weka . associations . Apriori -t ../ mushroom50 . arff >> t est 50 _4 co r e_ VS . txt sync sleep 5doneecho fine test 4 core 50 MBcd / home / fabiopustetto / wekacore /8 core 143
  • 156. for (( a =1; a <= 12; a ++))do java weka . associations . Apriori -t ../ mushroom50 . arff >> t est5 0 _8 co re _ VS . txt sync sleep 5doneecho fine test 8 core 50 MBcd / home / fabiopustetto / wekacore /1 corefor (( a =1; a <= 12; a ++))do java - Xmx2048mweka . associations . Apriori -t ../ mushroom200 . arff >> t e s t 20 0 _ 1 c o r e _ V S . txt sync sleep 5doneecho fine test 1 core 200 MBcd / home / fabiopustetto / wekacore /2 corefor (( a =1; a <= 12; a ++))do java - Xmx2048m weka . associations . Apriori -t ../ mushroom200 . arff >> t e s t 20 0 _ 2 c o r e _ V S . txt sync sleep 5doneecho fine test 2 core 200 MBcd / home / fabiopustetto / wekacore /4 corefor (( a =1; a <= 12; a ++))do java - Xmx2048m weka . associations . Apriori -t ../ mushroom200 . arff >> t e s t 20 0 _ 4 c o r e _ V S . txt sync sleep 5doneecho fine test 4 core 200 MBcd / home / fabiopustetto / wekacore /8 corefor (( a =1; a <= 12; a ++))do java - Xmx2048m weka . associations . Apriori -t ../ mushroom200 . arff >> t e s t 20 0 _ 8 c o r e _ V S . txt sync sleep 5doneecho fine test 8 core 200 MBcd / home / fabiopustetto / wekacore /1 core 144
  • 157. for (( a =1; a <= 12; a ++))do java - Xmx2048m weka . associations . Apriori -t ../ mushroom500 . arff >> t e s t 50 0 _ 1 c o r e _ V S . txt sync sleep 5doneecho fine test 1 core 500 MBcd / home / fabiopustetto / wekacore /2 corefor (( a =1; a <= 12; a ++))do java - Xmx2048m weka . associations . Apriori -t ../ mushroom500 . arff >> t e s t 50 0 _ 2 c o r e _ V S . txt sync sleep 5doneecho fine test 2 core 500 MBcd / home / fabiopustetto / wekacore /4 corefor (( a =1; a <= 12; a ++))do java - Xmx2048m weka . associations . Apriori -t ../ mushroom500 . arff >> t e s t 50 0 _ 4 c o r e _ V S . txt sync sleep 5doneecho fine test 4 core 500 MBcd / home / fabiopustetto / wekacore /8 corefor (( a =1; a <= 12; a ++))do java - Xmx2048m weka . associations . Apriori -t ../ mushroom500 . arff >> t e s t 50 0 _ 8 c o r e _ V S . txt sync sleep 5doneecho fine test 8 core 500 MB 145
  • 158. 146
  • 159. Appendice BCoreB.1 Instances/* * * Class for handling an ordered set of weighted instances . * Typical usage ( code from the main () method of this class ): * * // Read all the instances in the file * reader = new FileReader ( filename ); * instances = new Instances ( reader ); * * All methods that change a set of instances are safe , ie . a change * of a set of instances does not affect any other sets of * instances . All methods that change a datasets ’s attribute * information clone the dataset before it is changed . * * @author Eibe Frank ( eibe@cs . waikato . ac . nz ) * @author Len Trigg ( trigg@cs . waikato . ac . nz ) * @version $Revision : 1.67 $ */public class Instances implements Serializable { /* * for serialization */ static final long s e r i a l Ve r s i o n U I D = -19412345060742748 L ; /* * The filename extension that should be used for arff files */ public static String FILE_EXT ENSION = " . arff " ; /* * The filename extension that should be used for bin serialized instances files */ public static String S E R I A L I Z E D _ O B J _ F I L E _ E X T E N S I O N = " . bsi " ; /* * The keyword used to denote the start of an arff header */ 147
  • 160. static String ARFF_RELATION = " @relation " ;/* * The keyword used to denote the start of the arff data section */static String ARFF_DATA = " @data " ;/* * The dataset ’s name . */protected /* @spec_public non_null@ */ String m_R elationN ame ;/* * The attribute information . */protected /* @spec_public non_null@ */ FastVector m_Attributes ;/* * The instances . */protected /* @spec_public non_null@ */ FastVector m_Instances ;/* * The class attribute ’s index */protected int m_ClassIndex ;/* * Buffer of values for sparse instance */protected double [] m_ValueBuffer ;/* * Buffer of indices for sparse instance */protected int [] m _I nd ic e sB uf fe r ;/* * * Reads an ARFF file from a reader , and assigns a weight of * one to each instance . Lets the index of the class * attribute be undefined ( negative ). * * @param reader the reader * @throws IOException if the ARFF file is not read * successfully */public Instances ( /* @non_null@ */ Reader reader ) throws IOException { S tr ea m To ke ni z er tokenizer ; tokenizer = new S tr e am To ke n iz er ( reader ); initTokenizer ( tokenizer ); readHeader ( tokenizer ); m_ClassIndex = -1; m_Instances = new FastVector (1000); while ( getInstance ( tokenizer , true )) {}; compactify ();}/* * * Adds one instance to the end of the set . * Shallow copies instance before it is added . Increases the * size of the dataset if it is not large enough . Does not * check if the instance is compatible with the dataset . 148
  • 161. * * @param instance the instance to be added */public void add ( /* @non_null@ */ Instance instance ) { Instance newInstance = ( Instance ) instance . copy (); newInstance . setDataset ( this ); m_Instances . addElement ( newInstance );}/* * * Returns a random number generator . The initial seed of the random * number generator depends on the given seed and the hash code of * a string re presenta tion of a instances chosen based on the given * seed . * * @param seed the given seed * @return the random number generator */public Random g e t R a n d o m N u m b e r G e n e r a t o r ( long seed ) { Random r = new Random ( seed ); r . setSeed ( instance ( r . nextInt ( numInstances ())). toString (). hashCode () + seed ); return r ;}/* * * Copies instances from one set to the end of another * one . * * @param from the position of the first instance to be copied * @param dest the destination for the instances * @param num the number of instances to be copied */// @ requires 0 <= from && from <= numInstances () - num ;// @ requires 0 <= num ;protected void copyInstances ( int from , /* @non_null@ */ Instances dest , int num ) { for ( int i = 0; i < num ; i ++) { dest . add ( instance ( from + i )); }}/* * * Removes an instance at the given position from the set . * * @param index the instance ’s position ( index starts with 0) */// @ requires 0 <= index && index < numInstances ();public void delete ( int index ) { 149
  • 162. m_Instances . re mo v eE le m en tA t ( index );}/* * * Returns the mean ( mode ) for a numeric ( nominal ) attribute as * a floating - point value . Returns 0 if the attribute is neither nominal nor * numeric . If all values are missing it returns zero . * * @param attIndex the attribute ’s index ( index starts with 0) * @return the mean or the mode */public /* @pure@ */ double meanOrMode ( int attIndex ) { double result , found ; int [] counts ; if ( attribute ( attIndex ). isNumeric ()) { result = found = 0; for ( int j = 0; j < numInstances (); j ++) {if (! instance ( j ). isMissing ( attIndex )) { found += instance ( j ). weight (); result += instance ( j ). weight ()* instance ( j ). value ( attIndex );} } if ( found <= 0) {return 0; } else {return result / found ; } } else if ( attribute ( attIndex ). isNominal ()) { counts = new int [ attribute ( attIndex ). numValues ()]; for ( int j = 0; j < numInstances (); j ++) {if (! instance ( j ). isMissing ( attIndex )) { counts [( int ) instance ( j ). value ( attIndex )] += instance ( j ). weight ();} } return ( double ) Utils . maxIndex ( counts ); } else { return 0; }}/* * * Returns found and result instances * * @param attIndex the attribute ’s index ( index starts with 0) * @return a vector with result [0] and found [1] */public /* @pure@ */ double [] getResFnd ( int attIndex ) { 150
  • 163. double result , found ; double [] resFnd = new double [2]; int [] counts ; if ( attribute ( attIndex ). isNumeric ()) { result = found = 0; for ( int j = 0; j < numInstances (); j ++) { if (! instance ( j ). isMissing ( attIndex )) { found += instance ( j ). weight (); result += instance ( j ). weight ()* instance ( j ). value ( attIndex ); } } if ( found <= 0) { return null ; } else { resFnd [0] = result ; resFnd [1] = found ; return resFnd ; } } else if ( attribute ( attIndex ). isNominal ()) { counts = new int [ attribute ( attIndex ). numValues ()]; for ( int j = 0; j < numInstances (); j ++) { if (! instance ( j ). isMissing ( attIndex )) { counts [( int ) instance ( j ). value ( attIndex )] += instance ( j ). weight (); } } resFnd [0] = ( double ) Utils . maxIndex ( counts ); resFnd [1]=1; return resFnd ;// return ( double ) Utils . maxIndex ( counts ); } else { return null ; } } /* * * Merges a sets of Instances together . The sets mush have the same attributes * * @param first the first set of Instances * @param second the second set of Instances * @return the merged set of Instances */ public static Instances m e r g e I n s t a n c e s V e c t o r ( Instances [] vector ) { int i , numInst =0; // Create the vector of merged attributes FastVector newAttributes = new FastVector (); for ( i = 0; i < vector [0]. numAttributes (); i ++) { newAttributes . addElement ( vector [0]. attribute ( i )); } for ( i =0; i < vector . length ; i ++) 151
  • 164. numInst += vector [ i ]. numInstances (); // Create the set of Instances Instances merged = new Instances ( vector [0]. relationName () , newAttributes , numInst ); // Merge each instance for ( i = 0; i < vector . length ; i ++) {// merged . add ( first . instance ( i ). mergeInstance ( second . instance ( i ))); vector [ i ]. copyInstances (0 , merged , vector [ i ]. numInstances ()); } return merged ; }B.2 Instance/* * Instance . java * Copyright ( C ) 1999 Eibe Frank * */package weka . core ;import java . io . Serializable ;import java . util . Enumeration ;/* * * Class for handling an instance . All values ( numeric , date , nominal , string * or relational ) are internally stored as floating - point numbers . If an * attribute is nominal ( or a string or relational ) , the stored value is the * index of the corresponding nominal ( or string or relational ) value in the * attribute ’s definition . We have chosen this approach in favor of a more * elegant object - oriented approach because it is much faster . <p >*/public class Instance implements Copyable , Serializable { /* * for serialization */ static final long s e r i a l Ve r s i o n U I D = 1 4 8 2 6 3 5 1 9 4 4 9 9 3 6 5 1 2 2 L ; /* * Constant representing a missing value . */ protected static final double MISSING_VALUE = Double . NaN ; /* * * The dataset the instance has access to . Null if the instance * doesn ’t have access to any dataset . Only if an instance has * access to a dataset , it knows about the actual attribute types . */ 152
  • 165. protected /* @spec_public@ */ Instances m_Dataset ;/* * The instance ’s attribute values . */protected /* @spec_public non_null@ */ double [] m_AttValues ;/* * The instance ’s weight . */protected double m_Weight ;/* * * Constructor that copies the attribute values and the weight from * the given instance . Reference to the dataset is set to null . * ( ie . the instance doesn ’t have access to information about the * attribute types ) * * @param instance the instance from which the attribute * values and the weight are to be copied */// @ ensures m_Dataset == null ;protected Instance ( /* @non_null@ */ Instance instance ) { m_AttValues = instance . m_AttValues ; m_Weight = instance . m_Weight ; m_Dataset = null ;} 153
  • 166. 154
  • 167. Appendice CAprioriSMPC.1 Apriori/* * This program is free software ; you can redistribute it and / or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation ; either version 2 of the License , or * ( at your option ) any later version . * * This program is distributed in the hope that it will be useful , * but WITHOUT ANY WARRANTY ; without even the implied warranty of * ME RC H AN TA BI L IT Y or FITNESS FOR A PARTICULAR PURPOSE . See the * GNU General Public License for more details . * * You should have received a copy of the GNU General Public License * alo0ng with this program ; if not , write to the Free Software * Foundation , Inc . , 675 Mass Ave , Cambridge , MA 02139 , USA . *//* * Apriori . java * Copyright ( C ) 1999 Eibe Frank , Mark Hall , Stefan Mutter * */package weka . associations ;import java . util . Date ;import org . w3c . dom . CDATASection ;import weka . core . Attr ibuteSt ats ;import weka . core . Capabilities ;import weka . core . FastVector ;import weka . core . Instances ;import weka . core . Option ; 155
  • 168. import weka . core . OptionHandler ;import weka . core . SelectedTag ;import weka . core . Tag ;import weka . core . T e c h n i c a l I n f o r m a t i o n ;import weka . core . T e c h n i c a l I n f o r m a t i o n H a n d l e r ;import weka . core . Utils ;import weka . core . Capabilities . Capability ;import weka . core . T e c h n i c a l I n f o r m a t i o n . Field ;import weka . core . T e c h n i c a l I n f o r m a t i o n . Type ;import weka . filters . Filter ;import weka . filters . unsupervised . attribute . Remove ;import java . util . Enumeration ;import java . util . Hashtable ;import java . lang . Runtime ;public class Apriori extends Associator implements OptionHandler , CARuleMiner , T e c h n i c a l I n f o r m a t i o n H a n d l e r { /* * for serialization */ static final long s e r i a l Ve r s i o n U I D = 3 2 7 7 4 9 8 8 4 2 3 1 9 2 1 2 6 8 7 L ; /* * The minimum support . */ protected double m_minSupport ; /* * The upper bound on the support */ protected double m _ u p p e r B o u n d M i n S u p p o r t ; /* * The lower bound for the minimum support . */ protected double m _ l o w e r B o u n d M i n S u p p o r t ; /* * Metric type : Confidence */ protected static final int CONFIDENCE = 0; /* * Metric type : Lift */ protected static final int LIFT = 1; /* * Metric type : Leverage */ protected static final int LEVERAGE = 2; /* * Metric type : Conviction */ protected static final int CONVICTION = 3; /* * Metric types . */ public static final Tag [] T AGS_SELE CTION = { new Tag ( CONFIDENCE , " Confidence " ) , new Tag ( LIFT , " Lift " ) , new Tag ( LEVERAGE , " Leverage " ) , new Tag ( CONVICTION , " Conviction " ) }; /* * The selected metric type . */ 156
  • 169. protected int m_metricType = CONFIDENCE ;/* * The minimum metric score . */protected double m_minMetric ;/* * The maximum number of rules that are output . */protected int m_numRules ;/* * Delta by which m_minSupport is decreased in each iteration . */protected double m_delta ;/* * Significance level for optional significance test . */protected double m _ s i g n i f i c a n c e L e v e l ;/* * Number of cycles used before required number of rules was one . */protected int m_cycles ;/* * The set of all sets of itemsets L . */protected FastVector m_Ls ;/* * The same information stored in hash tables . */protected FastVector m_hashtables ;/* * The list of all generated rules . */protected FastVector [] m_allTheRules ;/* * The instances ( transactions ) to be used for generating the association rules . */protected Instances m_instances ;/* * Output itemsets found ? */protected boolean m _ o u t p u t It e m S e t s ;/* * Remove columns with all missing values */protected boolean m _ r e m o v e M i s s i n g C o l s ;/* * Report progress iteratively */protected boolean m_verbose ;/* * Only the class attribute of all Instances . */protected Instances m_onlyClass ;/* * The class index . */protected int m_classIndex ;/* * Flag indicating whether class association rules are mined . */protected boolean m_car ;protected int numCores = java . lan . Runtime . getRuntime (). a v a i l a b l e P r o c e s s o r s (); 157
  • 170. /* * * Constructor that allows to sets default values for the * minimum confidence and the maximum number of rules * the minimum confidence . */public Apriori () { resetOptions ();}/* * * Resets the options to the default values . */public void resetOptions () { m _ r e m o v e M i s s i n g C o l s = false ; m_verbose = false ; m_delta = 0.05; m_minMetric = 0.90; m_numRules = 10; m _ l o w e r B o u n d M i n S u p p o r t = 0.1; m _ u p p e r B o u n d M i n S u p p o r t = 1.0; m _ s i g n i f i c a n c e L e v e l = -1; m _ o u t p u t I t e m Se t s = false ; m_car = false ; m_classIndex = -1;}/* * * Method that generates all large itemsets with a minimum support , and from * these all association rules with a minimum confidence . * * @param instances the instances to be used for generating the associations * @throws Exception if rules can ’t be built successfully */public void b u i l d A s s o c i a t i o n s ( Instances instances ) throws Exception { double [] confidences , supports ; int [] indices ; FastVector [] sortedRuleSet ; int necSupport =0; if ( m _ r e m o v e M i s s i n g C o l s ) { instances = r e m o v e M i s s i n g C o l u m n s ( instances ); } if ( m_car && m_metricType != CONFIDENCE ) throw new Exception ( " For CAR - Mining metric type has to be confidence ! " ); if ( m_classIndex == -1) instances . setClassIndex ( instances . numAttributes () -1); 158
  • 171. else if ( m_classIndex < instances . numAttributes () && m_classIndex >= 0) instances . setClassIndex ( m_classIndex );else throw new Exception ( " Invalid class index . " );// can associator handle the data ?g et Ca pa b il it i es (). testWithFail ( instances );m_cycles = 0;if ( m_car ){ // m_instances does not contain the class attribute m_instances = L abeledIt emSet . divide ( instances , false ); // m_onlyClass contains only the class attribute m_onlyClass = L abeledIt emSet . divide ( instances , true );}else m_instances = instances ;if ( m_car && m_numRules == Integer . MAX_VALUE ){ // Set desired minimum support m_minSupport = m _ l o w e r B o u n d M i n S u p p o r t ;}else { // Decrease minimum support until desired number of rules found . m_minSupport = m _ u p p e r B o u n d M i n S u p p o r t - m_delta ; m_minSupport = ( m_minSupport < m _ l o w e r B o u n d M i n S u p p o r t ) ? m_lowerBoundMinSupport : m_minSupport ;}do { // Reserve space for variables m_Ls = new FastVector (); m_hashtables = new FastVector (); m_allTheRules = new FastVector [6]; m_allTheRules [0] = new FastVector (); m_allTheRules [1] = new FastVector (); m_allTheRules [2] = new FastVector (); if ( m_metricType != CONFIDENCE || m _ s i g n i f i c a n c e L e v e l != -1) { m_allTheRules [3] = new FastVector (); m_allTheRules [4] = new FastVector (); m_allTheRules [5] = new FastVector (); } sortedRuleSet = new FastVector [6]; sortedRuleSet [0] = new FastVector (); sortedRuleSet [1] = new FastVector (); sortedRuleSet [2] = new FastVector (); if ( m_metricType != CONFIDENCE || m _ s i g n i f i c a n c e L e v e l != -1) { 159
  • 172. sortedRuleSet [3] = new FastVector (); sortedRuleSet [4] = new FastVector (); sortedRuleSet [5] = new FastVector (); } if (! m_car ){ f i n d L a r g e I t e m S e t s (); if ( m _ s i g n i f i c a n c e L e v e l != -1 || m_metricType != CONFIDENCE ) f i n d R u l e s B r u t e F o r c e (); else f i n d R u l e s Q u i c k ly (); } else { f i n d L a r g e C a r I t e m S e t s (); f i n d C a r R u l e s Q u i c k l y (); } int j = m_allTheRules [2]. size () -1; supports = new double [ m_allTheRules [2]. size ()]; for ( int i = 0; i < ( j +1); i ++) supports [j - i ] = (( double )(( ItemSet ) m_allTheRules [1]. elementAt (j - i )). support ())*( -1); indices = Utils . stableSort ( supports ); for ( int i = 0; i < ( j +1); i ++) {sortedRuleSet [0]. addElement ( m_allTheRules [0]. elementAt ( indices [j - i ]));sortedRuleSet [1]. addElement ( m_allTheRules [1]. elementAt ( indices [j - i ]));sortedRuleSet [2]. addElement ( m_allTheRules [2]. elementAt ( indices [j - i ]));if ( m_metricType != CONFIDENCE || m _ s i g n i f i c a n c e L e v e l != -1) { sortedRuleSet [3]. addElement ( m_allTheRules [3]. elementAt ( indices [j - i ])); sortedRuleSet [4]. addElement ( m_allTheRules [4]. elementAt ( indices [j - i ])); sortedRuleSet [5]. addElement ( m_allTheRules [5]. elementAt ( indices [j - i ]));} } // Sort rules according to their confidence m_allTheRules [0]. r e m o v e A l l E l e m e n t s (); m_allTheRules [1]. r e m o v e A l l E l e m e n t s (); m_allTheRules [2]. r e m o v e A l l E l e m e n t s (); if ( m_metricType != CONFIDENCE || m _ s i g n i f i c a n c e L e v e l != -1) {m_allTheRules [3]. r e m o v e A l l E l e m e n t s ();m_allTheRules [4]. r e m o v e A l l E l e m e n t s ();m_allTheRules [5]. r e m o v e A l l E l e m e n t s (); } confidences = new double [ sortedRuleSet [2]. size ()]; int sortType = 2 + m_metricType ; for ( int i = 0; i < sortedRuleSet [2]. size (); i ++)confidences [ i ] = (( Double ) sortedRuleSet [ sortType ]. elementAt ( i )). doubleValue (); indices = Utils . stableSort ( confidences ); for ( int i = sortedRuleSet [0]. size () - 1; ( i >= ( sortedRuleSet [0]. size () - m_numRules )) && ( i >= 0); i - -) { 160
  • 173. m_allTheRules [0]. addElement ( sortedRuleSet [0]. elementAt ( indices [ i ]));m_allTheRules [1]. addElement ( sortedRuleSet [1]. elementAt ( indices [ i ]));m_allTheRules [2]. addElement ( sortedRuleSet [2]. elementAt ( indices [ i ]));if ( m_metricType != CONFIDENCE || m _ s i g n i f i c a n c e L e v e l != -1) { m_allTheRules [3]. addElement ( sortedRuleSet [3]. elementAt ( indices [ i ])); m_allTheRules [4]. addElement ( sortedRuleSet [4]. elementAt ( indices [ i ])); m_allTheRules [5]. addElement ( sortedRuleSet [5]. elementAt ( indices [ i ]));} } if ( m_verbose ) {if ( m_Ls . size () > 1) { System . out . println ( toString ());} } if ( m_minSupport == m _ l o w e r B o u n d M i n S u p p o r t || m_minSupport - m_delta > m _ l o w e r B o u n d M i n S u p p o r t ) m_minSupport -= m_delta ; else m_minSupport = m _ l o w e r B o u n d M i n S u p p o r t ; necSupport = Math . round (( float )(( m_minSupport * ( double ) m_instances . numInstances ())+0.5)); m_cycles ++; } while (( m_allTheRules [0]. size () < m_numRules ) && ( Utils . grOrEq ( m_minSupport , m _ l o w e r B o u n d M i n S u p p o r t )) /* ( necSupport >= l o w e r B o u n d N u m I n s t a n c e s S u p p o r t ) */ /* ( Utils . grOrEq ( m_minSupport , m _ l o w e r B o u n d M i n S u p p o r t )) */ && ( necSupport >= 1)); m_minSupport += m_delta ;}/* * * Method that finds all large itemsets for the given set of instances . * * @throws Exception if an attribute is numeric */private void f i n d L a r g e I t e m S e t s () throws Exception { FastVector kMinusOneSets , kSets = null ; Hashtable hashtable ; int necSupport , necMaxSupport , i = 0; int numInstances = m_instances . numInstances (); C o u n t D i s t r i b u t i o n [] cdVector = new C o u n t D i s t r i b u t i o n [ numCores ]; // ha gli indirizzi delle partizioni di kSets ! FastVector [] kSetsVector = new FastVector [ numCores ]; necSupport = ( int )( m_minSupport * ( double ) m_instances . numInstances ()+0.5); necMaxSupport = ( int )( m _ u p p e r B o u n d M i n S u p p o r t * 161
  • 174. ( double ) m_instances . numInstances ()+0.5);// converte le transazioni nell ’ 1 - itemset candidatokSets = Aprio riItemS et . singletons ( m_instances );for ( int core = 0; core < numCores ; core ++){ kSetsVector [ core ] = Apriori ItemSet . d u p l i c a t e I t e m S e t s ( kSets ); cdVector [ core ] = new C o u n t D i s t r i b u t i o n ( core , kSetsVector [ core ] , m_instances );}for ( int core = 0; core < numCores ; core ++) cdVector [ core ]. start ();for ( int core = 0; core < numCores ; core ++) cdVector [ core ]. join (); // attendi la fine dei threads// unisce tutti i kSets trovati p arallel amentekSets = Aprio riItemS et . m e r g e _ k S e t s V e c t o r ( kSetsVector );// elimina item non frequenti , crea 1 - itemsetkSets = Aprio riItemS et . del eteItemS ets ( kSets , necSupport , necMaxSupport );if ( kSets . size () == 0) return ;do { // comincia il vero apriori ! m_Ls . addElement ( kSets ); kMinusOneSets = kSets ; // crea Lk da Ck -1 kSets = Aprio riItemS et . m e r g e A l l I t e m S e t s ( kMinusOneSets , i , m_instances . numInstances ()); hashtable = Apriori ItemSet . getHashtable ( kMinusOneSets , kMinusOneSets . size ()); m_hashtables . addElement ( hashtable ); kSets = Aprio riItemS et . pruneItemSets ( kSets , hashtable ); for ( int core = 0; core < numCores ; core ++){ kSetsVector [ core ] = Apriori ItemSet . d u p l i c a t e I t e m S e t s ( kSets ); cdVector [ core ] = new C o u n t D i s t r i b u t i o n ( core , kSetsVector [ core ] , m_instances ); } for ( int core = 0; core < numCores ; core ++) cdVector [ core ]. start (); for ( int core = 0; core < numCores ; core ++) cdVector [ core ]. join (); // attendi la fine dei threads // unisci tutti i kSets trovati parallel amente kSets = Aprio riItemS et . m e r g e _ k S e t s V e c t o r ( kSetsVector ); 162
  • 175. kSets = Apri oriItemS et . del eteItem Sets ( kSets , necSupport , necMaxSupport ); i ++; } while ( kSets . size () > 0); } /* * * Method that finds all association rules . * * @throws Exception if an attribute is numeric */ private void f i n d R u l e s Q u i c k l y () throws Exception { FastVector [] rules ; // Build rules for ( int j = 1; j < m_Ls . size (); j ++) { FastVector cu rr en t It em S et s = ( FastVector ) m_Ls . elementAt ( j ); Enumeration enumItemSets = cu r re nt It e mS et s . elements (); while ( enumItemSets . ha sM o re El em e nt s ()) { Apri oriItemS et c urrentI temSet = ( Aprio riItemS et ) enumItemSets . nextElement (); rules = curre ntItemS et . generateRules ( m_minMetric , m_hashtables , j + 1); for ( int k = 0; k < rules [0]. size (); k ++) { m_allTheRules [0]. addElement ( rules [0]. elementAt ( k )); m_allTheRules [1]. addElement ( rules [1]. elementAt ( k )); m_allTheRules [2]. addElement ( rules [2]. elementAt ( k )); } } } } /* * * Main method . * * @param args the commandline options */ public static void main ( String [] args ) { runAssociator ( new Apriori () , args ); }C.2 CountDistribution/* * C o u n t D i s t r i b u t i o n . java * Created on 3 gennaio 2008 , 18.35 * To change this template , choose Tools | Template Manager * and open the template in the editor . */package weka . associations ; 163
  • 176. import weka . core . FastVector ;import weka . core . Instances ;import java . util . Hashtable ;/* * * Class implementing a distribuited support count based on Apriori algorithm . * @author Fabio Pustetto ( fpustett@dsi . unive . it ) */public class C o u n t D i s t r i b u t i o n extends Thread { FastVector my_kSets ; Instances my_instances ; int my_first , my_last ; long start , stop ; /* * * Creates a new instance of C o u n t D i s t r i b u t i o n * @param kSets the candidate item - set where support count * @param core_Ins tances the transaction database * @param first start index for describe the portion assigned to this thread * @param last end index for describe the portion assigned to this thread */ public C o u n t D i s t r i b u t i o n ( int numThread , FastVector kSets , Instances instances ) { my_kSets = kSets ; my_instances = instances ; my_first = (( my_instances . numInstances ()/ Apriori . numCores )*( numThread )); if ( numThread == Apriori . numCores -1) my_last = my_instances . numInstances (); else my_last = (( my_instances . numInstances ()/ Apriori . numCores )*( numThread +1)); } public void run () { // conta il supporto 1 - itemset , aggiorna i contatori all ’ interno del kSet Apri oriItemS et . upD ateCoun ters ( my_kSets , my_instances , my_first , my_last ); return ; }} 164
  • 177. Appendice DK-MeansSMPD.1 SimpleKMeans/* * SimpleKMeans . java * Copyright ( C ) 2000 Mark Hall * */package weka . clusterers ;import weka . classifiers . rules . DecisionTable ;import weka . core . Attribute ;import weka . core . Capabilities ;import weka . core . Instance ;import weka . core . Instances ;import weka . core . Option ;import weka . core . Utils ;import weka . core . W e i g h t e d I n s t a n c e s H a n d l e r ;import weka . core . Capabilities . Capability ;import weka . filters . Filter ;import weka . filters . unsupervised . attribute . R e p l a c e M i s s i n g V a l u e s ;import java . util . Enumeration ;import java . util . HashMap ;import java . util . Random ;import java . util . Vector ;public class SimpleKMeans extends R a n d o m i z a b l e C l u s t e r e r implements NumberOfClustersRequestable , W e i g h t e d I n s t a n c e s H a n d l e r { /* * for serialization */ static final long s e r i a l Ve r s i o n U I D = -3235809600124455376 L ; /* * 165
  • 178. * replace missing values in training instances */private R e p l a c e M i s s i n g V a l u e s m _ R e p l a c e M i s s i n g F i l t e r ;/* * * number of clusters to generate */protected static int m_NumClusters = 3;/* * * holds the cluster centroids */protected static Instances m _ C l u s t e r C e n t r o i d s ;/* * * Holds the standard deviations of the numeric attributes in each cluster */protected Instances m _ C l u s t e r S t d D e v s ;/* * * For each cluster , holds the frequency counts for the values of each * nominal attribute */protected static int [][][] m _ C l u s t e r N o m i n a l C o u n t s ;/* * * The number of instances in each cluster */private int [] m _Cluster Sizes ;/* * * attribute min values */private static double [] m_Min ;/* * * attribute max values */private static double [] m_Max ;/* * * Keep track of the number of iterations completed before convergence */private int m_Iterations = 0;/* * * Holds the squared errors for all clusters */private static double [] m _s q ua re dE r ro rs ; 166
  • 179. protected static int numCores = java . lang . Runtime . getRuntime (). a v a i l a b l e P r o c e s s o r s ();protected static boolean converged = false ;protected static int e m p ty C l u s t e r C o u n t ;/* * * the default constructor */public SimpleKMeans () { super (); m_SeedDefault = 10; setSeed ( m_SeedDefault );}/* * * Generates a clusterer . Has to initialize all fields of the clusterer * that are not being set via options . * * @param data set of instances serving as training data * @throws Exception if the clusterer has not been * generated successfully */public void bu ildClust erer ( Instances data ) throws Exception { // can clusterer handle the data ? g et Ca p ab il it i es (). testWithFail ( data ); m_Iterations = 0; m_Iterations = 0; long start , stop , kstart , kstop , fstart , fstop , totass , totdist , totfor ; m _ R e p l a c e M i s s i n g F i l t e r = new R e p l a c e M i s s i n g V a l u e s (); Instances instances = new Instances ( data ); instances . setClassIndex ( -1); m _ R e p l a c e M i s s i n g F i l t e r . s etInputF ormat ( instances ); instances = Filter . useFilter ( instances , m _ R e p l a c e M i s s i n g F i l t e r ); m_Min = new double [ instances . numAttributes ()]; m_Max = new double [ instances . numAttributes ()]; for ( int i = 0; i < instances . numAttributes (); i ++) { m_Min [ i ] = m_Max [ i ] = Double . NaN ; } m _ C l u s t e r C e n t r o i d s = new Instances ( instances , m_NumClusters ); int [] c l u s t e r A s s i g n m e n t s = new int [ instances . numInstances ()]; 167
  • 180. for ( int i = 0; i < instances . numInstances (); i ++) { updateMinMax ( instances . instance ( i )); } Random RandomO = new Random ( getSeed ()); int instIndex ; HashMap initC = new HashMap (); DecisionTable . hashKey hk = null ; for ( int j = instances . numInstances () - 1; j >= 0; j - -) { instIndex = RandomO . nextInt ( j +1); hk = new DecisionTable . hashKey ( instances . instance ( instIndex ) , instances . numAttributes () , true ); if (! initC . containsKey ( hk )) {m _ C l u s t e r C e n t r o i d s . add ( instances . instance ( instIndex ));initC . put ( hk , null ); } instances . swap (j , instIndex ); if ( m _ C l u s t e r C e n t r o i d s . numInstances () == m_NumClusters ) {break ; } } m_NumClusters = m _ C l u s t e r C e n t r o i d s . numInstances (); int i ; Instances [] tempI = new Instances [ m_NumClusters ]; m _s qu a re dE rr o rs = new double [ m_NumClusters ]; m_ClusterNominalCounts = new int [ m_NumClusters ][ instances . numAttributes ()][0]; // vettore di thread kMeansSMP [] kVector = new kMeansSMP [ numCores ]; Instances [][] tempIVector = new Instances [ m_NumClusters ][ numCores ]; int numInstances = instances . numInstances (); double [][][] res = new double [ instances . numAttributes ()][ numCores ][ m_NumClusters ]; double [][][] fnd = new double [ instances . numAttributes ()][ numCores ][ m_NumClusters ]; while (! converged ) { e m p t y C l u s t e r C o u n t = 0; m_Iterations ++; converged = true ; for ( i = 0; i < m_NumClusters ; i ++) {tempI [ i ] = new Instances ( instances , 0); for ( int core =0; core < numCores ; core ++) tempIVector [ i ][ core ]= new Instances ( instances , 0); 168
  • 181. } for ( int core = 0; core < numCores ; core ++){ kVector [ core ] = new kMeansSMP ( core , instances , clusterAssignments , tempIVector , res , fnd ); } for ( int core = 0; core < numCores ; core ++) kVector [ core ]. start (); for ( int core = 0; core < numCores ; core ++) kVector [ core ]. join (); // attendi la fine dei threads // ricalcola centroidi m _ C l u s t e r C e n t r o i d s = new Instances ( instances , m_NumClusters ); for ( i =0; i < m_NumClusters ; i ++){ double [] vals = new double [ instances . numAttributes ()]; double tmpRes , tmpFnd ; for ( int j =0; j < instances . numAttributes (); j ++){ tmpRes =0; tmpFnd =0; for ( int core = 0; core < numCores ; core ++){ tmpRes += res [ j ][ core ][ i ]; tmpFnd += fnd [ j ][ core ][ i ]; } vals [ j ]= tmpRes / tmpFnd ; } m _ C l u s t e r C e n t r o i d s . add ( new Instance (1.0 , vals )); } if ( e m p t y C l u s t e r C o u n t > 0) {m_NumClusters -= e m p t y C l u s t e r C o u n t ;tempI = new Instances [ m_NumClusters ]; } if (! converged ) {m _s qu ar e dE rr or s = new double [ m_NumClusters ];m_ClusterNominalCounts = new int [ m_NumClusters ][ instances . numAttributes ()][0]; } } converged = false ; e m p t y C l u s t e r C o u n t =0; m _ C l u s t e r S t d De v s = new Instances ( instances , m_NumClusters ); m_Cl usterSiz es = new int [ m_NumClusters ]; for ( i = 0; i < m_NumClusters ; i ++) { double [] vals2 = new double [ instances . numAttributes ()]; for ( int j = 0; j < instances . numAttributes (); j ++) {if ( instances . attribute ( j ). isNumeric ()) { vals2 [ j ] = Math . sqrt ( tempI [ i ]. variance ( j ));} else { 169
  • 182. vals2 [ j ] = Instance . missingValue ();} } m _ C l u s t e r S t d D ev s . add ( new Instance (1.0 , vals2 )); m_Cl usterSiz es [ i ] = tempI [ i ]. numInstances (); }}/* * * clusters an instance that has been through the filters * * @param instance the instance to assign a cluster to * @param updateErrors if true , update the within clusters sum of errors * @return a cluster number */protected static int c l u s t e r P r o c e s s e d I n s t a n c e ( Instance instance , boolean updateErrors ) { double minDist = Integer . MAX_VALUE ; int bestCluster = 0; for ( int i = 0; i < m_NumClusters ; i ++) { if ( m _ C l u s t e r C e n t r o i d s . instance ( i ) == null ) System . out . println ( " null " ); double dist = distance ( instance , m _ C l u s t e r C e n t r o i d s . instance ( i )); if ( dist < minDist ) {minDist = dist ;bestCluster = i ; } } if ( updateErrors ) { m _s qu ar e dE rr or s [ bestCluster ] += minDist ; } return bestCluster ;}/* * * Classifies a given instance . * * @param instance the instance to be assigned to a cluster * @return the number of the assigned cluster as an interger * if the class is enumerated , otherwise the predicted value * @throws Exception if instance could not be classified * successfully */public int cl us te r In st an c e ( Instance instance ) throws Exception { m _ R e p l a c e M i s s i n g F i l t e r . input ( instance ); m _ R e p l a c e M i s s i n g F i l t e r . batchFinished (); Instance inst = m _ R e p l a c e M i s s i n g F i l t e r . output (); 170
  • 183. return c l u s t e r P r o c e s s e d I n s t a n c e ( inst , false );}/* * * Calculates the distance between two instances * * @param first the first instance * @param second the second instance * @return the distance between the two given instances , between 0 and 1 */protected static double distance ( Instance first , Instance second ) { double distance = 0; int firstI , secondI ; for ( int p1 = 0 , p2 = 0; p1 < first . numValues () || p2 < second . numValues ();) { if ( p1 >= first . numValues ()) {firstI = m _ C l u s t e r C e n t r o i d s . numAttributes (); } else {firstI = first . index ( p1 ); } if ( p2 >= second . numValues ()) {secondI = m _ C l u s t e r C e n t r o i d s . numAttributes (); } else {secondI = second . index ( p2 ); } /* if ( firstI == m _ C l u s t e r C e n t r o i d s . classIndex ()) {p1 ++; continue ; } if ( secondI == m _ C l u s t e r C e n t r o i d s . classIndex ()) {p2 ++; continue ; } */ double diff ; if ( firstI == secondI ) {diff = difference ( firstI , first . valueSparse ( p1 ) , second . valueSparse ( p2 ));p1 ++; p2 ++; } else if ( firstI > secondI ) {diff = difference ( secondI , 0 , second . valueSparse ( p2 ));p2 ++; } else {diff = difference ( firstI , first . valueSparse ( p1 ) , 0);p1 ++; } distance += diff * diff ; } 171
  • 184. // return Math . sqrt ( distance / m _ C l u s t e r C e n t r o i d s . numAttributes ()); return distance ;}/* * * Computes the difference between two given attribute * values . * * @param index the attribute index * @param val1 the first value * @param val2 the second value * @return the difference */private static double difference ( int index , double val1 , double val2 ) { switch ( m _ C l u s t e r C e n t r o i d s . attribute ( index ). type ()) { case Attribute . NOMINAL : // If attribute is nominal if ( Instance . i sMissing Value ( val1 ) || Instance . isMissi ngValue ( val2 ) || (( int ) val1 != ( int ) val2 )) {return 1; } else {return 0; } case Attribute . NUMERIC : // If attribute is numeric if ( Instance . i sMissing Value ( val1 ) || Instance . isMissi ngValue ( val2 )) {if ( Instance . i sMissing Value ( val1 ) && Instance . isMissi ngValue ( val2 )) { return 1;} else { double diff ; if ( Instance . i sMissing Value ( val2 )) { diff = norm ( val1 , index ); } else { diff = norm ( val2 , index ); } if ( diff < 0.5) { diff = 1.0 - diff ; } return diff ;} } else {return norm ( val1 , index ) - norm ( val2 , index ); } 172
  • 185. default : return 0; }}/* * * Normalizes a given value of a numeric attribute . * * @param x the value to be normalized * @param i the attribute ’s index * @return the normalized value */private static double norm ( double x , int i ) { if ( Double . isNaN ( m_Min [ i ]) || Utils . eq ( m_Max [ i ] , m_Min [ i ])) { return 0; } else { return ( x - m_Min [ i ]) / ( m_Max [ i ] - m_Min [ i ]); }}/* * * Updates the minimum and maximum values for all the attributes * based on a new instance . * * @param instance the new instance */private void updateMinMax ( Instance instance ) { for ( int j = 0; j < m _ C lu s t e r C e n t r o i d s . numAttributes (); j ++) { if (! instance . isMissing ( j )) {if ( Double . isNaN ( m_Min [ j ])) { m_Min [ j ] = instance . value ( j ); m_Max [ j ] = instance . value ( j );} else { if ( instance . value ( j ) < m_Min [ j ]) { m_Min [ j ] = instance . value ( j ); } else { if ( instance . value ( j ) > m_Max [ j ]) { m_Max [ j ] = instance . value ( j ); } }} } }}/* * * Main method for testing this class . * 173
  • 186. * @param argv should contain the following arguments : <p > * -t training file [ - N number of clusters ] */ public static void main ( String [] argv ) { runClusterer ( new SimpleKMeans () , argv ); }D.2 K-MeansSMP/* * kMeansSMP . java * Created on 4 febbraio 2008 , 18.21 * To change this template , choose Tools | Template Manager * and open the template in the editor . */package weka . clusterers ;import weka . core . Instance ;import weka . core . Instances ;/* * * Implements K - Means in parallel for Shared Memory Processors * @author Fabio Pustetto ( fpustett@dsi . unive . it ) */public class kMeansSMP extends Thread { Instances my_instances ; Instances [][] my_tempI ; double [][][] my_res , my_fnd ; double [] resFnd ; int numThread , my_first , my_last ; Boolean my_converged ; int [] m y _ c l u s t e r A s s i g n m e n t s ; long start , stop ; Object e m p t y C l u s t e r C o u n t _ l o c k = new Object (); /* * Creates a new instance of kMeansSMP */ public kMeansSMP ( int numThread , Instances instances , int [] clusterAssignments , Instances [][] tempI , double [][][] res , double [][][] fnd ) { this . setName ( " " + numThread ); this . numThread = numThread ; my_instances = instances ; my_clusterAssignments = clusterAssignments ; my_tempI = tempI ; my_fnd = fnd ; my_res = res ; my_first = (( my_instances . numInstances ()/ SimpleKMeans . numCores )*( numThread )); if ( numThread == SimpleKMeans . numCores -1) 174
  • 187. my_last = my_instances . numInstances (); else my_last = (( my_instances . numInstances ()/ SimpleKMeans . numCores )*( numThread +1)); } public void run (){ // associa ogni istanza ad un cluster e confronta con la precedente for ( int i = my_first ; i < my_last ; i ++) { Instance toCluster = my_instances . instance ( i ); int newC = SimpleKMeans . c l u s t e r P r o c e s s e d I n s t a n c e ( toCluster , true ); if ( newC != m y _ c l u s t e r A s s i g n m e n t s [ i ]) { SimpleKMeans . converged = false ; } m y _ c l u s t e r A s s i g n m e n t s [ i ] = newC ; } // associa ogni centroide con le relative istanze for ( int i = my_first ; i < my_last ; i ++) { my_tempI [ m y _ c l u s t e r A s s i g n m e n t s [ i ]][ numThread ]. add ( my_instances . instance ( i )); } // crea i nuovi centroidi e calcola la media di ogni attributo for ( int i = 0; i < SimpleKMeans . m_NumClusters ; i ++) { if ( my_tempI [ i ][ numThread ]. numInstances () != 0){ for ( int j = 0; j < my_instances . numAttributes (); j ++) { resFnd = my_tempI [ i ][ numThread ]. getResFnd ( j ); my_res [ j ][ numThread ][ i ] = resFnd [0]; my_fnd [ j ][ numThread ][ i ] = resFnd [1]; } } } return ; }} 175
  • 188. 176
  • 189. Bibliografia [AIS93] Rakesh Agrawal, T. Imielinski, and A. Swami. Mining association rules between sets of items in large database. 1993. [AKD01] Gosling James Arnold Ken and Holmes David. The Java programming language. 2001. [amd] Amd - advanced micro devices, inc. www.amd.com. [AS94] Rakesh Agrawal and Ramakrishnan Srikant. Fast algorithms for mining association rules. In Jorge B. Bocca, Matthias Jarke, and Carlo Zaniolo, editors, Proc. 20th Int. Conf. Very Large Data Bases, VLDB, pages 487–499. Morgan Kaufmann, 12–15 1994. [AS96] R. Agrawal and J. C. Shafer. Parallel mining of associa- tion rules. Ieee Trans. On Knowledge And Data Engineering, 8:962–969, 1996. [BB95] Leon Bottou and Yoshua Bengio. Convergence properties of the K-means algorithms. In G. Tesauro, D. Touretzky, and T. Leen, editors, Advances in Neural Information Processing Systems, volume 7, pages 585–592. The MIT Press, 1995. [BJ06] Monia Bellalouna and Patrick Jaillet. A priori parallel machines scheduling, 2006. [Bod03] Ferenc Bodon. A fast apriori implementation. In Bart Goe- thals and Mohammed J. Zaki, editors, Proceedings of the IEEE ICDM Workshop on Frequent Itemset Mining Implementa- tions (FIMI’03), volume 90 of CEUR Workshop Proceedings, Melbourne, Florida, USA, 19. November 2003. 177
  • 190. [BPa] Zachary K. Baker and Viktor K. Prasanna. Efficient hardware data mining with the apriori algorithm on fpgas. [BPb] Zachary K. Baker and Viktor K. Prasanna. Hardware accelerated apriori algorithm for data mining. [BP06] Zachary K. Baker and Viktor K. Prasanna. An architecture for efficient hardware data mining using reconfigurable computing systems, 2006.[CDG+ 99] Jaturon Chattratichat, John Darlington, Yike Guo, S. Hedvall, Martin K¨ler, and Jameel Syed. An architecture for distribu- o ted enterprise data mining. In HPCN Europe ’99: Proceedings of the 7th International Conference on High-Performance Computing and Networking, pages 573–582, London, UK, 1999. Springer-Verlag. [Chi06] Yanbin Ye; Chia-Chu Chiang. A parallel apriori algorithm for frequent itemsets mining. In Software Engineering Resear- ch, Management and Applications, 2006. Fourth International Conference on Volume, pages 87 – 94, 2006. [CHX98] David W. Cheung, Kan Hu, and Shaowei Xia. Asynchro- nous parallel algorithm for mining association rules on a shared-memory multi-processors. In SPAA ’98: Proceedings of the tenth annual ACM symposium on Parallel algorithms and architectures, pages 279–288, New York, NY, USA, 1998. ACM. [CM] Sebastian Celis and David R. Musicant. Weka-parallel: Machine learning in parallel. [Cor] Paolo Corsini. Sempre pi` cpu quad core in commer- u cio. http://www.hwupgrade.it/news/cpu/sempre-piu-cpu- quad-core-in-commercio 19832.html. [CT03] Mario Cannataro and Domenico Talia. The knowledge grid. Commun. ACM, 46(1):89–93, 2003. 178
  • 191. [DM00] Inderjit S. Dhillon and Dharmendra S. Modha. A data- clustering algorithm on distributed memory multiproces- sors. In Large-Scale Parallel Data Mining, Lecture Notes in Artificial Intelligence, pages 245–260, 2000.[ELICICRC] Li Liu (Tsinghua University Eric Li (Intel Corp.) and China) Intel China Research Center. Optimization of frequent itemset mining on multiple-core processor. [fim] Frequent itemset mining dataset repository. http://fimi.cs.helsinki.fi/data/. [gnu] Gnu general public license. http://www.gnu.org. [Har75] J. A. Hartigan. Clustering algorithms, 1975. [HKK97] Eui-Hong Han, George Karypis, and Vipin Kumar. Scalable parallel data mining for association rules, 1997. [HS95] Maurice A. W. Houtsma and Arun N. Swami. Set-oriented mining for association rules in relational databases. In ICDE ’95: Proceedings of the Eleventh International Conference on Data Engineering, pages 25–33, Washington, DC, USA, 1995. IEEE Computer Society. [int] Intel corporation. www.intel.com. [ita] Italia personality inventories inventari italiani di personalit`. a http://www.itapi.org. [JA02] R. Jin and G. Agrawal. Shared memory parallelization of data mining algorithms: Techniques , programming interface, and performance., 2002. [jav] The java tutorial, concurrency. http://java.sun.com/docs/books/tutorial/essential/concurrency/. [JMJ98] Dan Judd, Philip K. McKinley, and Anil K. Jain. Large- scale parallel data clustering. IEEE Transactions on Pattern Analysis and Machine Intelligence, 20(8):871–876, 1998. 179
  • 192. [Jos03] Manasi N. Joshi. Parallel k - means algorithm on distributed memory multiprocessors, 2003. [Lea05] Doug Lea. The java.util.concurrent synchronizer framework. Sci. Comput. Program., 58(3):293–309, 2005. [LOP07] C. Lucchese, S. Orlando, and R. Perego. Parallel mining of frequent closed patterns: Harnessing modern computer architectures, 2007.[MRGW82] D. S. Johnson M. R. Garey and H. S. Witsenhausen. The complexity of the generalized lloyd-max problem, 1982. [OPPS02] S. Orlando, P. Palmerini, R. Perego, and F. Silvestri. An effi- cient parallel and distributed algorithm for counting frequent sets, 2002. [Pia03] Manuele Piastra. Cure+: un’implementazione dell’algoritmo di clustering gerarchico agglomerativo cure con riferimento al- l’analisi di grandi volumi di dati. Master’s thesis, Universit` a di Pisa, 2003. [SB00] Kilian Stoffel and Adbelkader Belkoniene. Parallel k/h-means clustering for large data sets, 2000. [SK96] Takahiko Shintani and Masaru Kitsuregawa. Hash based pa- rallel algorithm for mining association rules. In Proc. of the Conference on Parallel and Distributed Information Systems, 1996. [uci] Machine learning repository. http://archive.ics.uci.edu/ml/. [VKK94] Anshul Gupta Vipin Kumar, Ananth Grama and George Ka- rypis. Introduction to parallel computing: Algorithm design and Analysis. Benjaming Cummings, 1994. [wek] http://www.cs.waikato.ac.nz/ml/weka/. [WF05] Ian H. Witten and Eibe Frank. Data Mining - Pratical Ma- chine Learning Tools and Techniques. Diane Cerra, second edition, 2005. 180
  • 193. [Zak99] Mohammed Javeed Zaki. Parallel and distributed association mining: A survey, 1999.[ZOPL96] Mohammed Javeed Zaki, Mitsunori Ogihara, Srinivasan Par- thasarathy, and Wei Li. Parallel data mining for association rules on shared-memory multiprocessors. Technical Report TR618, 1996.[ZOPL04] Mohammed Javeed Zaki, Mitsunori Ogihara, Srinivasan Par- thasarathy, and Wei Li. Parallel data mining for association ru- les on shared-memory systems. In Knowledge and Information Systems, pages 1–29. Springer, 2004. [ZPL97] Mohammed Javeed Zaki, Srinivasan Parthasarathy, and Wei Li. A localized algorithm for parallel association mining. In SPAA ’97: Proceedings of the ninth annual ACM symposium on Parallel algorithms and architectures, pages 321–330, New York, NY, USA, 1997. ACM. [Zuo] Xin Zuo. Grid weka. http://smi.ucd.ie/ rinat/weka/. 181

×