edulab

Kompilator i interpreter

Dwa sposoby na to, by tekst, który napisałeś, zaczął coś robić: skompilować raz i wypuścić na wolność albo czytać linijka po linijce i od razu odpowiadać za wynik.

Kod źródłowy nie jest jeszcze programem. Jest opisem programu, tekstem w języku, który ludzie umieją czytać, a procesor nie. Żeby cokolwiek się wydarzyło, ten opis trzeba zamienić na liczby, które procesor potrafi wykonać. Ta zamiana, czyli tłumaczenie z języka wysokopoziomowego na maszynowy, może nastąpić w dwóch zupełnie różnych momentach.

Można ją zrobić raz, długo i dokładnie, dostając w rezultacie samodzielny plik gotowy do błyskawicznego uruchomienia. Albo raz po raz, na bieżąco, czytając kod linia po linii i wykonując go natychmiast. Pierwszy sposób to kompilator. Drugi - interpreter. Niżej dwie symulacje pokazują różnicę krok po kroku.

Dwie ścieżki wykonania

Po lewej kompilator C++ zamienia kod na plik wykonywalny w pięciu fazach. Po prawej interpreter Pythona czyta kod linia po linii i wykonuje go w locie. Uruchom obie i porównaj czasy.

C++ · Python Interaktywne

Kompilator

Tłumaczy cały program na plik maszynowy przed pierwszym uruchomieniem.

1. Kod źródłowy (C++)

2. Proces kompilacji

01Analiza leksykalna
02Parsowanie
03Analiza semantyczna
04Optymalizacja
05Generowanie kodu

5. Wyjście wykonania

Interpreter

Czyta i wykonuje kod źródłowy linia po linii, bezpośrednio.

1. Kod źródłowy (Python)

2. Interpretacja i wykonanie

Interpreter w stanie spoczynku.

3. Wyjście wykonania

Skąd ta różnica

Kompilator i interpreter robią to samo: zamieniają abstrakcyjny tekst kodu na czyny, czyli liczby zapisywane do pamięci, instrukcje wykonywane przez procesor i znaki rysowane na ekranie. Różnią się momentem, w którym to robią, i tym, ile pracy wykonują na zapas.

Kompilator robi tłumaczenie raz, długo i porządnie. Może sobie pozwolić na optymalizacje, bo wynik jego pracy będzie używany wielokrotnie. Interpreter tłumaczy w biegu, krócej i pobieżniej, bo nie wie, czy ten sam kawałek kodu zaraz nie zostanie zastąpiony przez inny.

Co z tego wynika dla nas

Programy skompilowane (C, C++, Rust, Go) uruchamiają się błyskawicznie, bo cała praca tłumaczeniowa jest już wykonana, a procesor dostaje gotowe rozkazy. Programy interpretowane (Python, Ruby, JavaScript w starszych przeglądarkach) uruchamiają się wolniej, bo równolegle dzieją się dwie rzeczy: tłumaczenie i wykonanie. W zamian programista dostaje swobodę: może zmieniać kod między kolejnymi uruchomieniami bez ponownej kompilacji.

Granica nie jest jednak tak ostra, jak ją tu narysowaliśmy. Współczesne silniki JIT kompilują interpretowane programy w trakcie ich działania, tylko te fragmenty, które są wykonywane wystarczająco często, by się to opłacało. To jednak temat na osobny artykuł.