Da qualche tempo i sorgenti di Python 3.2 (al momento arrivato alla quarta alpha) sono mio oggetto di studi per comprendere le differenze col vecchio ramo 2.x (Python 2.7 è ne sarà l’ultima versione) e le possibili modifiche apportabili per migliorarlo dal punto di vista prestazionale.
Seguendo anche la mailing list degli sviluppatori ho sentito parlare spesso di una modifica al cuore della virtual machine che permette di ottenere un notevole boost, e che è basata sui cosiddetti computed goto, funzionalità questa che ho visto la prima volta nel linguaggio Fortran, ma mai in C e derivati.
Sorgenti alla mano (si tratta del file ceval.c che potrete trovare nella cartella Python scaricabile da qui), ho potuto constatare che effettivamente la VM è stata modificata per sfruttare questa caratteristica (segue un estratto):
#if USE_COMPUTED_GOTOS
/* Import the static jump table */
#include "opcode_targets.h"
[...]
#define FAST_DISPATCH() \
{ \
if (!_Py_TracingPossible) { \
f->f_lasti = INSTR_OFFSET(); \
goto *opcode_targets[*next_instr++]; \
} \
goto fast_next_opcode; \
}
#endif
#else
#define FAST_DISPATCH() goto fast_next_opcode
#endif
Com’è possibile vedere, il codice ha subito una “biforcazione”, utilizzando il preprocessore per controllare se i computed goto sono disponibili e abilitati, altrimenti viene compilato il normale code-path.
L’istruzione incriminata è ovviamente:
goto *opcode_targets[*next_instr++]
della cui sintassi non v’è traccia alcuna nel draft dello standard ISO C99 (paragrafo 6.8.6.1, pagg. 137-138 o pagg.149-150 usando Acrobat Reader).
Si trova, invece, nel manuale del compilatore GCC alla sezione 6.3 (“Labels as Values“), pagg. 279-280 (291-292 con Acrobat).
Il titolo del capitolo 6 è tutto un programma (“Extensions to the C Language Family“), e già dalla prima riga lo scopo risulta chiaro: “GNU C provides several language features not found in ISO standard C“.
Si tratta della parte più corposa del manuale, considerato che consta di ben 282 pagine (su 692 totali), a cui seguono altre 12 pagine per le estensioni al C++, e infine altre 9 per quelle dedicate a Objective-C.
Uno scenario analogo si ha per la libreria standard. ANSI e ISO riportano nei draft un ben preciso insieme di file include che vanno supportati e che definiscono costanti, macro e funzioni che vi fanno parte, e che devono essere messi a disposizione da tutti i compilatori aderenti.
Il manuale della GNU C Library (chiamata anche libc o glibc) presenta, invece, una “ricca dote”. Oltre alla libreria standard mette a disposizione funzionalità POSIX, Berkeley System V, e… estensioni GNU.
E’ facile immaginare, a questo punto, la quantità di estensioni (soprattutto al C) & funzioni che siano state introdotte. Nuove funzionalità, quindi, ma che, se utilizzate, creano di fatto ulteriori problemi di portabilità e compatibilità (oltre ai soliti con cui si ha a che fare col linguaggio C).
L’esempio della VM Python è eloquente: è stato necessario far ricorso al preprocessore per sfruttare i computed goto e mantenere, al contempo, la compatibilità con tutti gli altri compilatori che seguono, invece, lo standard C (per CPython, come per diversi altri progetti, il riferimento è l’ANSI C89; infatti nemmeno i commenti C++ style, sebbene introdotti con l’ISO C99, sono permessi).
Paradossale, poi, che alcune estensioni GNU al linguaggio C non siano supportate dal frontend C++, col risultato che, se un progetto per cui è stato utilizzato il C ne fa uso un giorno si dovesse decidere di passarlo al C++, l’operazione non sarebbe immediata, ma richiederebbe di rimetter mano al codice…
La non stretta né piena compatibilità allo standard crea una situazione tutt’altro che piacevole per chi deve sviluppare applicazioni portabili, o dovesse decidere di effettuare il porting di un progetto verso sistemi non inizialmente supportati.
Ne parla esplicitamente un’apprezzata programmatrice Amiga, Elena Novaretti, di cui trovate opinione e problematiche in questa pagina, alla sezione 2.3 (“Compilazione su AmigaOS3.x (GCC)“), in cui appare evidente di come GCC stia letteralmente soffocando gli altri compilatori a causa dei progetti che sono scritti con e, purtroppo spesso, per esso.
Un altro esempio concreto arriva in questi giorni, con l’avanzamento dei lavori del porting di AROS alle vecchie, gloriose, macchine Amiga, che sono basate sulla CPU Motorola 68000.
Al momento l’obiettivo primario è la realizzazione di una versione del Kickstart interamente basata sul codice di AROS, ma esiste già qualcosa di parzialmente funzionante anche se, com’è evidente dal secondo commento, risulta troppo lento.
D’altra i problemi di GCC col frontend 68000 sono ben noti (ne ho parlato anche in quest’articolo), tant’è che anche un membro del team di Natami (progetto di cui abbiamo già parlato e a cui sta molto a cuore un port di AROS per 68000) auspica l’uso di un compilatore migliore (ultimo commento di giorno 29 novembre).
Si pensa di utilizzare un altro noto compilatore in ambito 68000, il Vbcc, ma anche a un eventuale miglioramento di LLVM che supporti pienamente questa famiglia di processori.
In ogni caso lo scoglio più grande da superare rimane il codice già scritto e troppo legato al GCC e alla sua non stretta aderenza allo standard. Standard che, è bene ricordarlo, non si possono tirare in ballo soltanto quando fa comodo, come nella stigmatizzata e ben nota politica “embrace, extend and extinguish“…