Compilazione e compilazione: differenze tra compilatore e interprete

📖 15min read

Le macchine non conoscono l’inglese

Quando ho imparato per la prima volta la lingua C a scuola, c’era qualcosa che non capivo molto. “Perché il mio codice non viene eseguito subito, ma deve passare attraverso il fastidioso processo di creazione e compilazione?”

Python si avvia subito quando scrivi il codice e premi Invio, ma Java e C richiedono passaggi che richiedono molto tempo. Quando ero uno studente universitario, pensavo semplicemente: “Ogni lingua ha una grammatica diversa”. Quando ti presentavi al test, tutto quello che dovevi fare era rispondere “C è un linguaggio compilato, Python è un linguaggio interpretato”.

Tuttavia, mentre lavoravo con server back-end che nella pratica gestiscono grandi quantità di traffico, mi sono reso conto che questa semplice differenza rappresentava un’enorme barriera che determinava le prestazioni e la velocità di implementazione del sistema. L’elegante codice che ho scritto era, in effetti, solo un linguaggio estraneo sconosciuto al lavoratore ignorante e coscienzioso chiamato CPU.

La traduzione è necessaria tra il linguaggio che usiamo e il linguaggio che usano le macchine.

Che diavolo è “Costruisci”?

Che cos’è esattamente una build? In cosa differisce dal semplice “risparmio”?

Il codice sorgente che abbiamo scritto (.java, .c) è in realtà solo un file di testo. È un “testo” leggibile anche se aperto con il blocco note. Tuttavia, i computer (CPU) non sanno leggere. Puoi solo sapere se l’elettricità scorre (1) o meno (0).

Build è un processo di creazione di pacchetti completo che converte il “file di testo” che abbiamo scritto in un “file eseguibile (.exe, .class, .jar) che può essere eseguito da un computer.

In altre parole, se il codice sorgente è una “ricetta di cucina (carta)”, la creazione è il processo di taglio e frittura degli ingredienti secondo la ricetta e di servizio di un “piatto finito (cibo)”. Non importa quanto modifichi la ricetta (codice), se non la cucini (costruisci) di nuovo, il cibo freddo che hai preparato ieri rimane sul tavolo (server).

Istruzioni di lavoro per il centro di distribuzione digitale

Per comprendere questo processo complesso, confronta l’interno di un computer con un gigantesco centro logistico digitale.

Qui, la CPU è un lavoratore incredibilmente veloce, ma con flessibilità pari a zero. Questo lavoratore può leggere solo istruzioni di lavoro chiamate linguaggio macchina (0 e 1).

L’atto di scrivere codice in Java o Python è il processo di creazione di un “manuale di lavoro” da fornire ai lavoratori. Ma il problema è che scriviamo questo manuale in inglese (linguaggio di programmazione). L’operaio non conosce l’inglese. Quindi abbiamo bisogno di traduttori. Il destino di una lingua dipende da questo metodo di traduzione.

1. Compilatore: traduttore professionista che traduce in anticipo

2. Interprete: interprete simultaneo in tempo reale

È la differenza tra tradurre in anticipo (velocità) e interpretare al volo (flessibilità).

[Verifica del codice] Verifica del linguaggio macchina

Non riesco davvero a sentirlo solo ascoltandolo. Controlliamo visivamente se Python sta davvero traducendo riga per riga e come appare il codice che la macchina vede.

Python ha un modulo chiamato dis (Disassembler). Usandolo, puoi vedere quali istruzioni vengono suddivise internamente nel codice Python quando viene eseguito.

import dis

def my_function():
    a = 10
    b = 20
    print(a + b)

# Verifica in quale codice macchina (bytecode) viene convertito il codice Python
print("--- Python Bytecode Verification ---")
dis.dis(my_function)

Quando esegui questo codice, vengono emesse le seguenti lingue aliene.

  5           0 LOAD_CONST               1 (10)
              2 STORE_FAST               0 (a)

  6           4 LOAD_CONST               2 (20)
              6 STORE_FAST               1 (b)

  7           8 LOAD_GLOBAL              0 (print)
             10 LOAD_FAST                0 (a)
             12 LOAD_FAST                1 (b)
             14 BINARY_ADD
             16 CALL_FUNCTION            1
             18 POP_TOP
             20 LOAD_CONST               0 (None)
             22 RETURN_VALUE

Analisi:

D’altra parte, una volta compilato, il linguaggio C viene tradotto direttamente nel linguaggio assembly della CPU, come MOV EAX, 10 (inserisci 10 nel registro EAX). Dal momento che non ci sono quadri intermedi, non ha altra scelta che essere veloce.

Compromesso in pratica: quale scegliere?

Quando ero uno studente universitario, sentirmi a mio agio era la cosa migliore. Piuttosto che il linguaggio C, che presenta errori di compilazione, ho preferito Python o JavaScript, che possono essere scritti ed eseguiti facilmente. Tuttavia, in pratica, è necessario trovare un compromesso tra stabilità e produttività.

1. Paura di errori di runtime (debolezza dell’interprete)La cosa più spaventosa quando si costruisce un server con Python è che il server si ferma alle 3 del mattino a causa di un singolo errore di battitura. L’interprete non è a conoscenza dell’errore finché non viene eseguito (il punto in cui viene eseguito il codice). D’altra parte, Java (linguaggio compilato) chiede: “Ho fatto un errore di battitura qui?” quando si costruisce il codice. E me lo dice. Questo rigore nell’individuare gli errori prima della distribuzione diventa un salvatore nei progetti su larga scala.

2. Tempo di compilazione noioso (debolezza del compilatore) D’altra parte, quando un progetto Java cresce, potrebbero essere necessari diversi minuti per modificare e controllare una singola riga di codice (Gradle Build…). Questo è il motivo per cui Python viene utilizzato in modo predominante nelle prime fasi delle startup, dove modifiche e implementazioni rapide sono fondamentali, o in attività che richiedono risultati immediati, come l’analisi dei dati.

Un insegnante severo (compilatore) è sicuro ma frustrante, mentre un amico gentile (interprete) è a suo agio ma a volte trascura gli errori.

Conclusione: la teoria diventa un’arma

Ora sappiamo che il processo di “creazione e compilazione” e il modo in cui il codice viene tradotto varia da lingua a lingua. In teoria, scegliere il linguaggio migliore in base alla natura del tuo progetto sarebbe la risposta giusta.

Ma la realtà non è stata così clemente. L’azienda in cui sono entrato disponeva già di uno stack tecnologico definito e, sebbene fossi entrato come sviluppatore Java, ho dovuto toccare Python, JavaScript e persino Android XML.

Paradossalmente, ciò che mi ha salvato da questo campo confuso di “sviluppo onnivoro” sono state queste “teorie di base” che ho imparato oggi. Questo perché sebbene l’involucro del linguaggio sia diverso, i principi che operano al suo interno sono gli stessi.

La prossima volta parlerò della mia esperienza pratica su come mi sono “immerso in profondità in una lingua (Java) per indirizzare altre lingue” e perché le nozioni di base sono importanti per evitare di diventare schiavo del framework.

Lascia un commento