Come notato nei commenti, non è molto esatto affermare che "l'attuale raccolto di compilatori (Bigloo, SBCL, Gambit, Chicken, ecc.) È 20-50 volte più lento del codice C equivalente", senza qualificare come hai testato e cosa hai testato.
Per il mio uso, trovo che per molte cose Gambit e Chicken Scheme sono abbastanza vicini all'equivalente codice 'C' in velocità, con l'attuale versione di Gambit (4.7.3) generalmente più veloce di Chicken (4.9.0.1) ma oltre pre -optimizingil codice di output 'C' (facendo ipotesi sul numero di registri disponibili - presuppone x686 - e forzando l'uso della memoria dello stack per eventuali requisiti di memoria extra, quali decisioni dovrebbero essere lasciate al compilatore 'C' come fa Chicken, il che spesso elimina il requisito per registri extra e combina le fasi di elaborazione) in modo da evitare che il compilatore "C" esegua le proprie ottimizzazioni, portando a piccoli loop molto stretti fino a circa due volte più lenti degli stessi piccoli loop stretti in "C" (o Chicken ); Chicken definisce semplicemente tutte le variabili locali di una determinata funzione che ritiene opportune (utilizzate principalmente immutabilmente) e quindi dipende dal compilatore per ottimizzare la maggior parte di quelle assenti. Il pollo non
EDIT_ADD: ho fatto ulteriori ricerche sulle versioni di Chicken and Gambit-C Scheme e ho trovato quanto segue:
Chicken è più veloce di Gambit per piccoli loop stretti per il motivo sopra (dipende più dal compilatore 'C' per le ottimizzazioni senza fare altrettanto da sé e quindi sfrutta meglio i registri extra x86-64), ma anche perché non include un controllo di manutenzione dello stack "POLL" all'interno dei loop, mentre Gambit include il controllo "POLL" all'interno del loop. Anche quando questo non viene attivato (il solito caso), saranno necessari diversi cicli di clock della CPU per determinare che non è necessario nulla (circa 6 cicli). Un futuro compilatore più intelligente potrebbe vedere che non è necessario eseguire un controllo dello stack quando si è all'interno di un ciclo stretto e non si eseguono operazioni di costruzione dello stack, eseguendolo appena prima o dopo il ciclo e risparmiando questo tempo.
Le macro "C" di Gambit fanno troppo lavoro, come detto, nel definire con precisione come devono essere eseguite le operazioni, in particolare includendo operazioni con dimensioni dello stack fisse, e queste sono probabilmente più difficili da ottimizzare per il compilatore "C"; l'uso più efficace dei registri potrebbe ridurre il tempo di loop stretto di forse 4 cicli, che in combinazione con quanto sopra lo metterebbe nel campo da baseball di Chicken.
Né l'ottimizzazione "lettura / modifica / scrittura" dell'output, per esempio, operazioni vettoriali che sono state modificate sul posto e non producono codice in modo che il compilatore lo faccia. Un backend più intelligente come LLVM quando utilizzato con Haskell fa questo genere di cose. Ciò ridurrebbe l'uso del registro di uno e il tempo di esecuzione utilizzando solo una singola istruzione anziché una lettura separata, il calcolo della modifica e una scrittura nella stessa posizione; questo diventerebbe solo un calcolo della modifica (diciamo un po 'o), quindi una modifica in lettura (| =) scrivi una singola istruzione. Questo potrebbe rendere la velocità più veloce di un ciclo o giù di lì
Entrambi sono digitati dinamicamente e processano i bit "tag" dei dati come parte dei loro dati. Né sono abbastanza intelligenti per i loop stretti da ridurre i tag, eseguire il ciclo "senza tag", quindi aggiungere nuovamente i tag per qualsiasi risultato dal ciclo, né producono codice in cui il compilatore 'C' può vedere per fare questo sebbene combina queste operazioni per alcuni casi. L'ottimizzazione qui potrebbe ridurre i tempi di ciclo di un paio di cicli della CPU o giù di lì, a seconda del ciclo.
I loop 'C' molto stretti possono richiedere circa 3,5 cicli di clock della CPU per loop su una CPU veloce che non ha la velocità di accesso alla cache di memoria (come AMD Bulldozer, che è circa due volte più lento); lo stesso loop in Chicken attualmente richiede circa 6 cicli e Gambit richiede circa 16,9 cicli. Con tutte le ottimizzazioni di cui sopra, non c'è motivo per cui queste implementazioni di Scheme non possano farlo, tuttavia è necessario un po 'di lavoro:
Nel caso di Gambit, il lavoro più duro potrebbe essere il miglioramento dell'analisi del flusso per riconoscere quando non è necessario inserire test "POLL" (vale a dire, questo potrebbe essere guidato dagli interrupt, sebbene il compilatore consenta di disattivare gli interrupt per alcuni usi? ); il miglioramento più semplice sarebbe implementare semplicemente un migliore utilizzo dei registri (es. riconoscere meglio i registri x86-64 anziché l'architettura x686 predefinita). Per entrambi, una migliore analisi del flusso per riconoscere che possono "deselezionare" i dati, in particolare "fixnum", "flonum" e vettori, quindi queste operazioni non sono necessarie all'interno di circuiti stretti e combinando le istruzioni di lettura / modifica / scrittura. Entrambi questi scopi potrebbero essere raggiunti usando un backend migliore come LLVM (non una quantità banale di lavoro, ma entrambi sono già in parte lì).
Conclusione: Chicken è attualmente circa il 50% più lento di 'C' sulla CPU più veloce (non il mio Bulldozer, dove ha circa la stessa velocità a causa della limitazione della cache del codice 'C') e Gambit è circa il 400% più lento (solo circa 125% più lento sul mio lento Bulldozer). Tuttavia, i futuri miglioramenti ai compilatori potrebbero ridurlo in modo che il codice non sia più lento di "C" o entro il margine specificato dall'OP.
Un linguaggio più complesso come Haskell, quando si utilizza il back-end LLVM, prestando particolare attenzione all'uso rigoroso (non un problema con lo schema che è sempre desideroso per impostazione predefinita) e utilizzando strutture dati appropriate (array unboxed ST anziché elenchi per loop stretti; in qualche modo applicabile allo Schema usando i vettori), gira alla stessa velocità di 'C' se il back-end LLVM viene usato con ottimizzazioni complete. Se può farlo, lo stesso può fare Scheme con i miglioramenti del compilatore sopra indicati.
DI NUOVO, non c'è modo che una di queste sia da 20 a 50 volte più lenta se usata con flag di ottimizzazione adeguati. END_EDIT_ADD
Naturalmente, tutti i benchmark vengono invalidati se non si utilizzano le impostazioni di ottimizzazione appropriate per ognuno come si farebbe in un ambiente di produzione ...
Penserei che il compilatore commerciale Chez Scheme sarebbe in campo per quanto riguarda la produzione di output ad alte prestazioni come Gambit e Chicken, poiché essendo commerciale ha sicuramente un "serio tempo e denaro investito in esso".
L'unico modo in cui riesco a far funzionare Gambit o Chicken con una velocità che va da "20 a 50 volte più lenta di" C "è di non utilizzare alcuna impostazione di ottimizzazione, nel qual caso spesso non funzionano molto più velocemente di quanto interpretato nelle loro REPL - 10 volte più lento rispetto all'uso corretto di tali impostazioni.
È possibile che l'OP non abbia testato usando queste impostazioni correttamente?
Se l'OP si preoccupa di chiarire le sue procedure di test, modificherò volentieri questa risposta per mostrare che almeno Gambit e Chicken non devono essere così lenti.
(declare (optimize ...))
,(declare (<type> <var))
e(the <type> <expr>)
nelle funzioni? Altrimenti non è certo un paragone equo :)