Modularità e Compilazione Separata Mauro Franceschi, Giacomo Zanghi Concetti Generali La compilazione separata permette un completo controllo della coerenza tra le entità condivisi tra i moduli (come già visto a lezione). La coerenza non può essere garantita solamente dal compilatore ma necessita anche di adeguati concetti e costrutti nel linguaggio (es.: moduli). Concetti Generali Come può il compilatore garantire che gli oggetti visibili al di fuori dei confini del modulo siano usati in modo conforme con le regole di compatibilità? O meglio come possono essere visibili le dichiarazioni di un modulo durante la compilazione di un altro modulo? Soluzione: una symbol table per le entità esterne, memorizzabile in modo che sia disponibile per diverse compilazioni; Il compilatore ripristina la “symbol table” dal “symbol file” ogni qualvolta l’interfaccia compare nella “import list” del modulo che si sta compilando. Moduli Abbiamo già visto nella nostra carriera a cosa servono i moduli; Abbiamo visto a lezione i moduli in Modula-2: i concetti di “definition module” (module interface) ed “import list”; Ora vediamo come sono implementati i moduli in Oberon. Oberon Le parti di “definition” ed “implementation” sono fuse in una sola; Le entità che devono essere esportate per creare l’interfaccia sono segnati con un asterisco nella loro dichiarazione; Vantaggi: - in Oberon basta una sola compilazione; - non bisogna gestire due file separati. Moduli in Oberon IL Problema della Consistenza Concetti Generali I client di un’entità esportata hanno bisogno di informazioni per utilizzarla (memorizzate nel symbol file). Questi client devono essere invalidati quando queste informazioni vengono modificate! Per ripristinare l’integrità bisogna ricompilare. Cosa succede? Nei moderni linguaggi di alto livello le compilazioni ridondanti sono un pericolo serio. Il costo del processo di aggiunta di poche dichiarazioni o modifiche minori può essere così grave da ritardare la crescita e l’evoluzione del sistema stesso. Quindi L’estensione di un’interfaccia con nuovi oggetti deve lasciare le informazioni delle entità già esistenti che non sono cambiate, in modo che i client di queste vecchie entità non necessitino di essere ricompilati; Ma più che altro … Oggigiorno i sistemi sono composti di librerie di moduli che, in futuro, verranno utilizzati per lo sviluppo di nuove applicazioni. Inoltre non dovrebbe mai essere necessaria la ricompilazione di un sistema solo perché viene utilizzata una nuova versione di una libreria. Obiettivo Permettere l’estensione dei moduli evitando inutili ricompilazioni e continuando a garantire la consistenza tra i moduli. O meglio: eliminare il problema alla radice evitando l’invalidazione dei client. La Consistenza in Oberon In Oberon il modello originale prevede una key nel symbol file che viene rigenerata ad ogni compilazione. Ora vedremo altri due modelli migliorati: Layer Model; Object Model. IL Layer Model di OP2 Lo Stack degli Extension Layers La parte dei symbol file esistenti, deve rimanere immutata da un’estensione dell’interfaccia. Ogni volta che un’interfaccia viene estesa , un nuovo livello di estensione appare al top dell’interfaccia esistente. Estensioni portano a Interfaccia Multilivello --> Stack di livelli ordinati. Non è possibile determinare il livello a cui un oggetto appartiene, ma solo guardare se la dichiarazione e ‘ nel testo sorgente(tramite il vecchio symbol file). Tabella in memoria che associa un numero di livello per ogni nome presente nel symbol file. Il Numero di Livello di ogni oggetto Posizione dell’oggetto nel symbol file. IL Layer Model di OP2 Routine di compilazione IL Layer Model di OP2 Controllo sulla Consistenza Client di un modulo vede stack di livelli che descrivono l’interfaccia importata. Se l’interfaccia venisse estesa nello stesso tempo, la consistenza sarebbe garantita, perché lo stack di livelli, richiesto dal client, è presente come un prefisso dello stack effettivamente fornito. Il Client per ogni modulo importato specifica nel suo object file il numero di livelli che necessita così come il fingerprint. Il modulo che esporta elenca un fingerprint per ogni livello di strato, allora un client può importare un certo numero di livelli. IL Layer Model di OP2 Controllo sulla Consistenza Fingerprint: è una funzione di hash sul contenuto dei livelli. Per trovare il Fingerprint di un livello ci si basa anche sul Fingerprint del livello precedente. Vantaggi: i livelli hanno sempre un solo id indipendente dal lavoro a compilation time. La funzione di Fingerprinting garantisce che I cambiamenti su un certo livello si ripercuotono sul suo fingerprint. IL Layer Model di OP2 Esempio controllo della consistenza Object Model: premesse Evitare di mantenere la cronologia dello sviluppo; Prevenire disastrose conseguenze dovute alla perdita di symbol file; Consistency checking con una granularità più fine. Object Model: l’ idea Il problema è distinguere I nuovi oggetti introdotti con l’espansione del modulo; Considera l’altro estremo rispetto al modello classico e al Layer Model: un layer ed un identificativo individuale (fingerprint) per ogni oggetto; Cambia l’aspetto della lista degli oggetti importati, serve la coppia (objname, fingerprint). Esempio della lista di esportazione Passo 2 Passo 1 Object Model: Fingerprinting Nel layer model la funzione di fingerprinting era una funzione di hashing; qui non si può; Che cosa possiamo usare e cosa no ? Per capirlo ricordiamoci che il fingerprint serve a controllare la consistenza delle entità esportati al di fuori dei confini di un modulo. Object Model: Fingerprinting Cosa non possiamo usare: Attributi non esportati; Cosa possiamo usare: Il suo tipo (non basta il nome); Indirizzi delle variabili; I suoi attributi; Indirizzi dei descrittori di tipo; Il nome dell’oggetto (dipende); Object Model: Fingerprinting Il fingerprint di un oggetto è generato dai fingerprint del suo tipo e delle sue sottoparti considerate; Inoltre per le structure viene inserito anche il nome del modulo e il nome canonico; I fingerprint dei tipi predefiniti sono costanti e predefiniti. Esempio di fingerprint associati Considerazioni Ora facciamo una breve comparazione tra i tre modelli in termini di efficienza. Per il benchmarks verrà usato l’editor grafico Draw che è un’applicazione Oberon composta di 5 moduli per un totale di 46200 bytes. Dimensione dell’ object file Dimensione del symbol file Tempo di compilazione Uso della memoria Conclusioni Entrambi i modelli visti hanno raggiunto lo scopo di poter estendere i moduli limitando le invalidazioni. Il miglior modello è risultato essere l’ object model. Risolve il problema dell’invalidazione dei client nel caso in cui symbol file vada perduto. Inoltre permette l’eliminazione di oggetti obsoleti senza invalidare i client che non usano tali oggetti. Bibliografia Separate compilation and Module Extension, autore:Regis Bernard Joseph Crelier.