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

56
ALMA MATER STUDIORUM - UNIVERSIT ` A DI BOLOGNA FACOLT ` A 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 Michele Damian Relatore Chiar.mo Prof. Enrico Denti Anno Accademico 2007/08

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

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

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 prestazionedell’interprete tuProlog

su piattaforma Java

CandidatoMichele Damian

RelatoreChiar.mo Prof. Enrico Denti

Anno Accademico 2007/08

Page 2: Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Page 3: Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi

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 scalabilita . . . . . . . . . . . . . . . . . . . . . . . . . 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

Page 4: Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Page 5: Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi

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 piu utilizzati nell’esecuzione delle

teorie. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24

4.1 Analisi delle istanziazioni degli oggetti piu allocati nell’esecuzione

delle teorie. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37

iii

Page 6: Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Page 7: Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi

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 scalabilita del motore tuProlog. . . . . . . . . . . . . . 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

Page 8: Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Page 9: Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi

Introduzione

Il motore tuProlog e un interprete per il linguaggio Prolog, scritto interamente

in Java e interoperabile con le librerie fornite dalla piattaforma di sviluppo JDK.

Giunto recentemente alla versione 2.1, ha portato con se diversi cambiamenti dal

punto di vista strutturale. L’obiettivo del lavoro e quello di analizzare le pre-

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, sara chiaro su quali parti del codice dovra orientare la sua attenzione

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 velocita, allo stato attuale, i dati a di-

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 pero non garantiscono tempi di esecuzione ab-

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 e quello di analizzare come

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 e strutturata in modo da rendere piu comprensibile possi-

bile il motivo delle scelte fatte durante il progetto. Il Capitolo 1 presenta il motore

vii

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

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,

nonche le sue funzionalita principali. Inoltre vengono presentate le teorie di test

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 e il loro significato. In particolare, nel Capitolo 3

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 piu recente versione di tuProlog (2.1) e la versione 1.3. Questo

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

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

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 e il

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 portabilita, ovvero la facilita con cui e possibile distribuire

l’applicazione, legata solamente alla presenza della piattaforma Java; la riusabi-

lita, che permette il riutilizzo di componenti in diversi sistemi; la modularita, che

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 riusabilita e la modularita dell’architet-

tura del motore tuProlog. Esse vengono rese possibili grazie alla separazione delle

responsabilita del sistema. Ogni responsabilita viene associata ad un singolo ma-

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

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

CAPITOLO 1. IL MOTORE TUPROLOG

modificate preservando il funzionamento del sistema. Queste caratteristiche sono

rese possibili grazie al pattern Facade [2], che riduce anche il numero di oggetti

che il cliente deve utilizzare, rendendo il sistema piu semplice da gestire. Sono

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 peculiarita, oltre

a rendere estremamente configurabile tuProlog, fa si che il core metta a disposi-

zione funzionalita essenziali e quindi dia migliori prestazioni in termini di tempi

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 piu importanti, escludendo i predicati di I/O; la libreria IO-

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 e neces-

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 fara

riferimento durante il processo di risoluzione e riconosce le direttive che devono

essere eseguite immediatamente. Per questioni di ottimizzazione il corpo di una

2

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

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, e di fondamentale importanza in quanto realizza il motore di riso-

luzione delle teorie. Inoltre, il manager del motore permette di disaccoppiare il

motore di risoluzione tuProlog dal resto dell’architettura favorendo l’estensibilita

della macchina a stati finiti, a cui diventa possibile aggiungere nuovi stati. Per

realizzare tutto cio viene usato il pattern State [2] che permette di istanziare og-

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. Piu dettagliatamente il motore comprende uno stato iniziale

(Init), quattro stati rappresentanti le attivita svolte durante il processo di riso-

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

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

CAPITOLO 1. IL MOTORE TUPROLOG

Figura 1.2: La macchina a stati finiti del motore tuProlog

Lo stato Init e il punto di partenza e ha il compito di inizializzare il motore

di risoluzione estraendo gli obiettivi da valutare dalla teoria e creando un ogget-

to che rappresenta il contesto nella quale l’obiettivo verra valutato. Dopodiche

il controllo verra passato allo stato Goal Selection il quale seleziona l’obiettivo

da valutare da una lista di obiettivi. Se questo obiettivo non viene trovato e il

processo di esecuzione e terminato si possono presentare due casi: se esiste un

punto di scelta e quindi la teoria puo avere un’altra soluzione, lo stato finale sara

TRUE CP, altrimenti lo stato finale sara TRUE. Se invece esiste un altro obiettivo

che puo essere estratto deve essere valutato nello stato Goal Evaluation, a meno

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 sara

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

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

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

Page 16: Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Page 17: Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi

Capitolo 2

Benchmarks

La scelta del benchmark e un passo importante nell’analisi delle prestazioni di

un software. Le funzionalita del benchmark, il modo in cui le prestazioni vengono

da esso misurate e il modo in cui i dati finali vengono rappresentati determinano

quali parti del sistema e possibile analizzare, come e possibile analizzarle attraverso

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 e diviso in due sezioni. La prima descrive brevemente i possibili

profiler adatti ad un’analisi di memoria e velocita di esecuzione di un’applicazione

Java. Vengono poi confrontati i vari strumenti ed esposte le ragioni che hanno por-

tato alla scelta dell’uno anziche dell’altro, soffermandosi sulle caratteristiche del

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

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

CAPITOLO 2. BENCHMARKS

2.1 Profilers

Il profiler e essenzialmente uno strumento in grado di misurare le performance

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 piu

adatto allo scopo e dettata non solo da quali variabili si debbano misurare durante

il profiling, ma anche dalla qualita e quantita delle informazioni che e possibile

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 qualita delle informazioni ricavate puo anche essere definita

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 e la

varianza, minore sara la qualita delle informazioni in possesso. Questo problema 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 se e possibile ovviare al problema esprimendo i

tempi di esecuzione dei vari metodi1 con un valore relativo al tempo di esecuzione

totale della teoria, anziche come valore assoluto. In questo modo si possono avere

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 quantita di memoria heap allocata e i tempi di esecuzione

1Si vedra piu avanti che viene assunto come ipotesi il fatto che il tempo di esecuzione totaledella teoria sia dato dalla somma del tempo di esecuzione dei singoli metodi e l’ottimizzazionedi uno di essi comporta l’ottimizzazione del tempo di esecuzione totale.

2Non e escluso che alcuni sistemi operativi, o alcune implementazioni JVM diverse da quelladella Sun Microsystems, presentino ottimizzazioni che potrebbero far variare le velocita di ese-cuzione relative dei vari metodi. Queste variazioni dovrebbero comunque essere ragionevolmentepiccole.

8

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

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 e dettata dal fatto che utiliz-

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 piu consono e, come detto in precedenza, la possibilita di misurare i tempi

di esecuzione e la quantita di memoria allocata dai vari metodi utilizzati durante

la risoluzione di una teoria. Inoltre, osservando l’architettura della macchina a

stati finiti, si puo facilmente intuire che alcuni degli stati in cui il sistema transita

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 quantita di memoria, perche effettua un numero elevato

di chiamate a questo o perche esso e computazionalmente complesso.

Una buona parte dei profiler con le caratteristiche indicate e basata sull’in-

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 puo invocare chiamate o registrarsi ad eventi,

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 puo sembrare un difetto, o una mancanza di qualita, e in realta un pregio.

Gli altri profiler infatti, presentando un output piu o meno elaborato, tendono a

mettere in risalto un aspetto del sistema anziche un altro, perdendo informazione.

Con HPROF e possibile invece elaborare i dati grezzi, in modo tale da mettere in

risalto l’aspetto desiderato.

3Per software non commerciale si intende shareware o open source.

9

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

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 e la classe alice.tuprolog.Agent con, come primo

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 e stato chiamato da un altro metodo e il trace dello stack

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, nonche la memoria allocata e il numero di oggetti allocati durante l’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, e quello di non poter sfruttare questo procedimento

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 piu approfonditamente nei Capitoli 3 e 4.

10

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

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 percio devono essere scelte con cura, in modo tale da permettere

un’analisi il piu completa possibile. In questa dissertazione vengono presentati

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 e anche rivolta alla scalabilita del sistema e alcune delle teorie uti-

lizzate sono scritte in modo tale da permettere vari test sulla scalabilita senza la

necessita di pesanti modifiche alla teoria stessa o l’introduzione di nuove teorie.

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 velocita di risoluzione delle

teorie. Per mantenere una certa continuita e per poter confrontare i risultati del

lavoro svolto in passato si e deciso di mantenere, per quanto possibile, alcune delle

teorie gia utilizzate. Si noti comunque che i sistemi hardware e software su cui

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 gia state usate come benchmark, mentre le altre sono state introdotte so-

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

4Il sistema su cui sono stati ricavati i risultati antecedenti a quelli qui descritti era equipaggiatocon un processore Pentium III 800 MHz, 256 MB di RAM su Windows 2000 e J2SE 1.5.0 09. Ilsistema attuale consiste in un processore Pentium IV 2.6 GHz, con 1 GB di RAM su WindowsXP Home Edition e J2SE 1.6.0 04.

11

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

CAPITOLO 2. BENCHMARKS

modo e possibile incrementare o diminuire il lavoro svolto dal motore tuProlog

senza modificare la teoria in se, consentendo di analizzare la scalabilita del siste-

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 e la versione estesa a nove

regine del piu famoso problema delle 8 regine [1]; Query interroga un database

contenente per ogni nazione la rispettiva dimensione e il numero di cittadini e

restituisce, conoscendo gia il nome di una nazione, quella con la densita piu simile

ad essa; Einstein’s riddle tenta di risolvere un indovinello nel quale viene chiesto

chi e il proprietario di un certo animale data una serie di indizi riguardo a cinque

persone di diversa nazionalita, residenti in case di colore diverso e con una distinta

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

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

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 cio viene prima messa alla prova la scalabilita del sistema,

facendo il profiling con teorie via via piu complesse ed in secondo luogo vengono

costruiti i grafi delle chiamate ai metodi, in modo da evidenziare quali metodi

effettuano le chiamate piu costose da un punto di vista computazionale.

3.1 Metodo di profiling

Come gia detto, i dati risultanti dal profiling devono fornire il tempo di ese-

cuzione totale della teoria, il tempo di esecuzione dei metodi e, indirettamente,

per ognuno di essi, i tempi di esecuzione dei metodi innestati. Cio e possibile in

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 e stato chiamato. Il comando utilizzato per fare cio e il seguente

java -agentlib:hprof=cpu=times alice.tuprolog.Agent teoria.pl

13

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

CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE

dove la classe Agent, contenuta nel package alice.tuprolog, e uno strumento che

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

pero di isolare il motore tuProlog, in modo da non dover estromettere dal profiling

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 e equivalente a misurare undici volte il tempo di

esecuzione dell’obiettivo piu una volta il tempo di lettura della teoria per poi sot-

trarlo. E inoltre necessario evitare di misurare il tempo di creazione della JVM

e per fare cio viene scritta un’ulteriore teoria in cui viene usato il metodo Ja-

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 e contenuta la

teoria da valutare. All’interno di questo file sono contenute le seguenti clausole, le

quali permettono di valutare il tempo di lettura ed esecuzione:

1I tempi in cui questi processi avvengono sono dovuti alla JVM e non al motore tuProlog inse.

2Per diminuire la dispersione delle misure e stato misurato dodici volte il tempo di esecuzionedell’obiettivo e sottratto il valore massimo e minimo trovato. Il tempo di esecuzione e la mediadei valori rimanenti.

14

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

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 granularita del valore dipende dal sistema in cui e ospitata

la JVM e potrebbe essere maggiore di 1 ms. Nel sistema in cui sono avvenute le

misure non e stato possibile misurare intervalli di tempo minori a 15 ms, il che

fa pensare che lo stesso valore corrisponda alla granularita del sistema. Questo

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,75Einstein’s riddle 114598 2,62Poly 31 9677,42Qsort 671 447,09Queens 2574 116,55Query 312 961,54Spanning tree 18034 16,64Switch square 93787 3,20Tak 105721 2,84

Tabella 3.1: Tempi di esecuzione per cento risoluzioni e errore relativo calcolatoper 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

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

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 modalita di esecuzione ha

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,6Spanning tree 2216 1868 84,3Switch square 10110 9628 95,2Tak 11373 10797 94,9

Tabella 3.2: Tempi di lettura ed esecuzione.

Si puo quindi affermare che il tempo di lettura risulta essere all’incirca costante al

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 e stato fatto fin d’ora, dato che

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 e superiore o uguale

al 2% del tempo di esecuzione totale.

3Nel caso di Spanning tree, la teoria piu lunga, il numero di righe che il motore deve leggeree 186, mentre nel caso di Tak, quella piu corta, il numero di righe e 29.

4Il tempo impiegato ad un metodo per terminare non comprende il tempo impiegato ai metodiinnestati in esso ad essere eseguiti.

16

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

3.2. TEMPI DI ESECUZIONE DEI METODI

Teoria Nome metodo Tempo esec % N. chiamate

Cryptjava.lang.Object.wait 41,65 2

java.lang.AbstractStringBuilder.append 2,27 60583

E.’s riddle

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

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

Poly

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

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

Qsort

alice.tuprolog.Var.occurCheck 3,65 24612

java.lang.AbstractStringBuilder.append 3,58 37404

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

Query

java.lang.AbstractStringBuilder.append 3,9 34855

java.io.StringReader.read 2,73 24285

alice.tuprolog.Tokenizer.readNextToken 2,29 8534

alice.tuprolog.Tokenizer.readToken 2,23 21002

17

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

CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE

java.lang.StringBuffer.append 2,13 30505

S. tree

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

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

S. square

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

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

Tak

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

java.util.ArrayList.get 2,65 2716581

alice.tuprolog.Var.getTerm 2,60 6292230

18

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

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 puo notare, in tre delle nove teorie, il metodo che impiega piu tempo a

terminare e java.lang.Object.wait. Questo metodo viene lanciato dalla JVM (nel

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 e piu possibile

promuovere oggetti dallo young generation all’old generation [4]. Una spiegazione

piu approfondita a riguardo viene data nel capitolo seguente. Per tutti gli altri

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 piu eterogeneo possibile.

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 e stato scelto di sommare semplicemente i risultati parziali senza dare alcun

peso ai tempi di esecuzione delle teorie. Infatti, come si puo vedere nell’analisi

della scalabilita, modificando l’obiettivo di una teoria, in modo da allungare o ac-

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 anziche

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 e ancora

quello che occupa la maggior parte del tempo di esecuzione del motore tuProlog.

19

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

CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE

Nome metodo Tempo esec %java.lang.Object.wait 12,15alice.tuprolog.Var.occurCheck 3,70alice.tuprolog.Struct.getTerm 3,06alice.tuprolog.Var.unify 3,05java.lang.AbstractStringBuilder.append 2,82alice.tuprolog.Var.free 2,61alice.tuprolog.Struct.unify 2,25java.util.AbstractList$Itr.next 1,88java.util.AbstractList$Itr.hasNext 1,80alice.tuprolog.Var.getTerm 1,72java.util.ArrayList.get 1,72java.lang.StringBuffer.append 1,67java.util.ArrayList.<init> 1,49java.util.ArrayList.add 1,37java.io.StringReader.read 1,16java.util.AbstractList$Itr.<init> 1,08alice.tuprolog.Tokenizer.readNextToken 1,01alice.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 gia citato, non esistono situazioni in cui l’utilizzo di

una funzione e predominante rispetto alle altre, anche se i metodi nella Tabel-

la 3.4 rappresentano circa la meta del tempo di esecuzione totale. Questo dato

indirizza sicuramente ad una piu attenta analisi di questi metodi.

3.3 Analisi della scalabilita

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 cio e opportuno prendere come teoria di rife-

5Questa ipotesi, se pur plausibile, non e del tutto veritiera. A causa della lentezza del profilerHPROF non e stato infatti possibile analizzare teorie di una certa complessita computazionale eil valore di miglioramento potrebbe essere sottostimato.

20

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

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 piu elevato con il relativo numero di chiamate per quel metodo.

N. stati e Tempo esec (ms) Nome metodo N. chiamate

3 + 6! 247837

alice.tuprolog.Struct.unify 1947041alice.tuprolog.Var.unify 2584249alice.tuprolog.Var.free 1802505java.util.ArrayList.get 2639270java.util.AbstractList$Itr.next 1725777

3 + 2 ∗ 6! 488623

alice.tuprolog.Struct.unify 3876572alice.tuprolog.Var.unify 5181011alice.tuprolog.Var.free 3601750java.util.AbstractList$Itr.next 3449996java.util.ArrayList.get 5276156

3 + 3 ∗ 6! 1898223

java.lang.Object.wait 2java.lang.ref.ReferenceQueue.remove 4alice.tuprolog.Struct.unify 5813154alice.tuprolog.Var.unify 7768221alice.tuprolog.Var.free 5398379java.util.AbstractList$Itr.next 5173233java.util.ArrayList.get 7911406

Tabella 3.5: Analisi della scalabilita del motore tuProlog.

La Tabella 3.5, oltre a mostrare la linearita fra stati visitati e numero di chia-

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 scalabilita del sistema fino alla risoluzione dell’obiettivo con 3 + 2 ∗ 6! stati,

mentre le prestazioni degradano nella risoluzione dell’obiettivo con 3 + 3 ∗ 6! stati

a causa dell’intervento del garbage collector.

6Si tenga in considerazione che il tempo di esecuzione e pregiudicato dal profiler HPROF cherallenta l’esecuzione di tuProlog. Per questo motivo non deve essere confrontato con quello dellaTabella 3.3, ma solamente con quello degli altri obiettivi.

21

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

CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE

3.4 Grafo delle chiamate

Con lo scopo di analizzare piu in dettaglio i singoli metodi con i tempi di esecu-

zione piu elevati (si veda la Tabella 3.4) vengono esaminate le chiamate ad ognuno

dei metodi stessi. Dai risultati di questa analisi e possibile comprendere se l’elevato

tempo di esecuzione di una funzione e dovuto all’implementazione della funzione

stessa oppure all’uso troppo intenso della funzione da parte di altri metodi. Di

conseguenza e possibile orientare la riscrittura del codice in una delle due dire-

zioni. Per lo stesso principio che ha portato all’aggregazione dei risultati parziali

delle varie teorie ed esposto durante la discussione sull’analisi della scalabilita, lo

studio e stato realizzato esaminando le chiamate complessivamente piu utilizzate,

senza dare peso ai tempi di esecuzione delle singole teorie. Per ogni singolo metodo

presente nella Tabella 3.4 e stato esplorato lo stack allo scopo di trovare quale fu

la prima funzione del package alice a generarne l’esecuzione. Questo accorgimento

e dovuto al fatto che solamente il codice dei metodi del package alice puo essere

riscritto, quindi le chiamate fra metodi java (senza tenere conto del fatto che sono

da considerarsi gia ottimizzate) non aggiungono informazione all’analisi7.

Il risultato dello studio e la Figura 3.1, la quale mostra per ogni metodo,

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.

7L’unico metodo, inserito nella Figura 3.1, il cui chiamante fa parte del package java ejava.lang.Object.wait.

8Vengono visualizzati i chiamanti con percentuali uguali o superiori al 5%.

22

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

3.4. GRAFO DELLE CHIAMATE

N. nodo Nome metodo0 java.io.StringReader.read1 alice.tuprolog.Var.unify2 java.util.ArrayList.add3 alice.tuprolog.Var.getTerm4 alice.tuprolog.Struct.unify5 alice.tuprolog.Var.occurCheck6 alice.tuprolog.Var.copy7 alice.tuprolog.Struct.getTerm8 alice.tuprolog.Tokenizer.readNextToken9 java.util.AbstractList$Itr.hasNext

10 alice.tuprolog.Var.free11 java.lang.Object.wait12 java.util.ArrayList.get13 java.util.AbstractList$Itr.<init>14 java.util.AbstractList$Itr.next15 java.lang.StringBuffer.append16 java.lang.AbstractStringBuilder.append17 java.util.ArrayList.<init>

Tabella 3.6: Associazione fra il numero identificativo di un nodo nella Figura 3.1e il metodo che esso rappresenta.

23

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

CAPITOLO 3. ANALISI DEI TEMPI DI ESECUZIONE

0100

218

60

11

9

Cla

use

Sto

re.d

eun

ify

SubGoalTree.addChild1

36

Int.u

nify

61

34013

24

11

Int.u

nify

448

7 25

18

Term.matchStateRuleSelection.doJob5

14

85

699Struct.copy

713

10 68

899 Tokenizer.readToken

971

9

15ClauseStore.deunify

Stru

ct.c

op

y

10

75

714

Term

.mat

ch

StateBacktrack.dojob11

100 java

1250

68

13

18

ClauseInfo.bodyCopy

SubGoalTree.getChild

ClauseSto

re.d

eunify

ClauseStore.reunify

13

6

12

15

7

5ClauseStore.deunify

ClauseSto

re.re

unifyTerm

.match

java

1475

12

9

ClauseIn

fo.b

odyCopy

ClauseStore.deunify

15

45

27 7

10

Var.r

enam

e

Struct.toString PrimitiveManager.identify

Stru

ct.<

init

>

16

Var.r

enam

e

42

Struct.<init>11

Struct.toString 28

5

Operato

rManager$

Operato

rRegist

er.getO

perato

r

7Prim

itiveManager.identify

17

Term

.matc

h

54

SubGoalTree.<init>

Term

.un

ify

6

15

17

6

ClauseStore.deunify

StateRuleSelection.doJob

Figura 3.1: Analisi delle chiamate ai metodi piu utilizzati nell’esecuzione delleteorie.

24

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

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 modalita di studio dell’interprete segue la

presentazione dei dati ottenuti, ricavati con il proposito di mettere in evidenza

quali sono gli oggetti la cui istanziazione alloca in modo piu consistente questa

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 piu circoscritta e proficua possibile.

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

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

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 quantita di memoria totale

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 quantita di

memoria totale occupata da un oggetto, ma bensı la quantita di memoria occupata

da esso escludendo lo spazio utilizzato dalle istanze di oggetti generati al suo in-

terno. In questo modo e possibile affrontare un’analisi classe per classe, separando

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 profondita dello stack vi-

sualizzato in output. Dato che si vogliono studiare quali sono i metodi del package

alice che causano l’istanziazione di un oggetto, anche indirettamente, e ragione-

vole impostare una profondita del trace uguale a sedici per intercettare tutte le

chiamate, se pur a scapito di un profiling piu lento. Come spiegato nell’analisi dei

tempi di esecuzione la classe Agent accetta tr.pl come parametro (in questo caso

e il file di testo contente una delle nove teorie gia presentate) e viene preferita alle

altre modalita di esecuzione di tuProlog in quanto permette di analizzare solo la

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 quantita di memoria di cui

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 puo causare il lancio dell’errore OutOf-

MemoryError1, interrompendo la risoluzione della teoria.

La Tabella 4.1 e stata costruita sommando le quantita di memoria istanzia-

1Questo errore viene lanciato da parte della virtual machine nel momento in cui, a causa dellamancanza di spazio in memoria, non e piu possibile allocare un certo oggetto.

26

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

4.2. ALLOCAZIONE DEGLI OGGETTI IN MEMORIA

te dai vari trace per lo stesso oggetto durante l’esecuzione di tuProlog. In questo

modo e possibile valutare complessivamente quanto un determinato oggetto viene

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

Crypt

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

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

E.’s riddle

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

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

Poly

char[] 33,16 798736

java.util.LinkedList$Entry 13,36 321784

byte[] 12,04 290000

java.lang.String 6,47 155728

int[] 4,02 96728

alice.tuprolog.Token 3,17 76312

27

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

CAPITOLO 4. ANALISI DELL’UTILIZZO DELLA MEMORIA

java.util.regex.Matcher 2,36 56824

alice.tuprolog.Var 2,24 54008

Qsort

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

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

Queens

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

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

Query

java.lang.Object[] 26,57 647232

char[] 11,27 274544

byte[] 9,52 231960

java.util.ArrayList 9,49 231088

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

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

4.2. ALLOCAZIONE DEGLI OGGETTI IN MEMORIA

alice.util.OneWayList 2,39 58264

S. tree

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

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

S. square

java.lang.Object[] 24,74 66566376

alice.tuprolog.Struct 17,78 47833088

char[] 16,87 45410744

alice.tuprolog.Term[] 12,56 33810088

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

Tak

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

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

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

CAPITOLO 4. ANALISI DELL’UTILIZZO DELLA MEMORIA

alice.tuprolog.ExecutionContext 2,03 8132024

Tabella 4.1: Analisi degli oggetti utilizzati.

Come si puo osservare, in otto delle nove teorie, l’oggetto piu utilizzato e un

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 e l’array char[], che viene utilizzato

prevalentemente per memorizzare delle stringhe nelle classi String e StringBuilder

(o nella sua versione synchronized StringBuffer). Nel grafo delle allocazioni si puo

vedere in modo piu specifico quali sono i metodi che istanziano questi oggetti.

Nella Tabella 4.3 vengono mostrati quali sono gli oggetti complessivamente

piu utilizzati durante la risoluzione delle teorie. Per le stesse ragioni esposte nel-

l’analisi della scalabilita le varie percentuali di utilizzo della memoria degli oggetti

vengono sommate dando lo stesso peso alle varie teorie, sebbene la quantita di

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 piu elevate

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 puo comunque assumere, con

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

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

4.2. ALLOCAZIONE DEGLI OGGETTI IN MEMORIA

N. stati Nome oggetto M. allocata %

3 + 6!

java.lang.Object[] 24,74alice.tuprolog.Struct 17,78char[] 16,87alice.tuprolog.Term[] 12,56alice.tuprolog.Var 6,66java.lang.String 5,05

3 + 2 ∗ 6!

alice.tuprolog.Struct 23,23char[] 17,80alice.tuprolog.Term[] 17,31alice.tuprolog.Var 12,60java.lang.String 10,97java.lang.Object[] 8,87

3 + 3 ∗ 6!

alice.tuprolog.Struct 19,12char[] 18,37alice.tuprolog.Term[] 13,94java.lang.Object[] 9,97java.lang.String 9,78alice.tuprolog.Var 9,37

Tabella 4.2: Analisi dell’utilizzo della memoria da parte degli oggetti rispettoall’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 piu utilizzati da parte di tuProlog occupando il 42,11% della memoria

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,

e possibile considerare questi due oggetti istanziati solamente dalle classi Array-

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%. Sara quindi importante, al momento di una futura implemen-

tezione del codice, considerare queste tre classi, le quali occupano nell’insieme il

55,98% della memoria totale.

31

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

CAPITOLO 4. ANALISI DELL’UTILIZZO DELLA MEMORIA

Nome oggetto M. allocata %java.lang.Object[] 27,40char[] 14,71java.util.ArrayList 7,90java.util.AbstractList$Itr 6,95alice.tuprolog.Struct 5,37java.lang.String 4,15byte[] 4,05java.util.LinkedList$Entry 3,82alice.tuprolog.Term[] 3,82alice.tuprolog.Var 3,50java.util.AbstractList$ListItr 1,94java.lang.StringBuffer 1,82alice.util.OneWayList 1,43alice.tuprolog.ExecutionContext 1,35

Tabella 4.3: Analisi complessiva degli oggetti utilizzati.

4.3 Analisi del garbage collector

Come si e gia accennato precedentemente in questo capitolo e nell’analisi della

scalabilita il garbage collector influisce, anche pesantemente, sul tempo di esecu-

zione del motore tuProlog. Di seguito si espone piu in dettaglio quali sono le cause

del degrado delle prestazioni eseguendo alcuni test tramite le teorie Spanning tree

e Switch square, le quali permettono di istanziare un numero elevato di oggetti in

memoria e, di conseguenza, causare frequentemente l’intervento del garbage col-

lector.

Il sistema su cui viene studiato tuProlog viene definito dalla Sun Microsystems

un client-class machine [4]. Questo implica che il garbage collector utilizzato sia di

tipo seriale, la dimensione iniziale dell’heap disponibile sia di 4 MB e la sua dimen-

sione massima possa essere di 64 MB2. Il garbage collector seriale viene eseguito

su di una singola cpu, bloccando l’esecuzione dell’applicazione per tutto il tempo

necessario a marcare gli oggetti non referenziati e liberare la memoria. Al contra-

2La dimensione dell’heap allocabile dalla JVM viene incrementata ogni qualvolta non ci siapiu spazio per allocare un determinato oggetto. Nel caso la dimensione sia gia quella massimaprevista l’allocazione non e possibile e viene lanciato l’errore OutOfMemoryError.

32

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

4.3. ANALISI DEL GARBAGE COLLECTOR

rio il garbage collector parallelo utilizza piu cpu per eseguire la stessa operazione

riducendo il tempo di esecuzione. Questa seconda tipologia e particolarmente utile

quando la memoria da deallocare e piuttosto elevata3 ed, in seguito, il confronto

fra i due garbage collector viene utilizzato per stimare quanto la deallocazione

degli oggetti non referenziati incide sui tempi di esecuzione di tuProlog. Inoltre,

il garbage collector seriale utilizza a sua volta due differenti algoritmi di default:

il copying collector per lo young generation e il mark-compact collector per l’old

generation4 [3,4]. Entrambi gli algoritmi agiscono dopo aver bloccato l’esecuzione

dell’applicazione ma, mentre il primo deve agire su di una parte del heap di dimen-

sioni contenute, il secondo agisce su di una porzione piu vasta, causando un au-

mento consistente del tempo di esecuzione. In dettaglio l’algoritmo mark-compact

agisce in due fasi: nella prima marca tutti gli oggetti raggiungibili partendo dalla

radice; nella seconda gli oggetti marcati vengono compattati all’inizio del heap pre-

servandone l’ordine. Questa ultima fase, sebbene richieda un ulteriore intervallo

di tempo per essere completata, assicura un tempo d’allocazione del heap minore.

Inoltre, per garantire la consistenza degli oggetti marcati nell’old generation, l’e-

secuzione del programma deve essere interrotta in ambedue le fasi. In particolar

modo il degrado delle prestazioni di tuProlog avviene a causa di due metodi. Il

metodo Object.wait della classe java.lang.ref.Reference$ReferenceHandler5, dove

Object e un oggetto usato dal garbage collector per bloccare l’esecuzione del pro-

gramma mentre questo ultimo identifica gli oggetti che hanno cambiato stato. E

il metodo java.lang.ref.ReferenceQueue.remove, il quale viene chiamato dal meto-

do java.lang.ref.Finalizer$FinalizerThread.run quando il garbage collector rimuove

dal heap quegli oggetti che implementano il metodo finalize [5]. Nonostante questo

metodo sia altrettanto dispendioso, come si puo vedere dalla Tabella 3.3, a dif-

ferenza del metodo java.lang.ref.Reference$ReferenceHandler, non viene eseguito

altrettanto frequentemente.

3Si noti che la creazione di thread da parte del garbage collector introduce overhead.4Insieme alla permanent generation, la old generation e la young generation sono le tre sezioni

in cui viene divisa la memoria heap. Ognuna di queste ha il compito di mantenere oggetti didiverse eta (escludendo la permanent generation nel quale vengono allocati oggetti vitali perl’esecuzione dell’applicazione) in modo da rendere piu efficiente il lavoro del garbage collector.

5Essa estende java.lang.Thread ed esegue il suo codice nel metodo chiamato run chesovrascrive quello ereditato dalla classe madre.

33

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

CAPITOLO 4. ANALISI DELL’UTILIZZO DELLA MEMORIA

Con l’obiettivo di causare una o piu volte l’intervento del garbage collector

sono state eseguite le teorie Spanning tree e Switch square. In particolar modo la

Spanning tree e stata eseguita con l’intenzione di trovare uno spanning tree per un

albero formato da 64 nodi, mentre Switch square e stata eseguita con l’obiettivo di

visitare 1+2∗8! stati prima di risolvere la teoria. tuProlog e stato successivamente

eseguito attraverso il comando

java -XX:+PrintGCTimeStamps alice.tuprolog.Agent teoria.pl

dove -XX:+PrintGCTimeStamps e un’opzione del HotSpot6 della JVM che, per

ogni garbage collection, stampa: la memoria allocata prima e dopo il collecting;

dove il collecting e stato effettuato (nello young heap, nell’old heap o entrambi);

il tempo impiegato per essere eseguito. Al fine di valutare quanto l’utilizzo del

garbage collector della client-class machine incida sui tempi di esecuzione i risultati

vengono ora confrontati con quelli ottenuti eseguendo le stesse teorie su di una

server-class machine. Il comando utilizzato per eseguire il server HotSpot e

java -server -XX:+PrintGCTimeStamps alice.tuprolog.Agent teoria.pl

Sul sistema7 su cui vengono eseguiti i test la memoria heap minima a dispo-

sizione della JVM e di 48 MB, mentre quella massima e di 768 MB. Il garbage

collector di default e di tipo parallelo [4]. La Tabella 4.4 riassume, per ognuna delle

teorie, i risultati ottenuti per i due tipi di garbage collector, assieme al numero di

collecting dello young generation, dell’old generation e i tempi di esecuzione.

Dalla Tabella 4.4 si puo notare che la differenza fra i tempi di esecuzione dei

due tipi di JVM HotSpot risulta in poco piu di 800 ms per ogni garbage collecting

dell’old generation. La Sun Microsystems stima in meno di 500 ms il tempo di

esecuzione del garbage collector seriale sull’intero heap nel caso peggiore [4].

6HotSpot e il nome della java virtual machine implementata dalla Sun Microsystems. Esso,oltre a realizzare le funzionalita di base della JVM, provvede due diversi tipi di implementazionecon diverse ottimizzazioni: un HotSpot chiamato client e l’altro chiamato server.

7Il sistema e differente da quello utilizzato nell’analisi dei tempi di esecuzione e dell’utilizzodella memoria. In questo caso consiste in un processore Intel Core 2 Duo 2,4 GHz, con 3 GB diRAM su Windows Vista Home Premium.

34

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

4.4. GRAFO DELLE ALLOCAZIONI

Nome teoria HotSpot N. Young gc N. Old gc Tempo (ms)

Spanning treeclient 4910 5 10599server 105 0 6393

Switch squareclient 23753 49 75285server 609 0 35573

Tabella 4.4: Analisi del garbage collector.

4.4 Grafo delle allocazioni

In modo simile a quanto fatto nell’analisi dei tempi di esecuzione viene creato

un grafo raffigurante i metodi che causano l’allocazione degli oggetti maggiormente

istanziati in memoria. L’obiettivo di questo studio e quindi quello di mettere alla

luce quali sono i metodi che fanno uso della memoria in modo piu consistente e

che, causando l’intervento del garbage collector, provocano un aumento dei tempi

di esecuzione. Con l’intenzione di valutare, non solamente gli oggetti attivi, ma

tutta la memoria allocata durante l’esecuzione delle teorie, vengono analizzati gli

oggetti presenti nella Tabella 4.3. In particolare per ognuno di questi oggetti viene

attraversato lo stack, nel tentativo di trovare qual e il primo metodo del packa-

ge alice nell’albero delle chiamate ad aver causato l’allocazione di quel oggetto8.

Per ognuno dei metodi trovati viene successivamente considerata la quantita di

memoria che essi hanno allocato per l’oggetto valutato. Questo valore viene suc-

cessivamente trasformato in percentuale sulla memoria complessivamente allocata

per il medesimo oggetto.

Il risultato viene esposto nella Figura 4.1 la quale mostra per ogni oggetto

(per conoscere l’oggetto rappresentato da un certo nodo si veda la Tabella 4.5) i

metodi che allocano una percentuale uguale o maggiore al 5% della memoria to-

tale allocata dallo stesso oggetto. I nodi le cui frecce provengono da altri oggetti

indicano che quel nodo e stato istanziato dal costruttore dell’oggetto o, nel caso

l’oggetto sia un array, dal costruttore di una delle istanze dell’array.

8Ovviamente vengono escluse dall’analisi le istanziazioni da parte di metodi rilasciati insiemealla piattaforma java in quanto questi non sono implementabili.

35

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

CAPITOLO 4. ANALISI DELL’UTILIZZO DELLA MEMORIA

N. nodo Nome oggetto0 java.lang.Object[]1 char[]2 java.util.AbstractList$Itr3 java.lang.String4 java.lang.StringBuffer5 java.util.AbstractList$ListItr6 alice.tuprolog.Struct7 alice.tuprolog.Term[]8 alice.tuprolog.Var9 java.util.ArrayList

10 alice.tuprolog.ExecutionContext11 java.util.LinkedList$Entry12 alice.util.OneWayList13 byte[]

Tabella 4.5: Associazione fra il numero identificativo di un nodo nella Figura 4.1e l’oggetto che esso rappresenta.

36

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

4.4. GRAFO DELLE ALLOCAZIONI

0Var.unify5

30

23

14

ClauseInfo.performCopy

Term

.mat

chC

lau

seSt

ore

.deu

nify

2

21

61

7

9

Cla

use

Sto

re.d

eun

ify

Var.free

Term

.un

ify

SubGoalTree.iterator

1

Var.r

enam

e

98

397Var.rename

499Var.rename

5ClauseStore.reunify 99

699

799

899

9

50

18

167

7

Term

.mat

ch

ClauseStore.deunify

SubGoalTree.<init>

Term

.unify

StateRuleSelection.doJob10

99

11ClauseDatabase.getPredicates 98

12

99

13StateRuleSelection.doJob

PrimitiveInfo.evalAsPredicate

5

10

Eng

ineM

anag

er.s

olv

e

25

Tokenizer.<init> 16

Libra

ryM

anager.loadLib

rary

27

Figura 4.1: Analisi delle istanziazioni degli oggetti piu allocati nell’esecuzione delleteorie.

37

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

CAPITOLO 4. ANALISI DELL’UTILIZZO DELLA MEMORIA

E facile notare dalla Figura 4.1 che il metodo alice.tuprolog.Var.rename compa-

re come metodo predominante nelle istanziazioni delle classi String, StringBuffer

e char[] causando, come detto in precedenza, l’allocazione del 20,68% della me-

moria totale. Inoltre questo metodo compare in uno dei costruttori della classe

alice.tuprolog.Var, la quale e fra le classi piu utilizzate. Questo porta ad un utilizzo

complessivo della memoria da parte di questo componente al 24,18%. Osservan-

do il grafo e possibile vedere che questa classe e a sua volta parte di un array

di oggetti di tipo alice.tuprolog.Term. In aggiunta Term implementa il metodo

match, responsabile del 50% delle allocazioni totali della classe java.util.ArrayList

e il 30% della classe java.lang.Object[]. Si comprende quindi quanto la struttura

delle classi formata dalla classe Term e le due classi Var e Struct che la estendono

sia molto delicata ed abbia bisogno di una sostanziale reimplementazione al fine di

migliorare le prestazioni di tuProlog per quanto riguarda l’utilizzo della memoria.

38

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

Capitolo 5

Confronto con la versione

tuProlog 1.3

In questo capitolo vengono confrontate la versione 2.1 di tuProlog con la 1.3,

le quali sono rispettivamente le ultime due release delle versioni 2 e 1 del moto-

re. Nella piu recente fra loro sono stati introdotti diversi cambiamenti a livello

architetturale che potrebbero averne influenzato le performance. Il fine di questo

confronto e proprio quello di mettere in evidenza le differenze prestazionali fra i

due interpreti, comparando i dati ricavati dal profiling di ognuno di essi. Il pro-

filer utilizzato e sempre HPROF: ovviamente, per rendere uniforme il confronto,

vengono utilizzate per la release 1.3 le stesse impostazioni adottate nei test dei

capitoli precedenti.

Di seguito, nella Tabella 5.1, vengono esposti i tempi di esecuzione misura-

ti nell’esecuzione delle teorie utilizzate come benchmark, sia per la versione 2.1

che per la versione 1.3 di tuProlog. Per gli stessi motivi discussi nel Capitolo 3

non vengono inseriti nella tabella i dati relativi alle teorie Crypt, Poly, Qsort,

Queens e Query in quanto hanno tempi di esecuzione troppo bassi.

Si puo osservare che la nuova architettura ha portato ad un aumento dei tempi di

esecuzione piuttosto pronunciato in tre delle quattro teorie sottoposte a verifica.

L’unica eccezione riguarda la teoria Tak, che e invece risultata circa il 37% piu

39

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

CAPITOLO 5. CONFRONTO CON LA VERSIONE TUPROLOG 1.3

Nome teoria tuProlog 1.3 (ms) tuProlog 2.1 (ms)Einstein’s riddle 10359 13807Spanning tree 1670 2216Switch square 6675 10110Tak 18158 11373

Tabella 5.1: Comparazione dei tempi di esecuzione nelle singole teorie.

veloce nella release 2.1 rispetto alla release 1.3.

Per quanto riguarda la memoria totale allocata dalle due versioni dell’inter-

prete l’analisi viene invece effettuata sommando lo spazio del heap allocato da

tutti i singoli metodi utilizzati nella risoluzione di ogni teoria. Nella Tabella 5.2

vengono quindi esposti i dati ricavati in KByte.

Nome teoria tuProlog 1.3 (KB) tuProlog 2.1 (KB)Crypt 4988 6106Einstein’s riddle 90576 206040Poly 3510 2351Queens 20201 10832Qsort 4334 2742Query 3410 2378Spanning tree 27928 67885Switch square 199696 262795Tak 343402 391710

Tabella 5.2: Comparazione dell’allocazione della memoria nelle singole teorie.

Anche in questo caso e possibile vedere come la piu’ recente release di tuProlog

utilizzi mediamente piu’ memoria. In special modo si puo notare la differenza

maggiore fra i due interpreti nelle teorie Einstein’s riddle, Spanning tree, Switch

square e Tak, le quali sono anche le quattro teorie con i tempi di esecuzione piu

lunghi. Le teorie Crypt, Poly, Qsort e Queens, che hanno utilizzi della memoria

relativamente contenuti, risultano allocare meno spazio nell heap nella versione

piu recente del motore.

40

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

Capitolo 6

Conclusioni

Il lavoro di profiling del motore tuProlog ha messo in luce diverse sezioni del

codice su cui porre l’attenzione in una futura reimplementazione dello stesso. Sia

per quanto riguarda i tempi di esecuzione, sia per l’utilizzo della memoria, si e visto

che e plausibile ottenere dei buoni miglioramenti agendo su parti dell’interprete

piuttosto circoscritte. In questo modo e possibile limitare la quantita di lavoro da

dedicare alla sua riscrittura. In aggiunta si e potuto constatare che l’allocazione

della memoria incide visibilmente sui tempi di esecuzione del motore: percio, an-

che la velocita di tuProlog beneficerebbe da un piu oculato utilizzo della medesima.

In particolare le classi che rappresentano i termini della teoria sono quelle

che provocano il piu alto consumo di memoria e di tempo. La classe Term e

le sue specializzazioni Struct e Var sono la fonte principale dell’allocazione della

memoria. Studiando il loro comportamento dettagliatamente si e visto che il me-

todo Var.rename e la causa principale dell’allocazione degli oggetti char[], String e

StringBuffer. Questi occupano circa il 20% della memoria totale istanziata durante

l’esecuzione di tuProlog. Term.match, insieme al ClauseStore.deunify, e invece il

metodo che istanzia piu frequentemente gli oggetti Object[], ArrayList e Abstrac-

tList$Itr, producendo il 42% della memoria totale allocata. Anche nell’analisi dei

tempi di esecuzione queste classi risultano fra le piu costose. Infatti, escludendo il

metodo Object.wait, i sette metodi con i tempi di esecuzione piu lunghi apparten-

gono, oppure ricevono la maggior parte delle chiamate, da una di queste tre classi.

41

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

CAPITOLO 6. CONCLUSIONI

Uno dei punti fondamentali del lavoro e stato quello di valutare l’effetto del gar-

bage collector sui tempi di esecuzione. A questo proposito si e visto che la pulizia

della memoria grava anche pesantemente sulla velocita di risoluzione delle teorie.

Questo risulta essere particolarmente vero quando il sistema che ospita tuProlog

ha una quantita di memoria limitata. La conseguenza a questo problema e l’impos-

sibilita nell’avere performance scalabili all’aumentare della complessita delle teorie.

Oltre all’analisi delle prestazioni di tuProlog, il progetto ha avuto lo scopo

di mettere a confronto i tempi di esecuzione e l’utilizzo della memoria allocata da

parte delle versioni 2.1 e 1.3 dell’interprete. Il fine di questa comparazione e stato

quello di esaminare quali siano state le conseguenze in termini di performance in

seguito allo sviluppo dell’architettura del motore. A questo proposito si e visto che

la piu recente delle due versioni ha subito un aumento sia dei tempi di esecuzione

che della memoria allocata.

Un primo indirizzo che puo interessare il revisore di tuProlog riguarda il metodo

Var.rename. Questo metodo viene utilizzato dal costruttore della classe Var e, al

suo interno, compie delle concatenazioni fra oggetti String. I tempi di esecuzione,

come l’allocazione della memoria, potrebbero migliorare se, al posto di una classe

immutabile la quale crea nuovi oggetti char[] ad ogni operazione, venisse utilizzata

StringBuilder. Anche Term.match puo essere facilmente migliorato impostando

una capacita iniziale diversa da quella di default. Gli ArrayList creano infatti un

oggetto Object[] ogni volta che la capacita della lista diventi insufficiente ad acco-

gliere altri oggetti. Aumentando ragionevolmente le dimensioni iniziali della lista

e possibile diminuire il numero di volte che questa operazione viene effettuata e,

di conseguenza, le allocazioni di Object[].

Infine, per quanto riguarda gli sviluppi futuri, e ragionevole approfondire l’a-

nalisi della memoria. Il profiler HPROF non permette infatti di analizzare quali

sono gli oggetti live in un certo istante dell’esecuzione dell’applicazione, ma sem-

plicemente gli oggetti allocati. Conoscere la quantita di memoria live nel tempo

potrebbe rivelarsi utile, in quanto il valore massimo di questa funzione corrispon-

42

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

de alla quantita di memoria che deve essere a disposizione dell’applicazione per

eseguire correttamente1. Percio, individuare i metodi che causano eventuali pic-

chi nell’utilizzo della memoria, migliorerebbe l’utilizzo di tuProlog su sistemi con

risorse limitate.

1In qualsiasi istante della risoluzione della teoria la quantita di memoria live non puo esseresuperiore alla memoria heap a disposizione, in quanto questo causerebbe un OutOfMemoryError.

43

Page 54: Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi
Page 55: Analisi di prestazione dell'interprete tuProlog su piattaforma Java - Tesi

Bibliografia

[1] Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein.

Introduction to algorithms. MIT Press, 2008.

[2] Erich Gamma, Richard Helm, Ralph Johnson, and John M. Vlissides. Design

patterns: elements of reusable object-oriented software. Addison-Wesley, 1994.

[3] Richard Jones and Rafael D. Lins. Garbage collection: algorithms for automatic

dynamic memory management. Wiley, 1996.

[4] Sun Microsystems. Memory management in the java hotspot virtual machine.

Technical report, Sun Microsystems, 2006.

[5] Monica Pawlan. Reference objects and garbage collection. 1998.

[6] Alessandro Ricci, Alex Benini, Andrea Omicini, and Giulio Piancastelli. The

architecture and design of a malleable object-oriented prolog engine. Technical

report, DEIS - Universita di Bologna, 2008.

45

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