Kompilator JIT dla C, C ++ i podobnych

33

Czy istnieje jakiś kompilator just-in-time dla skompilowanych języków, takich jak C i C ++? (Pierwsze imiona, które przychodzą na myśl, to Clang i LLVM! Ale nie sądzę, że obecnie je obsługują.)

Wyjaśnienie:

Myślę, że oprogramowanie może skorzystać z informacji zwrotnych dotyczących profilowania środowiska wykonawczego i agresywnie zoptymalizowanej ponownej kompilacji punktów aktywnych w środowisku wykonawczym, nawet w przypadku języków kompilowanych z maszyną, takich jak C i C ++.

Optymalizacja sterowana profilem wykonuje podobną pracę, ale z tą różnicą JIT byłby bardziej elastyczny w różnych środowiskach. W PGO uruchamiasz swój plik binarny przed jego wydaniem. Po wydaniu nie używałby żadnych informacji zwrotnych dotyczących środowiska / danych wejściowych zebranych w czasie wykonywania. Tak więc, jeśli wzorzec wejściowy zostanie zmieniony, grozi to obniżeniem wydajności. Ale JIT działa dobrze nawet w takich warunkach.

Jednak myślę, że jest to kontrowersyjne, czy korzyści kompilacji JIT przewyższają własne koszty.

Ebrahim Mohammadi
źródło
1
Zasoby poza witryną.
DeadMG,
1
Nie jestem pewien, czy pasuje do pytania, ale dla przyszłej użyteczności uważam, że przydatny jest pakiet Cxx w języku Julii. Daje on interaktywny monit C ++ podobny do tych opisanych w odpowiedzi @ PhilippClaßen.
Antonello,
GCC 9 ma teraz kompilator jit gcc.gnu.org/onlinedocs/jit/intro/index.html
user3071643,

Odpowiedzi:

33

[Zobacz historię edycji, aby uzyskać zupełnie inną odpowiedź, która jest teraz zasadniczo przestarzała.]

Tak, istnieje kilka kompilatorów JIT dla C i / lub C ++.

CLing (jak można się domyślać z gry) jest oparty na Clang / LLVM. Działa jak tłumacz. To znaczy, dajesz mu kod źródłowy, wydajesz komendę do uruchomienia i działa. Nacisk kładziony jest przede wszystkim na wygodę i szybką kompilację, a nie maksymalną optymalizację. Jako taka, choć technicznie odpowiedź na samo pytanie, tak naprawdę nie odpowiada to celom PO.

Inną możliwością jest NativeJIT . To pasuje nieco do pytania. W szczególności nie akceptuje kodu źródłowego C lub C ++, kompiluje go i wykonuje. Jest to raczej mały kompilator, który można skompilować w programie C ++. Akceptuje wyrażenie, które jest zasadniczo wyrażone jako EDSL w twoim programie C ++ i generuje z niego rzeczywisty kod maszynowy, który następnie możesz wykonać. To znacznie lepiej pasuje do frameworka, w którym można skompilować większość programu za pomocą normalnego kompilatora, ale ma kilka wyrażeń, których nie poznasz do czasu wykonania, które chcesz wykonać z czymś, co zbliża się do optymalnej szybkości wykonania.

Jeśli chodzi o pozorną intencję pierwotnego pytania, myślę, że podstawowy punkt mojej pierwotnej odpowiedzi nadal jest zasadny: podczas gdy kompilator JIT może dostosowywać się do takich rzeczy, jak dane, które różnią się od jednego wykonania do drugiego, a nawet zmieniają się dynamicznie podczas jednego wykonania, w rzeczywistości jest to stosunkowo niewielka różnica, przynajmniej z zasady. W większości przypadków uruchomienie kompilatora w czasie wykonywania oznacza, że ​​musisz zrezygnować z dość dużej optymalizacji, więc najlepszym, na co zwykle masz nadzieję, jest to, że jest on tak szybki, jak mógłby to zrobić konwencjonalny kompilator.

Chociaż można postulować sytuacje, w których informacje dostępne dla kompilatora JIT mogłyby pozwolić mu wygenerować znacznie lepszy kod niż konwencjonalny kompilator, przypadki tego typu w praktyce wydają się dość niezwykłe (iw większości przypadków, w których udało mi się zweryfikować tak się dzieje, tak naprawdę było to spowodowane problemem w kodzie źródłowym, a nie statycznym modelem kompilacji).

Jerry Coffin
źródło
1
Dlaczego JIT nie zapisuje pliku przypominającego pamięć podręczną, aby pominąć ponowne uczenie się od zera?
JohnMudd
3
@JohnMudd: Podejrzewam, że rozumowanie to bezpieczeństwo. Na przykład zmodyfikuj buforowany kod, a następnie przy następnym uruchomieniu maszyny wirtualnej wykona kod, który tam umieściłem, zamiast tego, co tam napisał.
Jerry Coffin
4
OTOH, jeśli możesz modyfikować pamięci podręczne, możesz także modyfikować pliki źródłowe.
user3125367,
1
@ user3125367: Tak, ale w wielu przypadkach kompilator sprawdza różne typy i może zostać ominięty, jeśli załadujesz skompilowany kod bezpośrednio z pamięci podręcznej. Zależy oczywiście od JIT - Java wykonuje wiele operacji wymuszania podczas ładowania (skompilowanego) pliku .class, ale wiele innych robi o wiele mniej (prawie wcale, w wielu przypadkach).
Jerry Coffin,
11

Tak, istnieją kompilatory JIT dla C ++. Z czystego punktu widzenia wydajność, myślę, że profilowana optymalizacja (PGO) jest nadal lepsza.

Nie oznacza to jednak, że kompilacja JIT nie jest jeszcze stosowana w praktyce. Na przykład Apple używa LLVM jako JIT dla potoku OpenGL. Jest to domena, w której masz znacznie więcej informacji w czasie wykonywania, których można użyć do usunięcia dużej ilości martwego kodu.

Inną interesującą aplikacją JIT jest Cling, interaktywny interpreter C ++ oparty na LLVM i Clang: https://root.cern.ch/cling

Oto przykładowa sesja:

[cling]$ #include <iostream>
[cling]$ std::cout << "Hallo, world!" << std::endl;
Hallo, world!
[cling]$ 3 + 5
(int const) 8
[cling]$ int x = 3; x++
(int) 3
(int const) 3
[cling]$ x
(int) 4

Nie jest to projekt zabawkowy, ale w rzeczywistości jest wykorzystywany na przykład w CERN-ie do opracowania kodu dla dużego zderzacza hadronów.

Philipp Claßen
źródło
7

C ++ / CLI jest odrzucony. To prawda, że ​​C ++ / CLI nie jest C ++, ale jest całkiem blisko. To powiedziawszy, JIT Microsoftu nie robi super sprytnych / uroczych rodzajów optymalizacji opartych na zachowaniu środowiska uruchomieniowego, o które pytasz, przynajmniej o ile mi wiadomo. To naprawdę nie pomaga.

http://nestedvm.ibex.org/ zmienia MIPS w kod bajtowy Java, który zostałby następnie odrzucony. Problem z tym podejściem z twojego pytania polega na tym, że wyrzucasz wiele przydatnych informacji, zanim dotrze do JIT.

Logan Capaldo
źródło
2

Po pierwsze, zakładam, że chcesz jit śledzenia, a nie jit metody.

Najlepszym podejściem byłoby skompilowanie kodu do llvm IR, a następnie dodanie kodu śledzenia przed utworzeniem natywnego pliku wykonywalnego. Gdy blok kodu zostanie wystarczająco dobrze wykorzystany i gdy zgromadzona zostanie wystarczająca ilość informacji o wartościach zmiennych (nie typach jak w językach dynamicznych), kod może zostać ponownie skompilowany (z IR) za pomocą osłon opartych na wartościach zmiennych.

Wydaje mi się, że pamiętam pewne postępy w tworzeniu jit ac / c ++ w clangu pod nazwą libclang.

dan_waterworth
źródło
1
AFAIK, libclang to większość funkcji clang uwzględnionych w bibliotece. możesz więc użyć go do analizy kodu źródłowego, aby stworzyć wyrafinowane kolorowanie składni, wskazówki, przeglądanie kodu itp.
Javier
@Javier, to brzmi dobrze. Myślę, że w bibliotece była funkcja, która pobierała const char * kodu źródłowego i produkowała llvm ir, ale teraz myślę, że prawdopodobnie lepiej jest używać jit w oparciu o ir niż źródło.
dan_waterworth