Table of Contents
ARTÌS: documentazione online
ARTÌS: Advanced Runtime Infrastructure
Introduzione
Nel corso degli anni la simulazione ha progressivamente assunto un ruolo di primo piano come strumento di supporto alla progettazione, alla verifica e alla valutazione di prestazioni di sistemi. I sistemi attualmente considerati d'interesse sono spesso caratterizzati da un’estrema complessità: sono dinamici, formati da entità eterogenee e spesso massivamente popolati. Soprattutto in passato, la loro analisi basata sulle tecniche di simulazione è stata vincolata dalle ridotte capacità di calcolo. L’approccio caratterizzato da un’eccessiva semplificazione del modello concettuale ha spesso portato a risultati incompleti o errati. La progettazione e la valutazione delle performance di sistemi complessi, in particolare se di supporto a strutture di vitale importanza, non possono prescindere dagli strumenti di simulazione a disposizione. L’analisi di modelli complessi suggerisce naturalmente un approccio basato su simulazione parallela o distribuita. Questo approccio, anche se molto promettente, non è sempre prestazionalmente proficuo. I modelli intrinsecamente caratterizzati da un’elevata dinamicità spesso introducono tali overhead di comunicazione da vanificare i vantaggi offerta dalla parallelizzazione del carico. Per ovviare a questi problemi si sono cercate varie soluzioni, usualmente basate su meccanismi di filtraggio delle interazioni (Data Distribution Management, DDM). Lo standard IEEE 1516 (High Level Architecture) rappresenta l’attuale punto di riferimento per l’implementazione di simulazioni distribuite. Purtroppo la sua analisi ha evidenziato lacune significative sia nell'architettura che nelle implementazioni attualmente disponibili sul mercato. Ad esempio, l’assenza di un paradigma basato sulla migrazione delle entità simulate rende difficile l’elaborazione di sistemi dinamici (es. reti wireless Ad-Hoc, reti di sensori ecc.) salvo che non si faccia uso di ulteriori componenti software esterne al middleware.
L'assenza di implementazioni dello standard Open Source (o con codice facilmente utilizzabile a fini di ricerca) e la ricerca di migliori prestazioni ha portato al design e all’implementazione di ARTÌS (Advanced RTI System), un middleware adattivo fortemente basato sul riutilizzo delle componenti. Il middleware è espressamente orientato alla simulazione parallela e distribuita, pur essendo utilizzabile anche per l'implementazione di simulazioni monolitiche.
L'approccio parallelo e distribuito potenzialmente offre numerosi vantaggi, soprattutto in contesti come:
- Aggregazione di risorse di calcolo e di memoria;
- Supporto ad applicazioni time-critical, dove la simulazione viene utilizzata come strumento di supporto alle decisioni;
- Progettazione di sistemi complessi, rappresentabili con modelli che non sono valutabili in tempo utile attraverso l'approccio monolitico;
- Implementazione di Digital Virtual Environments (DVE), che comprendono risorse e/o utenti in locazioni geograficamente distanti;
Nel seguito di questo documento verranno introdotti i concetti principali che caratterizzano il runtime ARTÌS e la sua struttura logica generale. Verranno passati in rassegna i moduli principali che compongono il middleware, le Application Programming Interfaces (API) attualmente disponibili e alcuni esempi di utilizzo.
Struttura logica del Runtime
Il runtime ARTÌS nasce con il preciso obiettivo di supportare efficientemente simulazioni di scenari complessi seguendo un approccio ambito parallelo e distribuito. La struttura logica altamente modulare è stata riflessa in un’implementazione basata su componenti che sono facilmente estendibili. Tipicamente il punto critico dei sistemi distribuiti è rappresentato dall’overhead introdotto dalla latenza nelle comunicazioni di rete, questa è estremamente variabile a seconda della tipologia di rete utilizzata (la latenza di una comunicazione attraverso un bus è ordini di grandezza inferiore rispetto alla comunicazione degli stessi dati su Internet). Ottenere prestazioni di un certo interesse significa quindi distribuire la computazione e minimizzare il costo di comunicazione, traendo vantaggio da eventuale memoria condivisa su piattaforme multiprocessore e da protocolli ottimizzati a seconda dell’architettura di rete utilizzata. Nella nostra visione queste funzionalità dovrebbero essere rese disponibili in maniera quanto più trasparente possibile all’utente finale, possibilmente in modo del tutto adattivo, senza penalizzare le prestazioni. Una delle caratteristiche cruciali tipiche di qualsiasi runtime dedicato alla simulazione è la gestione del tempo: nel corso degli anni sono state sviluppate varie tecniche di gestione della sincronizzazione in ambiente parallelo e distribuito. A seconda del modello simulato è possibile trarre vantaggi da algoritmi di sincronizzazione diversi. Al momento ARTÌS mette a disposizione il supporto per simulazioni ad approccio pessimistico (Chandy-Misra-Bryant, Timestepped) e il supporto per la sincronizzazione ottimistica (Time-Warp). A differenza di quanto avviene nello standard IEEE 1516, ARTÌS intende offrire supporto nativo a varie tecniche di migrazione delle componenti, la possibilità di integrare questa metodologia modificando direttamente gli algoritmi di sincronizzazione permette una migliore gestione degli overhead. Spesso i runtime risultano opachi all’utente durante l’esecuzione, è impossibile accedere alle informazioni dettagliate di funzionamento, tra l’altro questa caratteristica rende difficoltosa un’eventuale ottimizzazione del modello simulato. Per ovviare a questo inconveniente è stato previsto un meccanismo di real-time introspection attraverso il quale gran parte delle informazioni interne al runtime sono rese disponibili all’utente finale tramite un meccanismo di publishing/subscribe, inoltre questo sistema permette la riconfigurazione durante l’esecuzione dei parametri di funzionamento del middleware.
Communication Layer
È evidente come la comunicazione rivesta un ruolo essenziale nella ricerca delle prestazioni per le simulazioni parallele e distribuite. Partendo da questo concetto è altrettanto evidente come nell’implementazione di un middleware sia necessario ottimizzare il sistema di comunicazione, per adattarlo alle varie architetture a seconda delle loro peculiarità. L’approccio classico alla simulazione distribuita prevede un insieme di Logical Process (LP) che interagiscono tra di loro attraverso semplici primitive di comunicazione. Nell’implementazione ARTÌS ogni LP risulta basato su un’architettura multithread: ogni thread è dedicato alla gestione della comunicazione su un singolo canale di input/output al fine di svincolare la componente simulativa vera e propria del processo logico dalle problematiche di comunicazione. Uno degli obiettivi del nostro progetto è la creazione di un middleware in grado di ottenere buone prestazioni su architetture miste (formate da sistemi multiprocessore e monoprocessori collegati in rete), abbiamo quindi lavorato in modo da rendere il meccanismo di comunicazione adattivo rispetto all’ambiente di esecuzione. Allo stato attuale di sviluppo ARTÌS comunica attraverso Shared Memory, dove possibile, e TCP/IP negli altri casi. Dall’architettura logica (Fig 2.1) si può notare come siano previste anche soluzioni basate su Reliable UDP e ottimizzazioni su multicast dove disponibile: inoltre la modularità è garantita in questo caso dalla natura multi-threaded dell’architettura, che permette appunto l’estensione a nuovi protocolli tramite l’aggiunta di ulteriori thread per la comunicazione.
RTI Kernel
Le componenti relative ai Services implementano parzialmente i servizi tipicamente offerti da un RTI Kernel, fornendo i servizi di Time, Federation, Declaration ecc. Management tipici di ogni middleware per la simulazione. Per quanto tali componenti costituiscano il vero cuore del runtime, l’utente finale può rimanere all’oscuro dei dettagli implementativi, accedendo alle funzionalità grazie a funzioni di libreria, che offrono all’utente l’interfaccia attraverso la quale accedere ai servizi di sincronizzazione e comunicazione.
Application Programming Interfaces (API)
Le API messe a disposizione dal middleware sono multiple, una versione base rappresentata dalle Unibo API disponibili in binding C/C++. A queste API iniziali è previsto si aggiunga un livello di compatibilit`a High Level Architecture IEEE 1516 e alcune versioni semplificate e specializzate per obiettivi particolari (es. Internet Gaming API).
Moduli di supporto
Real-Time Introspection, Logging, Performance, Migration e Utility intendono rappresentare una serie di moduli di servizio a supporto del runtime, integrati nel middleware e con obiettivi diversificati. Proprio per la loro peculiarità, tali moduli sono abilitati opzionalmente in modo da non interferire con le prestazioni del middleware in un contesto normale. Si tratta di funzionalità avanzate, utili in fase di debug o durante la fase di progettazione del modello, che è quindi opportuno poter disabilitare.
Implementazione
L’implementazione attuale offre un sottoinsieme limitato delle funzionalità preventivate per la versione completa del runtime. Questo sottoinsieme è comunque sufficiente per l’implementazioni di modelli basati su diversi approcci. La natura prettamente in sviluppo del lavoro non permette di assicurare il mantenimento della compatibilità tra le varie versioni del runtime che si succederanno nel tempo. Il riscontro degli utenti ricopre comunque una notevole importanza: ARTÌS uno strumento che ha l’obiettivo di facilitare l’implementazione di simulazioni. Per la sua natura, non è previsto che il runtime sia utilizzabile senza una prepazione basilare su temi come i sistemi distribuiti [6], la simulazione e la sincronizzazione. Allo stesso tempo riteniamo comunque indispensabile che tutta una serie di ottimizzazioni siano automatiche e demandate al runtime, senza alcun intervento da parte dell’utilizzatore. La trasparenza dei meccanismi di comunicazione, unita all’adattività del sistema apportano flessibilità e facilitano molte fasi del lavoro, ma il punto focale delle simulazioni rimane la descrizione del modello, la sua correttezza ed implementazione. Senza un’adeguata descrizione del contesto da ricreare sarà impossibile ottenere risultati fedeli, indipendentemente dal runtime utilizzato.
Ruolo del Simulation Manager (SIMA)
L’architettura dell’implementazione risulta parzialmente centralizzata per la presenza di un SImulation MAnager che ricopre vari compiti tra i quali la coordinazione degli LP, l’inizializzazione e terminazione della simulazione, la gestione di barriere di sincronizzazione durante l’esecuzione. Il SIMA è a conoscenza del numero totale di LP, e della natura dei canali di comunicazione che li collegano. Esso rileva la disposizione topologica degli host (secondo la nostra terminologia PEU, Physical Execution Unit) ed indica ad ognuno di essi come instaurare il dialogo con gli altri. In pratica, è necessario eseguire prima della simulazione vera e propria una fase di inizializzazione dei canali di trasmissione, cos`ı da predisporre ogni LP alla comunicazione con gli altri nel modo più efficiente possibile.
API
- void SIMA Initialize( int port, int numpeers, char *netfile ); Questa funzione viene invocata per indicare al SIMA quanti siano gli LP partecipanti alla simulazione, su quale porta avverranno le comunicazioni di inizializzazione e quale sia il file da leggere relativo ai canali di comunicazione. La funzione non ritorna finchè non sono stati contattati tutti gli LP (vedi Barrier);
- void SIMA Finalize( ); Chiude i socket di comunicazione tra SIMA e LP. Alla chiamata di questa funzione, la fase di inizializzazione è terminata, tutti gli LP sono in possesso delle informazioni che permettono loro di instaurare connessioni con gli altri LP e le risorse allocate in fase di inizializzazione possono essere rilasciate;
- void SIMA Barrier( ); Istituisce un punto di sincronizzazione durante l’esecuzione. Questa riprenderà solamente quando tutti gli LP si saranno sincronizzati con il SIMA.
Esempio di utilizzo
Il codice del SIMA dovrà avere una struttura di questo tipo:
SIMA Initialize(porta, lps,channels.txt); ... SIMA Finalize( );
Il file di configurazione indica al runtime quali siano i valori di lookahead su ogni singolo canale, oppure specifica un valore globale e comune a tutti gli LP. Esso dovrebbe essere composto in due parti: la prima per definire i canali di comunicazione ed i loro nomi, la seconda per specificare un valore di lookahead. Quest’ultimo potrà essere di tipo globale, cioè comune a tutti gli LP, oppure distinto per ognuno di essi (solo nel caso Chandy-Misra-Bryant) , come nell’esempio seguente:
# DEFINIZIONE DEI CANALI: :MILANO 0 :BOLOGNA 1 :ROMA 2 #DEFINIZIONE DEI LOOKAHEAD GLOBAL LA=0 # Look−Ahead Globale Disabilitato MILANO: BOLOGNA 5 ROMA 7 ROMA: MILANO 7 BOLOGNA 3 BOLOGNA: MILANO 5 ROMA 3
Nella prima parte vengono associati ID numerici alle etichette che identificano gli LP, quindi si procede con la specifica del lookahead per ognuno di essi, se il modello lo prevede, oppure con l’inserimento di un valore positivo nel campo GLOBAL_LA. Esso segnala che il lookahead sarà lo stesso per tutti gli LP e lo quantifica (nell’esempio non viene usato lookahead globale). Nel file di configurazione le linee che iniziano con il simbolo # sono considerate come commenti.
Gestione della sincronizzazione
Attualmente sono implementati due diversi meccanismi di sincronizzazione en- trambi basati su approccio pessimistico:
- Chandy-Misra-Bryant. È l’algoritmo conservativo più comune ed impiegato nella simulazione distribuita, nel tempo sono state proposte molteplici varianti ed ottimizzazioni. Il concetto alla base del protocollo è quello di “messaggio safe”, un messaggio si può definire “safe” solamente quando la sua elaborazione sicuramente non provocherà violazioni causali (es. ricezione da parte dell’LP di messaggi “nel passato”). Per verificare se un messaggio è “safe” o meno ogni LP deve essere informato sullo stato di avanzamento degli altri LP connessi. I dettagli del protocollo, la definizione dei NULL-message ed il concetto di lookahead sono approfonditi in [4](pagg. 51-63) e [10].
- TimeStepped. In questo caso il tempo viene suddiviso in step sequenziali di una certa durata, gli eventi si riferiscono agli step. Anche questa gestione del tempo è sostanzialmente pessimistica: l’avanzare degli step di simulazione non permette che il vincolo di causalità sia mai violato. In particolare, l’algoritmo TS può essere considerato come una variante del precedente, dove il lookahead viene staticamente determinato in fase di inizializzazione, ed è identico per ogni canale di comunicazione esistente tra i diversi LP.
Le procedure di sincronizzazione e gestione dei messaggi sono dichiarate nelle librerie TS.h e CMB.h, all’interno della cartella INCLUDE. Esse mettono a disposizione dell’utente le funzioni descritte nel paragrafo seguente.
API Chandy-Misra-Bryant
Tutte le funzioni elencate restituiscono un valore negativo in caso di errore. In particolare, per le chiamate relative all’invio di messaggi (CMB Send, Broadcast, SendToOthers), devono essere rispettati i vincoli temporali imposti dal valore del lookahead al momento dell’invocazione per ognuno dei destinatari. Nessuna delle funzioni adibite all’invio di messaggi è bloccante. Per evitare di ottenere risultati solo apparentemente corretti, è consigliabile verificare il valore di ritorno di tali funzioni, in caso esso risulti negativo, per avere una descrizione dell’errore è possibile controllare la stringa cmb error[MAX ERROR STRING], previa dichiarazione extern della stessa. Oltre ai vincoli temporali, possono infatti incorrere altre problematiche comuni negli ambienti distribuiti (overflow, time-out di rete, ecc).
- int CMB_Init( char *cname, char *sima_addr, int sima_port ); Si procede all’inizializzazione dell’algoritmo CMB. In dettaglio ogni LP procede all’inizializzazione delle strutture dati necessarie per l’esecuzione dell’algoritmo. Nella chiamata viene indicata la label di identificazione dell’LP (eventualmente NULL) ed i parametri per contattare il SIMA.
- int CMB_Send( int lp, TM_Time evt_time, void *msg, int msg_size ); funzione impiegata per l’invio dei messaggi. Prende in input l’identificativo del destinatario (LP), il timestamp al quale il messaggio fa riferimento, un puntatore al messaggio da inviare e la sua dimensione;
- int CMB_Broadcast( TM_Time evt_time, void *msg, int msg_size ); come la precedente, ma senza identificativo del destinatario in quanto il messaggio viene spedito a tutti (LP incluso);
- int CMB_SendToOthers( TM_Time evt_time, void *msg, int msg_size ); come la CMB Send, ma senza identificativo del destinatario in quanto il messaggio viene spedito a tutti (LP escluso);
- int CMB_Schedule( TM_Time evt_time, void *msg, int msg_size ); simile alla funzione CMB Send, ma anzich`e spedire il messaggio ad un altro LP, lo immette nella coda in entrata dello stesso processo dalla quale è stata invocata;
- long CMB_Receive( int *lp,TM_Time *evt_time,void *msg, int max_size ); questa funzione memorizza nella struttura puntata da *msg il messaggio ricevuto dal processo *lp al tempo time. Viene tipicamente utilizzata all’interno di un ciclo nella funzione main, fino al verificarsi di una condizione di arresto. Ritorna la lunghezza del messaggio ricevuto;
- void CMB_SetLookAhead( int lp, TM_Time lookahead); utilizzata per impostare a runtime il lookahead del processo lp, prende in input un intero (l’id del processo) e un valore numerico che indica il nuovo lookahead; Nell’attuale implementazione non `e possibile decrementare il lookahead, ma solo aumentarlo;
- TM_Time CMB_GetLookAhead( int lp); fornisce il valore di loockahead relativo al canale aperto con l’LP indicato da parametro.
- void CMB_Finalize( ); provvede ad effettuare la pulizia delle strutture dati nonchè a sincronizzare la terminazione dell’esecuzione della simulazione.
API TimeStepped
Le funzioni di libreria offerte dalla libreria TimeStepped sono del tutto analoghe a quelle appena descritte, con l’omissione delle due funzioni per la gestione del lookahead, in quanto esso viene specificato staticamente all’interno dei file di configurazione del modello. L’invocazione della funzione TimeAdvance conclude lo step passando al successivo.
- int TS_Init( char *cname, char *sima_addr, int sima_port );
- int TS_Send( int lp, TM_Time evt_time, void *msg, int msg_size );
- int TS_Broadcast( TM_Time evt_time, void *msg, int msg_size );
- int TS_SendToOthers( TM_Time evt_time, void *msg, int msg_size );
- int TS_Schedule( TM_Time evt_time, void *msg, int msg_size );
- long TS_Receive( int *lp, TM_Time *evt_time, void *msg, int max_size );
- TM_Time TS_TimeAdvance();
- void TS_Finalize();
Esempi di utilizzo
Simulazione del traffico aereo (timestepped)
Uno degli esempi più diffusi nella simulazione è quello del traffico aereo. La semplicità del modello concettuale, ed il ridotto numero di relazioni causali lo rende particolarmente adatto per illustrare il funzionamento di una simulazione. Ovviamente sono tralasciati molti aspetti fondamentali del contesto reale (es. altitudine degli aeroplani), ma secondari considerando la natura e gli scopi prettamente didattici. All’interno della cartella EXAMPLES/AIRPORTS si trova il codice relativo al modello, implementato con l’algoritmo TimeStepped. In pratica è suddiviso in 3 sezioni: le variabili di stato del simulatore, i gestori degli eventi e la funzione main, che inizializza le variabili e “spedisce” i primi aeroplani, quindi entra in un ciclo while che riceve i messaggi successivi ed invoca per ognuno di essi un handler appropriato, fino al raggiungimento del limite temporale impostato per la conclusione. Il flusso di aerei tra i diversi aeroporti è implementato tramite un semplice scambio di messaggi.
typedef struct sub msg header { char event type[4]; //Arrival, Landing or Departure char from[10]; //Airport from which the message arrives char id[12]; //Name of the fly } SubMsgHeader;
La variabile event type può rappresentare 3 diversi stati: Partenza, Arrivo ed Atterraggio. Il ciclo di vita di un aeroplano inizia nel suo aeroporto d’origine, dove al tempo 0 viene schedulata la prima partenza verso un aeroporto scelto a caso tra i due disponibili. Da questo momento, esso continuerà ad arrivare, attendere che si liberi la pista di atterraggio, atterrare scaricare i passeggeri e ripartire, fino al timestamp definito come termine della simulazione. La modellazione del modello `e estremamente lineare, in quanto si possono facilmente individuare gli handler necessari e la politica con la quale la simulazione procede. Il ciclo while all’interno della funzione main chiarifica ulteriormente questa semplice struttura:
/*Simulation main loop, receives messages and calls procedure associated with it*/ while (!end reached) { size = TS Receive(&from, &Ts, (void *)msg, 1024); if (size > 0) {//A message has been received, process it calling appropriate handler submsg = (SubMsgHeader *)msg; if (strcmp (submsg−>event type, "ARR")==0) arrival event handler (clock, submsg−>from, submsg−>id); 10 if (strcmp (submsg−>event type, "LAN")==0) landed event handler (clock, submsg−>from, submsg−>id); if (strcmp (submsg−>event type, "DEP")==0) departure event handler (clock, submsg−>id); } else {//no new messages, and end clock reached if (clock == end clock) { printf ("[%s]: condiz. arresto raggiunta...); 20 end˙reached = 1; TS˙Finalize(); } else//no new messages, and end clock still not reached clock = TS˙TimeAdvance(); } }
Si distinguono facilmente due casi, dipendentemente dall’arrivo di nuovi messaggi. Tale controllo viene eseguito sulla dimensione ritornata dalla funzione TS_Receive(): nel caso esso sia maggiore di zero, si può procedere all’elaborazione dell’evento passandolo all’handler. In caso contrario, se la simulazione è terminata (end clock = 1) viene invocata la TS Finalize(), se non è così dobbiamo attendere l’arrivo di un nuovo messaggio e far avanzare il tempo. La directory di questo esempio comprende uno script (run) che lancia prima il SIMA ed in seguito tre istanze di airports ts, deviando l’output di ogni singolo LP sui file x.out-ts e x.err-ts. All’interno di essi compare una riga per ogni evento, con l’indicazione del tempo simulato pi`u altre informazioni utili per ricostruire l’andamento della simulazione; si nota come la scelta delle destinazioni sia casuale, e come la definizione dei tempi di atterraggio/carico/partenza influisca sul numero totale di voli effettuati in un intervallo predefinito (2000). Lo script va eseguito passando come primo argomento l’eseguibile che si intende utilizzare.
Simulazione del traffico aereo (CMB)
All’interno della directory EXAMPLES/AIRPORTS si trova un esempio del tutto analogo al precedente, solo implementato utilizzando l’algoritmo Chandy/Misra/Bryant. A fronte di un’apparente analogia con il modello Time-Stepped, questo scenario simulato sfrutta un differente algoritmo di sincronizzazione. Descrivere nei dettagli questo modello sarebbe ridondante, è stato incluso a scopo didattico per permettere di studiare le analogie/differenze tra i due algoritmi. Principalmente, nel ciclo main() non compare la funzione per l’avanzamento del tempo, ed all’interno del file channels.txt sono da definire i lookahead per ognuno dei canali. Nel caso della simulazione del traffico aereo, il lookahead di un canale corrisponde alla durata minima del viaggio tra i due rispettivi aeroporti.
Modello didattico per reti Wireless
Allo scopo di includere un esempio più concreto, pur rimanendo in un ambito didattico, è stato incluso un modello di reti Wireless estremamente semplificato, che rappresenta lo scambio di messaggi PING tra un numero arbitrario di Host simulati (Simulted Mobile Hosts, SMH) in movimento. Ognuno di essi ha un determinato raggio di copertura, e si sposta all’interno di uno spazio bidimensionale toroidale. Il modello sfrutta le primitive fornite da ARTÌS (Time-Stepped) per far comunicare tra loro gli host simulati su LP distinti, e come negli esempi precedenti il ruolo del SImulation MAnager è quello di coordinarli in fase di inizializzazione. È importante notare come ogni LP gestisca solamente una parte degli host simulati. Ad ogni step della simulazione, ogni host esegue un PING verso tutti gli altri host che si trovano in sua prossimit`a (all’interno del suo raggio di copertura). Successivamente l’host simulato con probabilità del 50% si muove su una direzione casuale. Analizzando il traffico uscente dagli LP si può notare come ad ogni step vengano inviati i messaggi di PING dei vari host simulati (SMH) e gli aggiornamenti relativi alla loro posizione, necessari per mantenere aggiornato lo stato globale del sistema.
Bibliografia
- [DFW97] Dahmann J. S., Fujimoto R., and Weatherly R. M. The Department of Defense High Level Architecture. In Winter Simulation Conference, pages 142–149, 1997.
- [DFW98] Dahmann J. S., Fujimoto R., and Weatherly R. M. The DoD High Level Architecture: an update. In Winter Simulation Conference, pages 797–804, 1998.
- Wayne J. Davis and Gerald L. Moeller. The high level architecture: is there a better way? In Winter Simulation Conference, pages 1595–1601, 1999.
- Fujimoto R. M. Parallel and Distributed Simulation Systems. Wiley-Interscience, 2000.
- Michele Bracuto Gabriele D’Angelo and Lorenzo Donatiello. Design ed implementazione di un middleware adattivo per la simulazione parallela e distribuita. In Proceedings of ISCS Italian Society for Computer Simulation (ISCS ’03), 2003.
- Lamport L. Time, clocks, and the ordering of events in a distributed system. Communications of the ACM, 21(7):558–656, 1978.
- Luciano Bononi and Gabriele D’Angelo. Dynamic host allocation for HLA-based distributed simulation of mobile ad-hoc networks. In Proceedings of ISCS Italian Society for Computer Simulation (ISCS ’02), 2002.
- Luciano Bononi and Gabriele D’Angelo. A novel approach for distributed simulation of wireless mobile systems. In Proceedings of Personal Wireless Communications (PWC ’03), 2003.
- Gabriele D’Angelo Luciano Bononi and Lorenzo Donatiello. HLA-based adaptive distributed simulation of wireless mobile systems. In Proceedings of the 17th Workshop on Parallel and Distributed Simulation (PADS ’03), 2003.
- Misra J. Distributed discrete event simulation. ACM Computing Surveys, 18(1):39–65, 1986.