`
     ALMA MATER STUDIORUM - UNIVERSITA DI BOLOGNA
                                     `
                           FACO...
Indice

Introduzione                                                                                                      ...
Elenco delle figure

 1.1   Il pattern state che realizza la macchina a stati finiti . . . . . . . .     3
 1.2   La macchin...
Elenco delle tabelle

 3.1   Tempi di esecuzione per cento risoluzioni e errore relativo calcolato
       per una singola ...
Introduzione

    Il motore tuProlog ` un interprete per il linguaggio Prolog, scritto interamente
                       ...
tuProlog, spiegando quali sono le caratteristiche principali dell’interprete e facendo
un’analisi di alto livello del suo ...
Capitolo 1

Il motore tuProlog

     L’obiettivo per cui l’interprete tuProlog venne ideato fu quello di poter disporre
di...
CAPITOLO 1. IL MOTORE TUPROLOG


modificate preservando il funzionamento del sistema. Queste caratteristiche sono
rese poss...
Figura 1.1: Il pattern state che realizza la macchina a stati finiti



proposizione viene decomposto in sotto obiettivi no...
CAPITOLO 1. IL MOTORE TUPROLOG




            Figura 1.2: La macchina a stati finiti del motore tuProlog


     Lo stato I...
compito di trovare una clausola compatibile dal contesto creato nell’ultimo punto
di scelta aperto, se questa non viene tr...
Capitolo 2


Benchmarks


    La scelta del benchmark ` un passo importante nell’analisi delle prestazioni di
            ...
CAPITOLO 2. BENCHMARKS


2.1       Profilers
    Il profiler ` essenzialmente uno strumento in grado di misurare le performa...
2.1. PROFILERS


di un’applicazione Java, essendo tuProlog scritto interamente in Java, la scelta del
profiler viene fatta ...
CAPITOLO 2. BENCHMARKS


2.1.1     Il profiler HPROF
    HPROF genera le informazioni relative al profiling attraverso le ch...
2.2. TEORIE DI TEST


2.2        Teorie di test
     Le teorie di test sono una parte del benchmark importante quanto il p...
CAPITOLO 2. BENCHMARKS


modo ` possibile incrementare o diminuire il lavoro svolto dal motore tuProlog
        e
senza mo...
Capitolo 3

Analisi dei tempi di esecuzione

    In questo capitolo vengono presentati i risultati ottenuti dal profiling d...
CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE


    dove la classe Agent, contenuta nel package alice.tuprolog, ` uno strume...
3.2. TEMPI DI ESECUZIONE DEI METODI


:- solve(goAndRepeat).
:- solve(go).
goAndRepeat :- goN(11).
go :- goal().
goN(0).
g...
CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE


buona precisione. Nella Tabella 3.2 vengono quindi illustrati i tempi delle ...
3.2. TEMPI DI ESECUZIONE DEI METODI




Teoria        Nome metodo                              Tempo esec % N. chiamate
  ...
CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE


             java.lang.StringBuffer.append              2,13     30505
      ...
3.2. TEMPI DI ESECUZIONE DEI METODI


              alice.tuprolog.Var.copy                               2,47       11939...
CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE


               Nome metodo                            Tempo esec %
         ...
`
                                                        3.3. ANALISI DELLA SCALABILITA


rimento la teoria Switch square...
CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE


3.4      Grafo delle chiamate
    Con lo scopo di analizzare pi` in dettagli...
3.4. GRAFO DELLE CHIAMATE




               N. nodo   Nome metodo
                     0   java.io.StringReader.read
    ...
CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE




                                                                          ...
Capitolo 4

Analisi dell’utilizzo della memoria

    Le stesse teorie presentate nel Capitolo 2 vengono ora utilizzate nel...
CAPITOLO 4. ANALISI DELL’UTILIZZO DELLA MEMORIA


     dove l’opzione hprof=heap=sites visualizza, per ogni metodo incluso...
4.2. ALLOCAZIONE DEGLI OGGETTI IN MEMORIA


te dai vari trace per lo stesso oggetto durante l’esecuzione di tuProlog. In q...
CAPITOLO 4. ANALISI DELL’UTILIZZO DELLA MEMORIA


           java.util.regex.Matcher                 2,36    56824
       ...
4.2. ALLOCAZIONE DEGLI OGGETTI IN MEMORIA


            alice.util.OneWayList                  2,39           58264
      ...
CAPITOLO 4. ANALISI DELL’UTILIZZO DELLA MEMORIA


              alice.tuprolog.ExecutionContext                 2,03      ...
4.2. ALLOCAZIONE DEGLI OGGETTI IN MEMORIA


                 N. stati   Nome oggetto          M. allocata %
              ...
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Upcoming SlideShare
Loading in …5
×

Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi

1,349 views
1,296 views

Published on

Il documento presenta il lavoro di tesi svolto da Michele Damian presso la facoltà di Ingegneria Informatica dell'università di Bologna. Lo scopo della dissertazione è quello di analizzare il comportamento di tuProlog (un interprete per il linguaggio di programmazione logica Prolog) e individuarne i punti critici al fine di migliorarne le prestazioni sia in termini di tempi di esecuzione che di gestione della memoria.

Published in: Technology, Business
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,349
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
18
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi

  1. 1. ` ALMA MATER STUDIORUM - UNIVERSITA DI BOLOGNA ` FACOLTA DI INGEGNERIA Corso di Laurea Triennale in Ingegneria Informatica Tesi di Laurea in Fondamenti di Informatica L-B Analisi di prestazione dell’interprete tuProlog su piattaforma Java Candidato Relatore Michele Damian Chiar.mo Prof. Enrico Denti Anno Accademico 2007/08
  2. 2. Indice Introduzione vii 1 Il motore tuProlog 1 2 Benchmarks 7 2.1 Profilers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.1.1 Il profiler HPROF . . . . . . . . . . . . . . . . . . . . . . . 10 2.2 Teorie di test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 3 Analisi dei tempi di esecuzione 13 3.1 Metodo di profiling . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 3.2 Tempi di esecuzione dei metodi . . . . . . . . . . . . . . . . . . . . 15 3.3 Analisi della scalabilit` . . . . . a . . . . . . . . . . . . . . . . . . . . 20 3.4 Grafo delle chiamate . . . . . . . . . . . . . . . . . . . . . . . . . . 22 4 Analisi dell’utilizzo della memoria 25 4.1 Metodo di profiling . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 4.2 Allocazione degli oggetti in memoria . . . . . . . . . . . . . . . . . 26 4.3 Analisi del garbage collector . . . . . . . . . . . . . . . . . . . . . . 32 4.4 Grafo delle allocazioni . . . . . . . . . . . . . . . . . . . . . . . . . 35 5 Confronto con la versione tuProlog 1.3 39 6 Conclusioni 41 i
  3. 3. Elenco delle figure 1.1 Il pattern state che realizza la macchina a stati finiti . . . . . . . . 3 1.2 La macchina a stati finiti del motore tuProlog . . . . . . . . . . . . 4 3.1 Analisi delle chiamate ai metodi pi` utilizzati nell’esecuzione delle u teorie. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 4.1 Analisi delle istanziazioni degli oggetti pi` allocati nell’esecuzione u delle teorie. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 iii
  4. 4. Elenco delle tabelle 3.1 Tempi di esecuzione per cento risoluzioni e errore relativo calcolato per una singola esecuzione. . . . . . . . . . . . . . . . . . . . . . . . 15 3.2 Tempi di lettura ed esecuzione. . . . . . . . . . . . . . . . . . . . . 16 3.3 Analisi dei metodi. . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 3.4 Analisi dei metodi totale. . . . . . . . . . . . . . . . . . . . . . . . . 20 3.5 Analisi della scalabilit` del motore tuProlog. . . . . . . . . . . . . a . 21 3.6 Associazione fra il numero identificativo di un nodo nella Figura 3.1 e il metodo che esso rappresenta. . . . . . . . . . . . . . . . . . . . 23 4.1 Analisi degli oggetti utilizzati. . . . . . . . . . . . . . . . . . . . . . 30 4.2 Analisi dell’utilizzo della memoria da parte degli oggetti rispetto all’obiettivo della teoria. . . . . . . . . . . . . . . . . . . . . . . . . 31 4.3 Analisi complessiva degli oggetti utilizzati. . . . . . . . . . . . . . . 32 4.4 Analisi del garbage collector. . . . . . . . . . . . . . . . . . . . . . . 35 4.5 Associazione fra il numero identificativo di un nodo nella Figura 4.1 e l’oggetto che esso rappresenta. . . . . . . . . . . . . . . . . . . . . 36 5.1 Comparazione dei tempi di esecuzione nelle singole teorie. . . . . . . 40 5.2 Comparazione dell’allocazione della memoria nelle singole teorie. . . 40 v
  5. 5. Introduzione Il motore tuProlog ` un interprete per il linguaggio Prolog, scritto interamente e in Java e interoperabile con le librerie fornite dalla piattaforma di sviluppo JDK. Giunto recentemente alla versione 2.1, ha portato con s´ diversi cambiamenti dal e punto di vista strutturale. L’obiettivo del lavoro ` quello di analizzare le pre- e stazioni dell’interprete in modo tale da porre delle basi utili per un suo ulteriore miglioramento in versioni successive. Alla conclusione, all’eventuale revisore del- l’interprete, sar` chiaro su quali parti del codice dovr` orientare la sua attenzione a a per apportare dei miglioramenti. A questo proposito l’analisi viene scomposta in due sezioni, con lo scopo di mettere in luce l’utilizzo della cpu e l’allocazione della memoria heap da parte dei metodi e delle classi implementate dal motore. Per quanto riguarda le prestazioni in termini di velocit`, allo stato attuale, i dati a di- a sposizione si basano unicamente sui tempi di esecuzione di alcune teorie utilizzate come benchmark per interpreti Prolog e considerate standard per test prestazionali di questo tipo di motore. A causa della variazione del sistema hardware sul quale vengono eseguiti i test, queste teorie per` non garantiscono tempi di esecuzione ab- o bastanza elevati da poter essere misurati con un apprezzabile errore sulla misura. Per questo motivo ad esse devono essere aggiunte delle nuove teorie per ottimizzare il benchmark. Un altro obiettivo che il lavoro si pone ` quello di analizzare come e sono cambiate le prestazioni dell’interprete in seguito al passaggio dalla versione 1 alla versione 2, la quale ha introdotto alcune modifiche a livello architetturale. In particolare viene presa come riferimento l’ultima release di ognuna delle due versioni, le quali sono rispettivamente la release 1.3 e 2.1. La dissertazione ` strutturata in modo da rendere pi` comprensibile possi- e u bile il motivo delle scelte fatte durante il progetto. Il Capitolo 1 presenta il motore vii
  6. 6. tuProlog, spiegando quali sono le caratteristiche principali dell’interprete e facendo un’analisi di alto livello del suo funzionamento. Nel Capitolo 2 vengono discussi i motivi che hanno portato alla scelta di HPROF come strumento di profiling, nonch´ le sue funzionalit` principali. Inoltre vengono presentate le teorie di test e a che hanno permesso di ricavare le informazioni utilizzate nell’analisi di tuProlog. Nei capitoli successivi si entra nella parte centrale del lavoro, esponendo queste informazioni e analizzando qual ` il loro significato. In particolare, nel Capitolo 3 e vengono analizzati i tempi di esecuzione, mentre nel Capitolo 4 viene analizzato l’utilizzo della memoria heap da parte di tuProlog. Nel Capitolo 5 si trova un confronto fra la pi` recente versione di tuProlog (2.1) e la versione 1.3. Questo u confronto viene fatto riferendosi ai tempi di esecuzione ed ai dati relativi all’uso della memoria. Infine, nel Capitolo 6, vengono tratte le conclusioni, esponendo quali sono le parti del motore che possono essere migliorate. viii
  7. 7. Capitolo 1 Il motore tuProlog L’obiettivo per cui l’interprete tuProlog venne ideato fu quello di poter disporre di uno strumento in grado di realizzare dei componenti intelligenti da poter utiliz- zare in un’infrastruttura internet dinamica. La base sulla quale viene costruito ` il e linguaggio di programmazione logica dichiarativo Prolog. Sin dal primo interprete, scritto nel 1972, le implementazioni dei sistemi Prolog si pongono come obiettivo quello di ottimizzare fattori quali il tempo di esecuzione e l’utilizzo della memoria. tuProlog, scritto interamente in Java, si discosta da questa tendenza, introducendo nell’architettura dell’interprete i concetti legati alla programmazione orientata agli oggetti, con lo scopo di poterne sfruttare i vantaggi. Tra questi pregi, apprezzati quando si deve scrivere codice utilizzabile in infrastrutture intelligenti e sistemi dinamici, vi sono: la portabilit`, ovvero la facilit` con cui ` possibile distribuire a a e l’applicazione, legata solamente alla presenza della piattaforma Java; la riusabi- lit`, che permette il riutilizzo di componenti in diversi sistemi; la modularit`, che a a determina anche la leggerezza del core. Queste nozioni, ben note nell’ingegneria del software, vengono realizzate implementando i pattern descritti in seguito. In primo luogo viene considerata la riusabilit` e la modularit` dell’architet- a a tura del motore tuProlog. Esse vengono rese possibili grazie alla separazione delle responsabilit` del sistema. Ogni responsabilit` viene associata ad un singolo ma- a a nager il quale controlla parte del motore tuProlog, in modo tale che le librerie e i predicati primitivi siano indipendenti dal motore e possano essere sostituite o 1
  8. 8. CAPITOLO 1. IL MOTORE TUPROLOG modificate preservando il funzionamento del sistema. Queste caratteristiche sono rese possibili grazie al pattern Fa¸ade [2], che riduce anche il numero di oggetti c che il cliente deve utilizzare, rendendo il sistema pi` semplice da gestire. Sono u presenti quattro tipi di manager per gestire diversi oggetti: il manager delle li- brerie, il manager delle primitive, il manager delle teorie e il manager degli stati della macchina a stati finiti. Il manager delle librerie rende possibile il caricamento delle librerie di predicati al bisogno, anche a runtime. Questa peculiarit`, oltre a a rendere estremamente configurabile tuProlog, fa si che il core metta a disposi- zione funzionalit` essenziali e quindi dia migliori prestazioni in termini di tempi a di esecuzione e utilizzo della memoria. Tuttavia esistono alcuni predicati che non sono stati inseriti nelle librerie, ma sono stati definiti nel motore. Essi sono quei predicati che influenzano il processo di risoluzione, come cut/0, quelli che sono troppo basilari per essere definiti altrove, come fail/0 e quelli che per ragioni di efficienza devono essere definiti vicino al core, come ’,’/2. A parte queste eccezioni tutti gli altri predicati sono contenuti all’interno di librerie, le cui seguenti sono caricate durante la fase di creazione del motore: la libreria BasicLibrary, la quale contiene i predicati pi` importanti, escludendo i predicati di I/O; la libreria IO- u Library che mette a disposizione alcuni dei predicati di input/output standard di Prolog, come write/1, read/1 e nl/0; la libreria ISOLibrary che definisce predicati standard ISO non definiti nelle due librerie precedenti; e JavaLibrary che definisce tutti quei predicati idonei a permettere l’interazione fra Prolog e Java. Tutte le altre librerie, come quelle definite dall’utente, possono essere caricate runtime. Il manager delle primitive ha il compito di riconoscere i predicati presenti all’interno delle teorie da quelli definiti nelle librerie o dall’utente. Questa operazione ` neces- e saria in quanto i due gruppi di predicati dovranno essere trattati differentemente durante il processo di risoluzione: i primi dovranno essere eseguiti come metodi Java ogni qualvolta la risoluzione di una teoria preveda prima la loro risoluzione, mentre gli altri saranno gestiti dalla macchina a stati finiti del motore tuProlog. Il parser del motore tuProlog legge la teoria Prolog riconoscendo la corretta sin- tassi e creando gli oggetti corrispondenti ai termini identificati. A supporto del parser il manager delle teorie crea il database delle clausole, a cui il motore far` a riferimento durante il processo di risoluzione e riconosce le direttive che devono essere eseguite immediatamente. Per questioni di ottimizzazione il corpo di una 2
  9. 9. Figura 1.1: Il pattern state che realizza la macchina a stati finiti proposizione viene decomposto in sotto obiettivi non appena essa stessa viene rico- nosciuta e non durante la fase di risoluzione. La proposizione viene rappresentata in memoria tramite una struttura dati ad albero in cui ogni foglia rappresenta un sotto obiettivo. Infine il manager del motore si occupa di gestire i motori di risoluzione crean- do un’istanza della macchina a stati finiti per ogni teoria che debba essere risolta (situazione che potrebbe presentarsi nel caso le teorie siano innestate l’una nell’al- tra). Questa macchina, nel quale ogni stato rappresenta un passo nella risoluzione della teoria, ` di fondamentale importanza in quanto realizza il motore di riso- e luzione delle teorie. Inoltre, il manager del motore permette di disaccoppiare il motore di risoluzione tuProlog dal resto dell’architettura favorendo l’estensibilit`a della macchina a stati finiti, a cui diventa possibile aggiungere nuovi stati. Per realizzare tutto ci` viene usato il pattern State [2] che permette di istanziare og- o getti il cui comportamento dipende dallo stato interno. La classe Engine mantiene al suo interno sia lo stato attuale della macchina a stati finiti sia lo stato suc- cessivo al quale deve transitare, mentre gli stati vengono realizzati derivando la classe astratta State. Pi` dettagliatamente il motore comprende uno stato iniziale u (Init), quattro stati rappresentanti le attivit` svolte durante il processo di riso- a luzione (Goal Selection, Goal Evaluation, Rule Selection e Backtrack) e quattro stati finali rappresentanti i modi in cui la risoluzione potrebbe terminare (HALT, TRUE, TRUE CP e FALSE). 3
  10. 10. CAPITOLO 1. IL MOTORE TUPROLOG Figura 1.2: La macchina a stati finiti del motore tuProlog Lo stato Init ` il punto di partenza e ha il compito di inizializzare il motore e di risoluzione estraendo gli obiettivi da valutare dalla teoria e creando un ogget- to che rappresenta il contesto nella quale l’obiettivo verr` valutato. Dopodich´ a e il controllo verr` passato allo stato Goal Selection il quale seleziona l’obiettivo a da valutare da una lista di obiettivi. Se questo obiettivo non viene trovato e il processo di esecuzione ` terminato si possono presentare due casi: se esiste un e punto di scelta e quindi la teoria pu` avere un’altra soluzione, lo stato finale sar` o a TRUE CP, altrimenti lo stato finale sar` TRUE. Se invece esiste un altro obiettivo a che pu` essere estratto deve essere valutato nello stato Goal Evaluation, a meno o che esso non rappresenti un numero, in qual caso la risoluzione termina nello stato FALSE. Se l’obbiettivo valutato nello stato Goal Evaluation rappresenta un pre- dicato primitivo allora la macchina a stati finiti transita nello stato Goal Selection o Backtrack, a seconda se la valutazione del predicato abbia avuto esito positivo o negativo, altrimenti transita nello stato Rule Selection e viene scelta una clausola compatibile per la risoluzione della teoria. In qualsiasi momento venga raggiunta una condizione di errore nello stato Goal Evaluation la macchina transita nello stato HALT e termina l’esecuzione. Nel caso non venga trovata nessuna clausola dal set di regole compatibili con l’attuale obiettivo allora lo stato successivo sar` a quello di Backtrack. Nello stato di Rule Selection viene preparato un nuovo con- testo di esecuzione, l’obiettivo viene unificato con la testa della clausola, il corpo della clausola viene aggiunto al risolvente, viene creato un nuovo punto di scelta ed il sistema transita nello stato di Goal Selection. Infine lo stato di Backtrack ha il 4
  11. 11. compito di trovare una clausola compatibile dal contesto creato nell’ultimo punto di scelta aperto, se questa non viene trovata il motore termina la sua esecuzione nello stato FAIL. 5
  12. 12. Capitolo 2 Benchmarks La scelta del benchmark ` un passo importante nell’analisi delle prestazioni di e un software. Le funzionalit` del benchmark, il modo in cui le prestazioni vengono a da esso misurate e il modo in cui i dati finali vengono rappresentati determinano quali parti del sistema ` possibile analizzare, come ` possibile analizzarle attraverso e e lo strumento e come i dati possono essere interpretati e rielaborati in modo che essi abbiano un significato concreto, tale da mettere alla luce le lacune del software esa- minato. Il capitolo ` diviso in due sezioni. La prima descrive brevemente i possibili e profiler adatti ad un’analisi di memoria e velocit` di esecuzione di un’applicazione a Java. Vengono poi confrontati i vari strumenti ed esposte le ragioni che hanno por- tato alla scelta dell’uno anzich´ dell’altro, soffermandosi sulle caratteristiche del e profiler HPROF, che fornisce i dati analizzati nei capitoli successivi. La seconda sezione riguarda invece le teorie di test, essenziali per esaminare il comportamento del sistema attraverso il profiler. La risoluzione delle clausole di Horn attraverso l’interprete tuProlog viene infatti eseguita per una teoria ben precisa, in quanto ogni teoria determina una successione dei cambiamenti di stato della macchina a stati finiti diverso dalle altre e di conseguenza un diverso comportamento del motore tuProlog. Quindi, durante la scrittura delle teorie, si considera che esse devono testare l’interprete in modo tale da poterne analizzare le prestazioni in tutti i possibili impieghi. 7
  13. 13. CAPITOLO 2. BENCHMARKS 2.1 Profilers Il profiler ` essenzialmente uno strumento in grado di misurare le performance e e analizzare il comportamento di un certo software durante la sua esecuzione. Il modo in cui questo viene attuato ed il modo in cui i dati collezionati vengono resi disponibili al cliente varia da profiler a profiler. La scelta dello strumento pi` u adatto allo scopo ` dettata non solo da quali variabili si debbano misurare durante e il profiling, ma anche dalla qualit` e quantit` delle informazioni che ` possibile a a e ricavare analizzando i dati forniti. Ad esempio, la scelta di un profiler in grado di misurare, o in grado di fornire dati dal quale sia possibile misurare, la memo- ria allocata da metodi, anche innestati, viene privilegiata rispetto ad un profiler che fornisce solamente i dati relativi alla memoria allocata durante l’esecuzione dell’applicazione. La qualit` delle informazioni ricavate pu` anche essere definita a o come la varianza dei dati misurati dallo strumento in esecuzioni diverse della stes- sa teoria o in esecuzioni della stessa teoria su piattaforme diverse. Maggiore ` lae varianza, minore sar` la qualit` delle informazioni in possesso. Questo problema ` a a e reso evidente dal fatto che i dati ricavati saranno disponibili per future estensioni o modifiche, cosa difficilmente possibile nel caso queste informazioni non siano para- gonabili con quelle misurate su altre piattaforme. Sebbene la risoluzione di questo quesito meriti una discussione a s´ ` possibile ovviare al problema esprimendo i ee tempi di esecuzione dei vari metodi1 con un valore relativo al tempo di esecuzione totale della teoria, anzich´ come valore assoluto. In questo modo si possono avere e informazioni relative a quanto incide un metodo sul tempo di esecuzione totale e questo dato rimane coerente fra diverse esecuzioni dell’interprete in diverse piat- taforme2 . Dato per scontato che lo strumento con cui viene fatto il profiling debba essere in grado di misurare la quantit` di memoria heap allocata e i tempi di esecuzione a Si vedr` pi` avanti che viene assunto come ipotesi il fatto che il tempo di esecuzione totale au 1 della teoria sia dato dalla somma del tempo di esecuzione dei singoli metodi e l’ottimizzazione di uno di essi comporta l’ottimizzazione del tempo di esecuzione totale. Non ` escluso che alcuni sistemi operativi, o alcune implementazioni JVM diverse da quella e 2 della Sun Microsystems, presentino ottimizzazioni che potrebbero far variare le velocit` di ese- a cuzione relative dei vari metodi. Queste variazioni dovrebbero comunque essere ragionevolmente piccole. 8
  14. 14. 2.1. PROFILERS di un’applicazione Java, essendo tuProlog scritto interamente in Java, la scelta del profiler viene fatta in primo luogo fra due categorie: i profiler commerciali e quelli non commerciali3 . Data la natura accademica della ricerca si opta per un profiler con licenza di tipo non commerciale. Questa scelta ` dettata dal fatto che utiliz- e zare un software commerciale porrebbe delle barriere economiche ad una futura prosecuzione dell’analisi svolta. Un altro fattore di interesse nella scelta dello stru- mento pi` consono `, come detto in precedenza, la possibilit` di misurare i tempi u e a di esecuzione e la quantit` di memoria allocata dai vari metodi utilizzati durante a la risoluzione di una teoria. Inoltre, osservando l’architettura della macchina a stati finiti, si pu` facilmente intuire che alcuni degli stati in cui il sistema transita o ` vengono utilizzati in maniera predominante rispetto ad altri. E quindi opportuno che la scelta venga indirizzata su un profiler che fornisca anche il numero di chia- mate ad un metodo, in modo da poter analizzare meglio se il sistema spende un certo tempo, o una certa quantit` di memoria, perch´ effettua un numero elevato a e di chiamate a questo o perch´ esso ` computazionalmente complesso. e e Una buona parte dei profiler con le caratteristiche indicate ` basata sull’in- e terfaccia JVM TI (acronimo di Java Virtual Machine Tool Interface) fornita nella piattaforma J2SE della Sun Microsystems a partire dalla versione 1.5. Questa interfaccia provvede un accesso allo stato della JVM da parte di altri strumenti attraverso delle API. All’atto dell’inizializzazione della VM una libreria, scritta in C o C++, viene caricata. Questa pu` invocare chiamate o registrarsi ad eventi, o secondo quanto descritto dall’interfaccia, allo scopo di interrogare la VM riguardo al suo stato. Nei seguenti capitoli i dati sono tutti ricavati dalla libreria HPROF. Questo agente, a differenza degli altri strumenti a disposizione, presenta i risul- tati in maniera piuttosto grezza, senza che vengano elaborati o formattati. Quel che pu` sembrare un difetto, o una mancanza di qualit`, ` in realt` un pregio. o ae a Gli altri profiler infatti, presentando un output pi` o meno elaborato, tendono a u mettere in risalto un aspetto del sistema anzich´ un altro, perdendo informazione. e Con HPROF ` possibile invece elaborare i dati grezzi, in modo tale da mettere in e risalto l’aspetto desiderato. Per software non commerciale si intende shareware o open source. 3 9
  15. 15. CAPITOLO 2. BENCHMARKS 2.1.1 Il profiler HPROF HPROF genera le informazioni relative al profiling attraverso le chiamate a JVM TI, le chiamate di ritorno dagli eventi della JVM TI e attraverso l’iniezione di byte code nelle classi caricate dalla VM. Il metodo usato per il profiling dipende dall’opzione con cui viene lanciato HPROF: l’opzione cpu=times inietta il byte code all’ingresso e all’uscita di ogni metodo, l’opzione heap inietta il byte code nel metodo <init> della classe java.lang.Object ed in ogni ‘newarray’ visto all’interno di tutti i metodi, mentre l’opzione cpu=sample utilizza un thread che, svegliandosi ` dopo un determinato intervallo di tempo, campiona lo stack. E possibile utilizzare questo strumento attraverso il comando java -agentlib:hprof[=opzioni] ClasseDaAnalizzare dove la ClasseDaAnalizzare ` la classe alice.tuprolog.Agent con, come primo e parametro, la teoria di test. In particolare, per fare il profiling dei tempi di ese- cuzione e dell’uso dell’heap dell’interprete tuProlog, vengono usati i comandi con le seguenti opzioni: hprof=cpu=times e hprof=heap=sites. Il primo presenta i dati riguardanti al tempo effettivo speso da un metodo in millisecondi, il numero di volte che quel metodo ` stato chiamato da un altro metodo e il trace dello stack e che ha causato quella chiamata. Il secondo comando mostra gli oggetti che sono stati creati durante l’esecuzione del programma. In particolare si possono trovare informazioni riguardanti la memoria occupata e il numero di oggetti vivi in un certo trace, nonch´ la memoria allocata e il numero di oggetti allocati durante l’e- e secuzione dell’intero programma nello stesso trace. Questo permette di analizzare il comportamento del garbage collector durante l’esecuzione della teoria. Inoltre ad ogni oggetto viene associato il trace dello stack che ha generato la sua allocazione in memoria, permettendo di fatto di risalire a quale metodo lo ha istanziato. Uno degli svantaggi di usufruire di un’analisi metodo a metodo, anche se compensato dalla precisione delle misure, ` quello di non poter sfruttare questo procedimento e per teorie il cui calcolo computazionale richieda tempi lunghi, in quanto il pro- filer allunga, anche notevolmente, i tempi di esecuzione. Di questo e dei motivi che hanno portato alla scelta delle opzioni descritte in precedenza viene discusso comunque pi` approfonditamente nei Capitoli 3 e 4. u 10
  16. 16. 2.2. TEORIE DI TEST 2.2 Teorie di test Le teorie di test sono una parte del benchmark importante quanto il profiler. Esse infatti determinano il comportamento dell’interprete durante la risoluzione delle clausole e perci` devono essere scelte con cura, in modo tale da permettere o un’analisi il pi` completa possibile. In questa dissertazione vengono presentati u i risultati dei test sul core tuProlog e sulla libreria BasicLibrary, senza testare le librerie aggiuntive come IOLibrary, JavaLibrary o ISOLibrary, anche se queste vengono caricate automaticamente durante l’inizializzazione del motore tuProlog. L’attenzione ` anche rivolta alla scalabilit` del sistema e alcune delle teorie uti- e a lizzate sono scritte in modo tale da permettere vari test sulla scalabilit` senza la a necessit` di pesanti modifiche alla teoria stessa o l’introduzione di nuove teorie. a I risultati trovati in studi precedenti [6], con lo scopo di confrontare tuPro- log con altri interpreti Prolog basati su Java, vennero trovati utilizzando teorie di test note, utilizzate in benchmark atti a misurare la velocit` di risoluzione delle a teorie. Per mantenere una certa continuit` e per poter confrontare i risultati del a lavoro svolto in passato si ` deciso di mantenere, per quanto possibile, alcune delle e teorie gi` utilizzate. Si noti comunque che i sistemi hardware e software su cui a sono stati fatti i test passati e quelli qui descritti sono completamente differenti4 . Questo rende il confronto fra i risultati ottenuti solo indicativo. Tuttavia, alcune delle teorie utilizzate devono essere omesse completamente dall’analisi dei tempi di esecuzione totale (non dall’analisi dei tempi di esecuzione dei vari metodi) per i motivi illustrati nel capitolo 3. Le teorie utilizzate sono: Crypt, Poly, Qsort, Queens, Query, Tak, Einstein’s riddle, Spanning tree e Switch square. Le prime sei fra queste sono teorie che erano gi` state usate come benchmark, mentre le altre sono state introdotte so- a lamente ora. Einstein’s riddle, Spanning tree e Switch square permettono inoltre di ingrandire l’albero di risoluzione delle teorie modificando l’obiettivo. In questo Il sistema su cui sono stati ricavati i risultati antecedenti a quelli qui descritti era equipaggiato 4 con un processore Pentium III 800 MHz, 256 MB di RAM su Windows 2000 e J2SE 1.5.0 09. Il sistema attuale consiste in un processore Pentium IV 2.6 GHz, con 1 GB di RAM su Windows XP Home Edition e J2SE 1.6.0 04. 11
  17. 17. CAPITOLO 2. BENCHMARKS modo ` possibile incrementare o diminuire il lavoro svolto dal motore tuProlog e senza modificare la teoria in s´, consentendo di analizzare la scalabilit` del siste- e a ma. Esse svolgono il seguente lavoro: Crypt, data una formula matematica nota, ne cerca la soluzione; Poly dato un polinomio nella forma 1 + x + y + z lo eleva alla decima potenza attraverso la trasformazione simbolica della formula; Qsort riordina una lista di numeri in modo crescente; Queens ` la versione estesa a nove e regine del pi` famoso problema delle 8 regine [1]; Query interroga un database u contenente per ogni nazione la rispettiva dimensione e il numero di cittadini e restituisce, conoscendo gi` il nome di una nazione, quella con la densit` pi` simile a au ad essa; Einstein’s riddle tenta di risolvere un indovinello nel quale viene chiesto chi ` il proprietario di un certo animale data una serie di indizi riguardo a cinque e persone di diversa nazionalit`, residenti in case di colore diverso e con una distinta a preferenza in termini di animali domestici, marche di sigarette e bevande; Span- ning tree data una lista di collegamenti fra nodi, ognuno con un certo peso, trova appunto lo spanning tree [1] di quell’albero; Switch square data una matrice 3x3, i cui valori possono essere vero o falso, tenta di negare i valori posizionati ad una distanza di Manhattan uguale o inferiore ad 1 da una certa cella e, attraverso una sequenza di commutazioni delle celle, cerca di rendere tutti i valori della matrice veri. 12
  18. 18. Capitolo 3 Analisi dei tempi di esecuzione In questo capitolo vengono presentati i risultati ottenuti dal profiling delle teorie di test esposte precedentemente. In particolare vengono mostrati quali sono i tempi di esecuzione totali delle teorie e i tempi di esecuzione dei vari metodi utilizzati durante la loro risoluzione. I dati ottenuti vengono poi elaborati in modo da mettere in evidenza quali sono i punti fondamentali su cui il motore deve essere migliorato. Per fare ci` viene prima messa alla prova la scalabilit` del sistema, o a facendo il profiling con teorie via via pi` complesse ed in secondo luogo vengono u costruiti i grafi delle chiamate ai metodi, in modo da evidenziare quali metodi effettuano le chiamate pi` costose da un punto di vista computazionale. u 3.1 Metodo di profiling Come gi` detto, i dati risultanti dal profiling devono fornire il tempo di ese- a cuzione totale della teoria, il tempo di esecuzione dei metodi e, indirettamente, per ognuno di essi, i tempi di esecuzione dei metodi innestati. Ci` ` possibile in oe quanto il profiler HPROF mette a disposizione il trace dello stack che ha generato la chiamata ad un particolare metodo, oltre ovviamente al tempo di esecuzione del metodo (mostrato come percentuale sul tempo di esecuzione totale) e al numero di volte in cui ` stato chiamato. Il comando utilizzato per fare ci` ` il seguente e oe java -agentlib:hprof=cpu=times alice.tuprolog.Agent teoria.pl 13
  19. 19. CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE dove la classe Agent, contenuta nel package alice.tuprolog, ` uno strumento che e accetta come parametro una teoria Prolog scritta in un file di testo e, dopo aver tentato di risolverla, stampa sullo standard output il risultato della valutazione. tuProlog permette di valutare una teoria in altri tre modi: lanciando un’interfaccia grafica ed inserendo la teoria in un’apposita TextBox, in un ambiente interattivo attraverso la classe alice.tuprologx.ide.CUIConsole oppure istanziando un oggetto Prolog in una classe Java. Il metodo utilizzato, a differenza degli altri, permette per` di isolare il motore tuProlog, in modo da non dover estromettere dal profiling o i dati non dovuti alla risoluzione, come ad esempio l’istanziazione della GUI. Per misurare il tempo di esecuzione in modo ottimale il file teoria.pl viene costruito in modo da separare tre diversi processi che avvengono durante la riso- luzione della teoria: l’inizializzazione del motore e il caricamento delle librerie1 , la lettura delle clausole di Horn e la risoluzione vera e propria. Vengono quin- di dapprima misurati i tempi di esecuzione della lettura della teoria assieme alla sua risoluzione risolvendo un certo obiettivo2 . Successivamente lo stesso obiet- tivo viene risolto per undici volte e viene sottratto il tempo di esecuzione pre- cedentemente trovato. Questo ` equivalente a misurare undici volte il tempo di e esecuzione dell’obiettivo pi` una volta il tempo di lettura della teoria per poi sot- u ` trarlo. E inoltre necessario evitare di misurare il tempo di creazione della JVM e per fare ci` viene scritta un’ulteriore teoria in cui viene usato il metodo Ja- o va java.lang.System.currentTimeMillis prima e dopo l’esecuzione dell’obiettivo. Il codice Prolog della teoria indicata risulta il seguente: time(T) :- class(’java.lang.System’) <- currentTimeMillis returns T. go(File) :- time(A), consult(File), time(B), C is B-A, print(C). dove File rappresenta il nome completo del file di testo in cui ` contenuta la e teoria da valutare. All’interno di questo file sono contenute le seguenti clausole, le quali permettono di valutare il tempo di lettura ed esecuzione: I tempi in cui questi processi avvengono sono dovuti alla JVM e non al motore tuProlog in 1 s´. e Per diminuire la dispersione delle misure ` stato misurato dodici volte il tempo di esecuzione e 2 dell’obiettivo e sottratto il valore massimo e minimo trovato. Il tempo di esecuzione ` la media e dei valori rimanenti. 14
  20. 20. 3.2. TEMPI DI ESECUZIONE DEI METODI :- solve(goAndRepeat). :- solve(go). goAndRepeat :- goN(11). go :- goal(). goN(0). goN(Z) :- goal(), W is Z-1, goN(W). dove goAndRepeat risolve undici volte l’obiettivo goal(). 3.2 Tempi di esecuzione dei metodi Nonostante il metodo java.lang.System.currentTimeInMillis restituisca un tem- po in millisecondi, la granularit` del valore dipende dal sistema in cui ` ospitata a e la JVM e potrebbe essere maggiore di 1 ms. Nel sistema in cui sono avvenute le misure non ` stato possibile misurare intervalli di tempo minori a 15 ms, il che e fa pensare che lo stesso valore corrisponda alla granularit` del sistema. Questo a ha reso impossibile un’apprezzabile misurazione delle teorie Crypt, Poly, Qsort, Queens e Query in quanto i tempi misurati contengono un errore sulla misura troppo elevato. Teoria Tempo di esecuzione (ms) Errore relativo % Crypt 1669 179,75 Einstein’s riddle 114598 2,62 Poly 31 9677,42 Qsort 671 447,09 Queens 2574 116,55 Query 312 961,54 Spanning tree 18034 16,64 Switch square 93787 3,20 Tak 105721 2,84 Tabella 3.1: Tempi di esecuzione per cento risoluzioni e errore relativo calcolato per una singola esecuzione. Dalla Tabella 3.1 si evince chiaramente che la misurazione dei tempi di let- tura della teoria e risoluzione dell’obiettivo non possono essere misurati con una 15
  21. 21. CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE buona precisione. Nella Tabella 3.2 vengono quindi illustrati i tempi delle teorie Einstein’s riddle, Spanning tree, Switch square e Tak i quali hanno errori relativi ragionevolmente contenuti. La distinzione fra le due modalit` di esecuzione ha a lo scopo di mettere in luce quali sono le parti del motore tuProlog che possono causare una degradazione delle performance. Teoria Lettura esec (ms) Esec (ms) Esec/Lettura esec % Einstein’s riddle 13807 13331 96,6 Spanning tree 2216 1868 84,3 Switch square 10110 9628 95,2 Tak 11373 10797 94,9 Tabella 3.2: Tempi di lettura ed esecuzione. Si pu` quindi affermare che il tempo di lettura risulta essere all’incirca costante al o variare della teoria3 . I metodi coinvolti nella risoluzione dell’obiettivo occuperan- no la maggior parte del tempo di esecuzione totale e questo permette un profiling senza distinguere fra lettura ed esecuzione, come ` stato fatto fin d’ora, dato che e non valutare i metodi coinvolti durante la prima fase non comporta una perdita di informazione significativa. Di seguito, nella Tabella 3.3, vengono presentati i risultati del profiling, ese- guiti nei modi descritti precedentemente. Per ogni teoria ci si limita a presentare i risultati di quei metodi il cui tempo impiegato a terminare4 ` superiore o uguale e al 2% del tempo di esecuzione totale. Nel caso di Spanning tree, la teoria pi` lunga, il numero di righe che il motore deve leggere u 3 ` 186, mentre nel caso di Tak, quella pi` corta, il numero di righe ` 29. e u e Il tempo impiegato ad un metodo per terminare non comprende il tempo impiegato ai metodi 4 innestati in esso ad essere eseguiti. 16
  22. 22. 3.2. TEMPI DI ESECUZIONE DEI METODI Teoria Nome metodo Tempo esec % N. chiamate java.lang.Object.wait 41,65 2 Crypt java.lang.AbstractStringBuilder.append 2,27 60583 alice.tuprolog.Var.occurCheck 21,35 14909053 alice.tuprolog.Struct.getTerm 14,24 30803506 alice.tuprolog.Var.free 6,01 4676006 java.util.AbstractList$Itr.next 4,60 4251755 alice.tuprolog.Struct.getArity 3,9 14871224 E.’s riddle java.util.ArrayList.get 3,82 5599360 alice.tuprolog.Var.unify 3,59 2488524 alice.tuprolog.Struct.unify 3,54 2482605 java.util.AbstractList$Itr.hasNext 3,49 4998320 java.util.ArrayList.add 3,27 5000191 alice.tuprolog.Var.getTerm 2,53 9374073 java.lang.AbstractStringBuilder.append 5,01 34179 java.io.StringReader.read 3,59 24662 alice.tuprolog.Tokenizer.readNextToken 3,12 8815 java.lang.StringBuffer.append 2,96 32544 Poly alice.tuprolog.Tokenizer.readToken 2,92 21835 java.io.StreamTokenizer.nextToken 2,21 17063 alice.tuprolog.Struct.toString 2,21 1986 java.io.StreamTokenizer.read 2,12 24662 alice.tuprolog.Var.occurCheck 3,65 24612 java.lang.AbstractStringBuilder.append 3,58 37404 Qsort alice.tuprolog.Struct.getTerm 3,28 65039 java.lang.StringBuffer.append 2,22 35912 java.io.StringReader.read 2,05 22652 Queens java.lang.Object.wait 36,27 2 java.lang.AbstractStringBuilder.append 3,9 34855 java.io.StringReader.read 2,73 24285 Query alice.tuprolog.Tokenizer.readNextToken 2,29 8534 alice.tuprolog.Tokenizer.readToken 2,23 21002 17
  23. 23. CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE java.lang.StringBuffer.append 2,13 30505 java.lang.Object.wait 28,19 6 alice.tuprolog.Var.occurCheck 4,88 531993 alice.tuprolog.Struct.getTerm 4,24 1453666 alice.tuprolog.Var.unify 3,82 546668 S. tree alice.tuprolog.Var.free 2,80 451853 java.util.ArrayList.<init> 2,57 732603 alice.tuprolog.Struct.unify 2,48 295052 alice.tuprolog.Var.getTerm 2,26 1506972 alice.tuprolog.Term.match 2,01 128442 alice.tuprolog.Var.unify 7,95 3632513 alice.tuprolog.Struct.unify 7,92 2719626 alice.tuprolog.Var.free 4,70 2522186 java.util.AbstractList$Itr.next 4,13 2415736 java.util.ArrayList.get 3,99 3694306 alice.tuprolog.Int.unify 3,42 1849876 alice.tuprolog.Struct.copy 3,39 1492772 S. square alice.tuprolog.Var.getTerm 3,22 6554696 java.lang.AbstractStringBuilder.append 3,14 1691076 java.util.AbstractList$Itr.hasNext 3,02 2821175 java.util.ArrayList.add 2,83 2475406 alice.tuprolog.Var.copy 2,77 1118445 java.util.IdentityHashMap.get 2,35 1118445 alice.tuprolog.Var.rename 2,18 561971 java.lang.StringBuffer.append 2,00 1677681 alice.tuprolog.Var.unify 5,76 3065460 alice.tuprolog.Var.free 3,74 2081312 java.util.ArrayList.<init> 3,25 3259113 java.util.AbstractList$Itr.hasNext 3,17 3194571 java.lang.AbstractStringBuilder.append 3,04 1947463 java.util.AbstractList$Itr.next 2,78 1823163 Tak java.util.ArrayList.get 2,65 2716581 alice.tuprolog.Var.getTerm 2,60 6292230 18
  24. 24. 3.2. TEMPI DI ESECUZIONE DEI METODI alice.tuprolog.Var.copy 2,47 1193928 java.util.AbstractList$Itr.<init> 2,35 3388196 java.util.ArrayList.add 2,23 2210364 alice.tuprolog.Var.rename 2,13 645368 alice.tuprolog.Int.unify 2,12 1355254 Tabella 3.3: Analisi dei metodi. Come si pu` notare, in tre delle nove teorie, il metodo che impiega pi` tempo a o u terminare ` java.lang.Object.wait. Questo metodo viene lanciato dalla JVM (nel e caso si tratti del client HotSpot della JVM e l’algoritmo usato sia seriale) e per- mette al garbage collector di ripulire parte dell’heap quando non ` pi` possibile eu promuovere oggetti dallo young generation all’old generation [4]. Una spiegazione pi` approfondita a riguardo viene data nel capitolo seguente. Per tutti gli altri u metodi risulta difficile fare una ragionevole analisi basata su di una singola teo- ria e, come detto nei capitoli precedenti, si deve ricercare un approccio che possa mettere in luce i miglioramenti in un ambito di utilizzo il pi` eterogeneo possibile. u Nella Tabella 3.4 vengono sommati i tempi di esecuzione relativi di tutti i metodi (anche quelli non inclusi nella Tabella 3.3). Nell’aggregare i risultati delle varie teorie ` stato scelto di sommare semplicemente i risultati parziali senza dare alcun e peso ai tempi di esecuzione delle teorie. Infatti, come si pu` vedere nell’analisi o della scalabilit`, modificando l’obiettivo di una teoria, in modo da allungare o ac- a corciare l’albero di ricerca di una soluzione, il tempo per ottenere una risposta, rispettivamente, cresce o diminuisce linearmente con il numero di foglie dell’albero visitate dalla macchina a stati finiti. Tuttavia i tempi di esecuzione relativi dei vari metodi rimangono costanti. Per questo motivo dare un peso a questi tem- pi significherebbe sbilanciare l’analisi complessiva a favore di una teoria anzich´ e un’altra. Per la stessa ragione nella Tabella 3.4 viene omesso il numero di chiamate ai metodi. Sempre dalla Tabella 3.4 si evince che il metodo java.lang.Object.wait ` ancora e quello che occupa la maggior parte del tempo di esecuzione del motore tuProlog. 19
  25. 25. CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE Nome metodo Tempo esec % java.lang.Object.wait 12,15 alice.tuprolog.Var.occurCheck 3,70 alice.tuprolog.Struct.getTerm 3,06 alice.tuprolog.Var.unify 3,05 java.lang.AbstractStringBuilder.append 2,82 alice.tuprolog.Var.free 2,61 alice.tuprolog.Struct.unify 2,25 java.util.AbstractList$Itr.next 1,88 java.util.AbstractList$Itr.hasNext 1,80 alice.tuprolog.Var.getTerm 1,72 java.util.ArrayList.get 1,72 java.lang.StringBuffer.append 1,67 java.util.ArrayList.<init> 1,49 java.util.ArrayList.add 1,37 java.io.StringReader.read 1,16 java.util.AbstractList$Itr.<init> 1,08 alice.tuprolog.Tokenizer.readNextToken 1,01 alice.tuprolog.Var.copy 1,01 Tabella 3.4: Analisi dei metodi totale. Eliminando, o limitando, il numero di chiamate ad esso comporterebbe un miglio- ramento del tempo di esecuzione di circa il 12% sul tempo totale5 . Purtroppo, escludendo il metodo Java gi` citato, non esistono situazioni in cui l’utilizzo di a una funzione ` predominante rispetto alle altre, anche se i metodi nella Tabel- e la 3.4 rappresentano circa la met` del tempo di esecuzione totale. Questo dato a indirizza sicuramente ad una pi` attenta analisi di questi metodi. u 3.3 Analisi della scalabilit` a In questa parte della dissertazione viene data una dimostrazione di come la relazione fra numero di stati visitati nell’albero di ricerca e numero di chiamate ai metodi utilizzati sia lineare. Per fare ci` ` opportuno prendere come teoria di rife- oe Questa ipotesi, se pur plausibile, non ` del tutto veritiera. A causa della lentezza del profiler e 5 HPROF non ` stato infatti possibile analizzare teorie di una certa complessit` computazionale e e a il valore di miglioramento potrebbe essere sottostimato. 20
  26. 26. ` 3.3. ANALISI DELLA SCALABILITA rimento la teoria Switch square che permette di ingrandire o rimpicciolire l’albero di ricerca solamente modificando l’obiettivo e calcolare anche il numero di stati in cui si deve transitare per trovare una soluzione. La Tabella 3.5 mostra per ogni obiettivo valutato il tempo di esecuzione della teoria6 e i cinque metodi con tempo di esecuzione pi` elevato con il relativo numero di chiamate per quel metodo. u N. stati e Tempo esec (ms) Nome metodo N. chiamate alice.tuprolog.Struct.unify 1947041 alice.tuprolog.Var.unify 2584249 3 + 6! 247837 alice.tuprolog.Var.free 1802505 java.util.ArrayList.get 2639270 java.util.AbstractList$Itr.next 1725777 alice.tuprolog.Struct.unify 3876572 alice.tuprolog.Var.unify 5181011 3 + 2 ∗ 6! 488623 alice.tuprolog.Var.free 3601750 java.util.AbstractList$Itr.next 3449996 java.util.ArrayList.get 5276156 java.lang.Object.wait 2 java.lang.ref.ReferenceQueue.remove 4 alice.tuprolog.Struct.unify 5813154 3 + 3 ∗ 6! 1898223 alice.tuprolog.Var.unify 7768221 alice.tuprolog.Var.free 5398379 java.util.AbstractList$Itr.next 5173233 java.util.ArrayList.get 7911406 Tabella 3.5: Analisi della scalabilit` del motore tuProlog. a La Tabella 3.5, oltre a mostrare la linearit` fra stati visitati e numero di chia- a mate ad un metodo, mostra anche come il tempo di esecuzione risulti lineare in rapporto all’aumentare dei nodi nell’albero di risoluzione. Questo dimostra una buona scalabilit` del sistema fino alla risoluzione dell’obiettivo con 3 + 2 ∗ 6! stati, a mentre le prestazioni degradano nella risoluzione dell’obiettivo con 3 + 3 ∗ 6! stati a causa dell’intervento del garbage collector. Si tenga in considerazione che il tempo di esecuzione ` pregiudicato dal profiler HPROF che e 6 rallenta l’esecuzione di tuProlog. Per questo motivo non deve essere confrontato con quello della Tabella 3.3, ma solamente con quello degli altri obiettivi. 21
  27. 27. CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE 3.4 Grafo delle chiamate Con lo scopo di analizzare pi` in dettaglio i singoli metodi con i tempi di esecu- u zione pi` elevati (si veda la Tabella 3.4) vengono esaminate le chiamate ad ognuno u dei metodi stessi. Dai risultati di questa analisi ` possibile comprendere se l’elevato e tempo di esecuzione di una funzione ` dovuto all’implementazione della funzione e stessa oppure all’uso troppo intenso della funzione da parte di altri metodi. Di conseguenza ` possibile orientare la riscrittura del codice in una delle due dire- e zioni. Per lo stesso principio che ha portato all’aggregazione dei risultati parziali delle varie teorie ed esposto durante la discussione sull’analisi della scalabilit`, lo a studio ` stato realizzato esaminando le chiamate complessivamente pi` utilizzate, e u senza dare peso ai tempi di esecuzione delle singole teorie. Per ogni singolo metodo presente nella Tabella 3.4 ` stato esplorato lo stack allo scopo di trovare quale fu e la prima funzione del package alice a generarne l’esecuzione. Questo accorgimento ` dovuto al fatto che solamente il codice dei metodi del package alice pu` essere e o riscritto, quindi le chiamate fra metodi java (senza tenere conto del fatto che sono da considerarsi gi` ottimizzate) non aggiungono informazione all’analisi7 . a Il risultato dello studio ` la Figura 3.1, la quale mostra per ogni metodo, e rappresentato come nodo nel grafo (si faccia riferimento alla Tabella 3.6 per cono- scere a quale metodo corrisponde ogni nodo), quali sono i suoi chiamanti attraverso una freccia entrante. Una freccia uscente ed entrante nello stesso nodo indica una chiamata ricorsiva e per ognuna delle frecce viene indicata in quale percentuale un determinato metodo viene eseguito per causa del chiamante sul totale delle chiamate8 . L’unico metodo, inserito nella Figura 3.1, il cui chiamante fa parte del package java ` e 7 java.lang.Object.wait. Vengono visualizzati i chiamanti con percentuali uguali o superiori al 5%. 8 22
  28. 28. 3.4. GRAFO DELLE CHIAMATE N. nodo Nome metodo 0 java.io.StringReader.read 1 alice.tuprolog.Var.unify 2 java.util.ArrayList.add 3 alice.tuprolog.Var.getTerm 4 alice.tuprolog.Struct.unify 5 alice.tuprolog.Var.occurCheck 6 alice.tuprolog.Var.copy 7 alice.tuprolog.Struct.getTerm 8 alice.tuprolog.Tokenizer.readNextToken 9 java.util.AbstractList$Itr.hasNext 10 alice.tuprolog.Var.free 11 java.lang.Object.wait 12 java.util.ArrayList.get 13 java.util.AbstractList$Itr.<init> 14 java.util.AbstractList$Itr.next 15 java.lang.StringBuffer.append 16 java.lang.AbstractStringBuilder.append 17 java.util.ArrayList.<init> Tabella 3.6: Associazione fra il numero identificativo di un nodo nella Figura 3.1 e il metodo che esso rappresenta. 23
  29. 29. CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE ClauseStore.deunify Int.unify 36 11 100 60 9 SubGoalTree.addChild 0 1 2 61 18 Int.unify 13 14 18 40 StateRuleSelection.doJob 25 7 Term.match 3 4 5 24 85 48 11 10 68 99 Tokenizer.readToken 99 Struct.copy 6 7 8 13 Term.match Struct.copy 9 75 14 100 71 java 15 StateBacktrack.dojob ClauseStore.deunify 7 9 10 11 Cl au se St or e.r eu n ify 18 6 50 75 12 ClauseStore.deunify java 15 5 SubGoalTree.getChild 13 ClauseStore.deunify 12 13 14 ClauseInfo.bodyCopy 6 12 9 7 8 ify py Te fy un r o ni m yC eu .re .m od Var.rename Var.rename e.d re at Term.unify b to ch o. or eS nf St us eI se us a au Cl a Cl Cl 42 6 45 7 11 27 Struct.toString Struct.<init> ClauseStore.deunify 17 PrimitiveManager.identify 15 16 17 SubGoalTree.<init> 15 Struct.toString 28 7 6 54 10 5 St Pr at im eR ch at $ iti Struct.<init> er er ul ve at or Op ag eS .m M et an el an m ec er rM r ag Te tio st to er n. gi era .id .g do en Re p J or O tif ob y at er Op Figura 3.1: Analisi delle chiamate ai metodi pi` utilizzati nell’esecuzione delle u teorie. 24
  30. 30. Capitolo 4 Analisi dell’utilizzo della memoria Le stesse teorie presentate nel Capitolo 2 vengono ora utilizzate nel profiling del motore con lo scopo di analizzare in quale modo tuProlog sfrutta la memoria heap. Ad una prima discussione sulle modalit` di studio dell’interprete segue la a presentazione dei dati ottenuti, ricavati con il proposito di mettere in evidenza quali sono gli oggetti la cui istanziazione alloca in modo pi` consistente questa u risorsa. A questo fine viene valutata la memoria complessivamente allocata du- rante la risoluzione di una teoria, senza considerare il fatto che il garbage collector libera lo spazio occupato da quegli oggetti non referenziati in un certo momento dell’esecuzione. Infine, come nel capitolo precedente, vengono analizzati quali so- no i metodi che sfruttano maggiormente la memoria, con lo scopo di rendere la riscrittura del codice del motore il pi` circoscritta e proficua possibile. u 4.1 Metodo di profiling Il profiling del motore tuProlog fornisce i dati riguardanti l’utilizzo della memo- ria ed in particolare, per permettere il tipo di analisi introdotta precedentemente, deve mettere a disposizione, per ogni oggetto, i dati relativi alla memoria com- plessiva da esso allocata e il metodo che alloca quel oggetto. Il profiler HPROF permette di ricavare queste informazioni attraverso il seguente comando: java -agentlib:hprof=heap=sites,depth=16 alice.tuprolog.Agent tr.pl 25
  31. 31. CAPITOLO 4. ANALISI DELL’UTILIZZO DELLA MEMORIA dove l’opzione hprof=heap=sites visualizza, per ogni metodo incluso in un certo trace dello stack e per ogni oggetto da esso istanziato, i seguenti dati: il tra- ce dello stack che ha generato la chiamata al metodo; la quantit` di memoria totale a istanziata da quel determinato trace insieme al numero totale di istanze create del- l’oggetto in questione. Si deve precisare che HPROF non fornisce la quantit` di a memoria totale occupata da un oggetto, ma bens` la quantit` di memoria occupata ı a da esso escludendo lo spazio utilizzato dalle istanze di oggetti generati al suo in- terno. In questo modo ` possibile affrontare un’analisi classe per classe, separando e ognuna di esse da parte del suo contenuto. Se si volesse studiare quanto una classe sfrutta complessivamente la memoria sarebbe sempre possibile farlo analizzando lo stack. L’opzione depth=16 serve invece ad aumentare la profondit` dello stack vi- a sualizzato in output. Dato che si vogliono studiare quali sono i metodi del package alice che causano l’istanziazione di un oggetto, anche indirettamente, ` ragione- e vole impostare una profondit` del trace uguale a sedici per intercettare tutte le a chiamate, se pur a scapito di un profiling pi` lento. Come spiegato nell’analisi dei u tempi di esecuzione la classe Agent accetta tr.pl come parametro (in questo caso ` il file di testo contente una delle nove teorie gi` presentate) e viene preferita alle e a altre modalit` di esecuzione di tuProlog in quanto permette di analizzare solo la a parte del motore che ha il compito di risolvere la teoria. 4.2 Allocazione degli oggetti in memoria L’allocazione degli oggetti, oltre ad incrementare la quantit` di memoria di cui a tuProlog deve disporre per risolvere una teoria, porta anche ad un aumento dei tempi di esecuzione. Questo avviene sia a causa del tempo necessario alla JVM a creare una nuova istanza di un oggetto in memoria, sia a causa del tempo neces- sario al garbage collector per ripulire lo spazio non referenziato da alcun oggetto. Inoltre l’uso improprio di questa risorsa pu` causare il lancio dell’errore OutOf- o MemoryError1 , interrompendo la risoluzione della teoria. La Tabella 4.1 ` stata costruita sommando le quantit` di memoria istanzia- e a Questo errore viene lanciato da parte della virtual machine nel momento in cui, a causa della 1 mancanza di spazio in memoria, non ` pi` possibile allocare un certo oggetto. eu 26
  32. 32. 4.2. ALLOCAZIONE DEGLI OGGETTI IN MEMORIA te dai vari trace per lo stesso oggetto durante l’esecuzione di tuProlog. In questo modo ` possibile valutare complessivamente quanto un determinato oggetto viene e istanziato. Inoltre vengono omessi quegli oggetti che occupano uno spazio in me- moria inferiore al 2% dello spazio totale allocato. Teoria Nome oggetto M. allocata % M. allocata Byte java.lang.Object[] 26,09 1631496 char[] 15,44 965728 java.util.ArrayList 8,54 534280 java.util.AbstractList$Itr 7,68 480040 alice.tuprolog.Struct 5,80 362712 Crypt java.lang.String 4,83 302128 alice.tuprolog.Term[] 4,37 273128 alice.tuprolog.Var 3,85 240728 byte[] 3,71 231960 java.lang.StringBuffer 2,32 145080 java.lang.Object[] 41,27 87078352 char[] 10,15 21423200 java.util.ArrayList 9,92 20935528 java.util.AbstractList$Itr 8,49 17917864 alice.tuprolog.Var 3,88 8191384 E.’s riddle alice.tuprolog.Struct 3,26 6870200 java.util.AbstractList$ListItr 3,22 6787896 java.lang.String 2,94 6207184 java.util.LinkedList$Entry 2,68 5652544 alice.tuprolog.Term[] 2,41 5087632 char[] 33,16 798736 java.util.LinkedList$Entry 13,36 321784 byte[] 12,04 290000 java.lang.String 6,47 155728 Poly int[] 4,02 96728 alice.tuprolog.Token 3,17 76312 27
  33. 33. CAPITOLO 4. ANALISI DELL’UTILIZZO DELLA MEMORIA java.util.regex.Matcher 2,36 56824 alice.tuprolog.Var 2,24 54008 java.lang.Object[] 23,12 649056 char[] 15,02 421856 byte[] 8,63 242384 java.util.ArrayList 6,81 191344 java.util.AbstractList$Itr 5,67 159112 Qsort java.lang.String 5,02 141088 alice.tuprolog.Struct 4,69 131768 alice.tuprolog.Var 3,96 111224 alice.tuprolog.Term[] 3,49 98064 java.util.LinkedList$Entry 2,08 58408 java.lang.Object[] 33,73 3742072 char[] 10,43 1156760 java.util.ArrayList 9,96 1104856 java.util.AbstractList$Itr 8,30 921016 alice.tuprolog.Struct 4,58 508408 java.util.AbstractList$ListItr 3,50 388536 Queens java.lang.String 3,02 335128 alice.tuprolog.Term[] 2,86 316936 java.util.LinkedList$Entry 2,66 294712 alice.tuprolog.Var 2,47 273880 alice.tuprolog.ExecutionContext 2,16 239552 byte[] 2,09 231960 java.lang.Object[] 26,57 647232 char[] 11,27 274544 byte[] 9,52 231960 java.util.ArrayList 9,49 231088 Query java.util.AbstractList$Itr 9,20 223984 java.lang.String 3,99 97192 java.util.LinkedList$Entry 3,95 96160 alice.tuprolog.Struct 2,87 70008 28
  34. 34. 4.2. ALLOCAZIONE DEGLI OGGETTI IN MEMORIA alice.util.OneWayList 2,39 58264 java.lang.Object[] 38,19 26544168 java.util.ArrayList 12,70 8831704 java.util.AbstractList$Itr 11,46 7964704 char[] 7,57 5262592 java.util.LinkedList$Entry 4,90 3404920 S. tree alice.tuprolog.Struct 3,05 2122008 alice.tuprolog.Var 2,82 1957752 alice.util.OneWayList 2,67 1856008 java.util.AbstractList$ListItr 2,43 1686392 java.lang.String 2,30 1595608 alice.tuprolog.Term[] 2,25 1563112 java.lang.Object[] 24,74 66566376 alice.tuprolog.Struct 17,78 47833088 char[] 16,87 45410744 alice.tuprolog.Term[] 12,56 33810088 S. square alice.tuprolog.Var 6,66 17926784 java.lang.String 5,05 13602072 java.util.ArrayList 3,78 10162824 java.util.AbstractList$Itr 3,54 9532368 java.lang.StringBuffer 3,32 8947632 java.lang.Object[] 32,60 130756896 char[] 12,92 51823792 java.util.ArrayList 9,75 39111280 java.util.AbstractList$Itr 8,21 32912632 alice.tuprolog.Struct 4,51 18097912 alice.tuprolog.Var 4,38 17576824 Tak java.lang.String 3,88 15560176 alice.tuprolog.Term[] 3,54 14217224 java.util.AbstractList$ListItr 2,57 10326264 java.lang.StringBuffer 2,57 10325736 java.util.LinkedList$Entry 2,32 9301336 29
  35. 35. CAPITOLO 4. ANALISI DELL’UTILIZZO DELLA MEMORIA alice.tuprolog.ExecutionContext 2,03 8132024 Tabella 4.1: Analisi degli oggetti utilizzati. Come si pu` osservare, in otto delle nove teorie, l’oggetto pi` utilizzato ` un o u e array di Object. Esso viene utilizzato principalmente nella classe ArrayList per mantenere un buffer nel quale viene memorizzata la lista in questione. Un altro oggetto che compare vistosamente nella tabella ` l’array char[], che viene utilizzato e prevalentemente per memorizzare delle stringhe nelle classi String e StringBuilder (o nella sua versione synchronized StringBuffer). Nel grafo delle allocazioni si pu` o vedere in modo pi` specifico quali sono i metodi che istanziano questi oggetti. u Nella Tabella 4.3 vengono mostrati quali sono gli oggetti complessivamente pi` utilizzati durante la risoluzione delle teorie. Per le stesse ragioni esposte nel- u l’analisi della scalabilit` le varie percentuali di utilizzo della memoria degli oggetti a vengono sommate dando lo stesso peso alle varie teorie, sebbene la quantit` di a memoria totale istanziata da una di esse possa essere diversa da quella istanziata dalle altre. A sostegno di questa tesi, nella Tabella 4.2, vengono elencati i sei og- getti che hanno fatto misurare le percentuali di utilizzo della memoria pi` elevate u durante la risoluzione della teoria Switch square. Le misurazioni sono state fatte per ognuno dei tre obiettivi utilizzati nell’analisi dei metodi, i quali permettono di cercare in 3 + 6!, in 3 + 2 ∗ 6! oppure in 3 + 3 ∗ 6! diversi stati prima di tro- vare la soluzione della teoria. Anche se la precisione dei risultati non rispecchia quella avuta nell’analisi dei tempi di esecuzione, si pu` comunque assumere, con o buona approssimazione, che al variare dell’obiettivo e del numero di stati visitati nell’albero di ricerca la percentuale di spazio allocato in memoria da ogni oggetto, rispetto allo spazio totale allocato, rimane costante. Come detto precedentemente, questo permette di analizzare l’utilizzo della memoria semplicemente sommando i dati contenuti nella Tabella 4.1. 30
  36. 36. 4.2. ALLOCAZIONE DEGLI OGGETTI IN MEMORIA N. stati Nome oggetto M. allocata % java.lang.Object[] 24,74 alice.tuprolog.Struct 17,78 char[] 16,87 3 + 6! alice.tuprolog.Term[] 12,56 alice.tuprolog.Var 6,66 java.lang.String 5,05 alice.tuprolog.Struct 23,23 char[] 17,80 alice.tuprolog.Term[] 17,31 3 + 2 ∗ 6! alice.tuprolog.Var 12,60 java.lang.String 10,97 java.lang.Object[] 8,87 alice.tuprolog.Struct 19,12 char[] 18,37 alice.tuprolog.Term[] 13,94 3 + 3 ∗ 6! java.lang.Object[] 9,97 java.lang.String 9,78 alice.tuprolog.Var 9,37 Tabella 4.2: Analisi dell’utilizzo della memoria da parte degli oggetti rispetto all’obiettivo della teoria. Infine, nella Tabella 4.3, vengono esposti i risultati di questa operazione omet- tendo gli oggetti che allocano meno dell’1% della memoria totale istanziata. L’a- nalisi conferma quanto detto precedentemente. Gli oggetti Object[] e char[] sono ancora i pi` utilizzati da parte di tuProlog occupando il 42,11% della memoria u totale impiegata. Inoltre, ricordando che il profiler HPROF non ingloba nella mi- sura della memoria allocata da un oggetto lo spazio occupato da oggetti innestati, ` possibile considerare questi due oggetti istanziati solamente dalle classi Array- e List, String e StringBuffer. Seguendo questa ipotesi le classi String e StringBuffer allocano complessivamente il 20,68% della memoria, mentre la classe ArrayList ne alloca il 35,30%. Sar` quindi importante, al momento di una futura implemen- a tezione del codice, considerare queste tre classi, le quali occupano nell’insieme il 55,98% della memoria totale. 31

×