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.
Kompilator
Tłumaczy cały program na plik maszynowy przed pierwszym uruchomieniem.
1. Kod źródłowy (C++)
2. Proces kompilacji
3. Kod maszynowy (uproszczony)
4. Plik wykonywalny
program.exe
Gotowy do uruchomienia bezpośrednio przez procesor.
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ł.