2009-11-26 16 views
18

Quali sono i motivi tecnici per cui linguaggi come Python e Ruby sono interpretati (fuori dalla scatola) anziché compilati? A me sembra che non dovrebbe essere troppo difficile per le persone ben informate in questo settore per rendere queste lingue non interpretabili come lo sono oggi, e vedremmo significativi miglioramenti nelle prestazioni. Quindi certamente mi manca qualcosa.Perché è interpretato (python | ruby)?

+2

Il fatto che (per lo meno Python) abbia avuto più di un progetto di compilazione in corso per anni, e che non è ancora abbastanza, dovrebbe sobriamente le tue aspettative di "non essere troppo difficile", immagino. –

+5

In realtà, penso che Python compili il codice della macchina virtuale, simile a .NET e Java. Ad ogni modo, penso perché i realizzatori linguistici rendono la scelta dell'interpretazione o della compilazione una buona domanda. Potresti voler controllare http://stackoverflow.com/questions/475223/what-is-the-difference-between-implementing-a-compiler-and-an-interpreter –

+2

Potresti essere interessato a questa risposta: http : //stackoverflow.com/questions/376611/why-interpreted-langs-are-mostly-ducktyped-while-compiled-have-strong-typing/376828#376828 –

risposta

1

In base alla progettazione.

Gli autori desideravano qualcosa su cui scrivere gli script.

Python viene compilato la prima volta che viene eseguito se

+0

ovvero "byte compilato" (to .pyc) la prima volta è eseguito. Ciò accelera solo i tempi di caricamento delle esecuzioni future. Penso che il richiedente stia parlando di essere compilato con codice macchina nativo. – overthink

+0

Anche Perl ... sospiro, nessun corpo chiede di Perl. – dlamblin

+1

"Gli autori desideravano qualcosa su cui scrivere gli script."? Questa affermazione mi confonde. L'idoneità dello scopo per determinati compiti di una data lingua ha molto meno a che fare con il suo ciclo di scrittura/(compilazione, collegamento)/esecuzione rispetto alla ricchezza di una libreria standard, gestione della memoria e modello di tipo. Ciò probabilmente otterrebbe un certo supporto per la scrittura di script anche in Java, se non ci fosse l'altra piccola considerazione di quanto sovraccarico ci vuole per avviare la macchina virtuale ... –

2

Beh, non è uno dei punti di forza di queste lingue che sono così facilmente script? Non lo sarebbero se fossero compilati. E d'altra parte, le lingue dinamiche sono più facili da interpretare che da compilare.

6

Penso che il motivo principale per cui le lingue vengono interpretate sia la portabilità. Come programmatore è possibile scrivere codice che verrà eseguito in un interprete, non in un sistema operativo specifico. Quindi i tuoi programmi si comportano in modo più uniforme su tutte le piattaforme (più che con linguaggi compilati). Un altro vantaggio che posso pensare è che è più semplice avere un sistema di tipo dinamico in un linguaggio interpretato. Penso che i creatori della lingua pensassero di avere un linguaggio in cui i programmatori possono essere più produttivi grazie alla gestione automatica della memoria, al sistema di tipo dinamico e alla meta-programmazione che vince su qualsiasi perdita di prestazioni dovuta all'interpretazione del linguaggio. Se sei preoccupato per le prestazioni, puoi sempre compilare la lingua su codice macchina nativo utilizzando una tecnica come la compilation JIT.

+0

Non sono sicuro che l'argomento della portabilità sia valido. Se decidi tu il tuo lexer/AST-builder dal tuo back-end di generazione del codice, puoi variare i due in modo indipendente (che è in qualche modo come gcc è architettato). Quindi non vedo che sia la ragione. Il vostro punto su un sistema di tipo dinamico che è più facile da implementare in un linguaggio interpretato è solido. In effetti, qualsiasi sistema di tipo dinamico richiede il supporto al runtime (tramite un interprete o una libreria di runtime) praticamente per definizione. –

+0

L'argomento della portabilità in realtà non è del tutto sano. Le lingue interpretate non sono più facili da trasportare di quelle compilate, in generale. L'unica ragione per cui potrebbe * sembrare * più facile portarlo è perché puoi scrivere la tua porta di lingua interpretata senza capire la macchina sottostante, ma questa è solo una questione di competenza, non di difficoltà. –

+0

voi ragazzi avete ragione..questo è uscito tutto sbagliato! Ho intenzione di aggiornare la mia risposta per dire cosa stavo veramente pensando. – neesh

2

In un linguaggio compilato, il ciclo si entra in quando si effettua il software è

  1. fare un cambiamento
  2. compila modifiche
  3. determinazione Cambiamenti
  4. goto 1

linguaggi interpretati tendono ad essere più veloci per fare roba perché si taglia il secondo passo di quel processo (e quando si ha a che fare con un grande sistema in cui i tempi di compilazione possono essere al rialzo di due minuti, il secondo passaggio può aggiungere una quantità significativa di tempo).

Questa non è necessariamente la ragione per cui i progettisti di python | ruby ​​hanno pensato, ma tieni presente che "Quanto è efficiente la macchina?" è solo la metà del problema di sviluppo del software.

Sembra anche che sarebbe più facile compilare il codice in un linguaggio che è interpretato in modo naturale rispetto a quello di aggiungere un interprete a un linguaggio compilato per impostazione predefinita.

32

diversi motivi:

  • più veloce del ciclo di sviluppo, write-test vs write-compile-link-test
  • più facile da organizzare per comportamento dinamico (riflessione, metaprogrammazione)
  • marche l'intero sistema portatile (basta ricompilare il codice C sottostante e si è pronti per andare su una nuova piattaforma)

Pensa a cosa accadrebbe se il sistema fosse non interpretato. Supponiamo che tu abbia usato translation-to-C come meccanismo. Il codice compilato dovrebbe periodicamente verificare se fosse stato sostituito da metaprogrammazione. Una situazione simile si presenta con le funzioni di tipo eval(). In questi casi, sarebbe necessario eseguire nuovamente il compilatore, un processo oltraggiosamente lento, oppure dovrebbe anche avere comunque l'interprete in fase di esecuzione.

L'unica alternativa qui è un compilatore JIT. Questi sistemi sono molto complessi e sofisticati e hanno impronte di run time ancora più grandi di tutte le altre alternative. Si avviano molto lentamente, rendendoli poco pratici per la sceneggiatura. Hai mai visto uno script Java? Non ho.

Quindi, si hanno due scelte:

  • tutti gli svantaggi sia di un compilatore e un interprete
  • solo gli svantaggi di un interprete

Non è sorprendente che in genere l'attuazione primario va solo con la seconda scelta. È abbastanza probabile che un giorno potremmo vedere implementazioni secondarie come i compilatori che appaiono. Ruby 1.9 e Python hanno una VM bytecode; quelli sono & frac12; Un compilatore potrebbe mirare solo al codice non dinamico, oppure potrebbe avere vari livelli di supporto linguistico dichiarabili come opzioni. Ma poiché una cosa del genere non può essere l'implementazione primaria, rappresenta molto lavoro per un beneficio molto marginale. Ruby ha già 200.000 righe di C in esso ...

Suppongo che dovrei aggiungere che si può sempre aggiungere un'estensione compilata C (o, con qualche sforzo, qualsiasi altra lingua). Quindi, diciamo che hai una lenta operazione numerica. Se si aggiunge, ad esempio Array#newOp con un'implementazione C, si ottiene la velocità, il programma rimane in Ruby (o qualsiasi altra cosa) e l'ambiente ottiene un nuovo metodo di istanza. Tutti vincono! Quindi questo riduce la necessità di un'implementazione secondaria problematica.

+5

"Mai visto uno script Java? Non ho." Ha! Quindi i tuoi punti reputazione potrebbero essere 30 volte superiori ai miei, ma anche io ho sentito parlare di JavaScript! – mtyaka

+4

Hehe, va bene. In realtà, imparo molto su SO, e so che ho ancora molto da imparare. Ma, nonostante l'inglese-pun, (1) per "script" intendevo "uno script eseguito da shell, come in bash o perl"; (2) Non ho mai visto uno di quelli in JS, e (3) JS non ha praticamente nulla a che fare con Java. – DigitalRoss

+3

@mtyaka: Chiaramente intende "script scritto in Java", non "Javascript", che è una lingua completamente diversa. @DigitalRoss: in realtà, javascript sta guadagnando popolarità come linguaggio di scripting non Web con l'avvento di interpreti indipendenti come V8 e SquirrelFish. –

5

Oggi non esiste più una netta distinzione tra linguaggi "compilati" e "interpretati". Python è infatti compilato così come Java è, le uniche differenze sono:

  • Il compilatore Python è molto più veloce rispetto al compilatore Java
  • Python compila automaticamente il codice sorgente di come viene eseguito, non v'è alcuna separata "compila" richiesto
  • bytecode Python passo è differente dalla JVM bytecode

Python ha anche una funzione chiamata compile() che è un'interfaccia al compilatore.

Sembra che la distinzione che si sta facendo è tra i linguaggi "tipizzati dinamicamente" e quelli "tipizzati staticamente". In linguaggi dinamici come Python, è possibile scrivere codice simile:

def fn(x, y): 
    return x.foo(y) 

Si noti che i tipi di x e y non sono specificati. In fase di runtime, questa funzione vedrà x per vedere se ha una funzione membro denominata foo e, in tal caso, la chiamerà con . In caso contrario, genererà un errore di runtime che indica che tale funzione non è stata trovata. Questo tipo di ricerca runtime è molto più semplice da rappresentare utilizzando una rappresentazione intermedia come bytecode, in cui una VM di runtime esegue la ricerca invece di dover generare codice macchina per eseguire la ricerca stessa (o, chiamare una funzione per effettuare la ricerca che è ciò che il bytecode farà comunque).

Python ha progetti come Psyco, PyPy, e Unladen Swallow che prendono diversi approcci per la compilazione di codice oggetto Python in qualcosa di più vicino al codice nativo. C'è una ricerca attiva in questo settore ma non c'è (ancora) una risposta semplice.

16

Esattamente come (nella tipica implementazione di) Java o C#, Python viene prima compilato in qualche forma di bytecode, a seconda dell'implementazione (CPython usa una sua forma speciale, Jython usa JVM proprio come un tipico Java, IronPython utilizza CLR proprio come un tipico C#, e così via) - che bytecode viene ulteriormente elaborato per essere eseguito da una macchina virtuale (interprete AKA), che può anche generare codice macchina "just in time" - noto come JIT - se e quando giustificato (le implementazioni CLR e JVM spesso lo fanno, la macchina virtuale di CPython in genere non può, ma può essere fatta per farlo, ad esempio con psyco o Unladen Swallow).

JIT può pagare per se stessa per i programmi sufficientemente lunghi in esecuzione (se il modo di memoria in meno rispetto cicli di CPU), ma non può (a causa di volte più lento di avvio e di memoria più grande impronta), soprattutto quando i tipi hanno anche dedurre o specializzato come parte della generazione del codice. Generare codice macchina senza inferenza di tipo o specializzazione è facile se è ciò che si desidera, ad es. freeze lo fa per te, ma in realtà non presenta i vantaggi che gli attribuiscono i "feticisti del codice macchina". Ad esempio, si ottiene un binario eseguibile da 1,5 a 2 MB al posto di un piccolo "mondo ciao" .pyc - non molto utile! -). Tale eseguibile è autonomo e distribuibile in quanto tale, ma funzionerà solo su una gamma molto specifica di sistemi operativi e architetture CPU, quindi i compromessi sono abbastanza incerti nella maggior parte dei casi. E il tempo necessario per preparare l'eseguibile è piuttosto lungo, quindi sarebbe una scelta pazzesca rendere quella modalità operativa quella predefinita.

8

Sostituire semplicemente un interprete con un compilatore non ti darà un grande incremento di prestazioni come potresti pensare per un linguaggio come Python. Quando la maggior parte del tempo è effettivamente spesa nelle ricerche simboliche dei membri degli oggetti nei dizionari, non importa se la chiamata alla funzione che esegue tale ricerca è interpretata, o è codice macchina nativo - la differenza, anche se non del tutto trascurabile, sarà sminuita guardando in alto.

Per migliorare davvero le prestazioni, è necessario ottimizzare i compilatori. E qui le tecniche di ottimizzazione sono molto diverse da ciò che si ha con C++, o anche Java JIT - un compilatore ottimizzante per un dattiloscritto/dattiloscritto come Python deve fare qualche inferenza di tipo molto creativo (incluso il probabilistico - cioè "90% di possibilità di esso è T "e quindi genera un codice macchina efficiente per quel caso con un controllo/ramo prima di esso) e analisi di fuga. Questo è difficile.

5

Lo sforzo richiesto per creare un buon compilatore per generare codice nativo per una nuova lingua è scaglionando.I piccoli gruppi di ricerca richiedono in genere da 5 a 10 anni (esempi: SML/NJ, Haskell, Clean, Cecil, lcc, Objective Caml, MLton e molti altri). E quando la lingua in questione richiede il controllo dei tipi e altre decisioni da eseguire in fase di esecuzione, uno scrittore di compilatori deve lavorare molto più duramente per ottenere buone prestazioni del codice nativo (per un esempio eccellente, vedere il lavoro di Craig Chambers e più tardi Urs Hoelzle su Se stesso). I guadagni in termini di prestazioni che potresti sperare sono più difficili da realizzare di quanto potresti pensare. Questo fenomeno spiega in parte why so many dynamically typed languages are interpreted.

come noto, un interprete di decente è anche immediatamente portatile, mentre il porting compilatori di nuove architetture di macchine richiede uno sforzo notevole (ed è un problema io personalmente ho lavorato per oltre 20 anni, con un po 'di tempo libero per buona condotta). Quindi un interprete è un modo per raggiungere rapidamente un vasto pubblico.

Infine, anche se esistono veloci compilatori e interpreti lenti, è solitamente più facile eseguire il ciclo di modifica-trasloco-vai più velocemente utilizzando un interprete. (Per alcuni bei esempi di compilatori veloci vedere il suddetto lcc così come go compilatore di Ken Thompson. Per un esempio di un interprete relativamente lento vedere GHCi.

2

REPL. Non bussare 'fino a che hai provato. :)

+1

SML/NJ ha offerto REPL compilato con codice nativo per oltre 20 anni ... come molti sistemi Lisp. –

1

Compilare Ruby è notoriamente difficile. Sto lavorando su uno, e come parte di ciò ho scritto un post sul blog enumerating some of the issues here.

In particolare, Ruby soffre di un confine molto poco chiaro (cioè inesistente) tra la fase "letta" e "esegui" del programma che rende difficile la compilazione efficiente. Potresti semplicemente emulare quello che fa l'interprete, ma non vedrai molta accelerazione, quindi non ne varrebbe la pena. Se vuoi compilarlo in modo efficiente, devi affrontare molte complicazioni aggiuntive per gestire l'estremo livello di dinamismo in Ruby.

La buona notizia è che lo sono le tecniche per il superamento di questo. Self, Smalltalk e Lisp/Scheme hanno affrontato con successo la maggior parte degli stessi problemi. Ma ci vuole tempo per setacciarlo e capire come farlo funzionare con Ruby. Inoltre, non aiuta che Ruby abbia una grammatica molto contorta.

1

Le prestazioni di calcolo non sono probabilmente un obiettivo della maggior parte delle lingue interpretate. Le lingue interpretate sono in genere più preoccupate della produttività del programmatore rispetto alla velocità non elaborata. Nella maggior parte dei casi queste lingue sono abbastanza veloci per le attività che le lingue sono state progettate per affrontare.

Dato che, e quasi gli unici vantaggi di un compilatore sono la verifica dei tipi (difficile da fare in un linguaggio dinamico) e la velocità, non c'è molto incentivo a scrivere i compilatori per la maggior parte delle lingue interpretate.

Problemi correlati