LuaJIT 2 poradnik optymalizacyjny

Szukam dobrego poradnika Jak zoptymalizować Kod Lua dla LuaJIT 2. Powinien skupić się na specyfikacjach LJ2, takich jak wykrywanie, które ślady są kompilowane, a które nie, itp.

Jakieś wskazówki? Zbieranie linków do postów Lua ML byłoby odpowiedzią (punkty bonusowe za podsumowanie tych linków tutaj.)

Aktualizacja: zmieniłem tekst tytułowy z przewodnika "profilowanie" na "optymalizacja", ponieważ ma to większy sens.

Author: hippietrail, 2011-08-24

3 answers

Update

Mike niedawno stworzył i wydał wspaniały lekki profiler dla LuaJIT, można go znaleźć tutaj .

Update

Wiki zyskała jeszcze kilka stron w tym obszarze, szczególnie TA , która zawiera szczegóły niektórych dodatkowych rzeczy nie wymienionych w oryginalnej odpowiedzi, i jest oparta na liście dyskusyjnej Post autorstwa Mike ' a.


LuaJIT niedawno uruchomił własną wiki i listę dyskusyjną , a z takie rzeczy przychodzi wiele, wiele więcej klejnotów o przyspieszeniu kodu dla LuaJIT.

W tej chwili wiki jest dość cienka (ale zawsze szuka ludzi, aby do niej dodać), jednak jedna świetna strona, która została ostatnio Dodana, to lista funkcji NYI. Funkcje NYI powodują, że JIT wycofuje się i wraca do interpretera, więc całkiem oczywiste jest, że należy unikać funkcji NYI w miarę możliwości na hotpath, szczególnie w pętlach.

Niektóre tematy interesujące z mailingu lista:

I po prostu powtórzyć to, co powiedział dalej (bo to tylko, że pomocne), -jv jest najlepszym narzędziem do dostrajania wydajności, powinien być również pierwszym przystankiem podczas rozwiązywania problemów.


Oryginalna ODPOWIEDŹ

Wątpię, że wiele na ten temat znajdziesz, głównie dlatego, że LJ2 jest jeszcze w wersji beta, a tak więc większość profili jest wykonywana naiwnie, ponieważ nie ma haczyków debugujących dla konkretnych rzeczy LJ2, takich jak rejestrator śladów.

Na plus, nowy moduł FFI umożliwia bezpośrednie połączenia do timerów o wysokiej rozdzielczości (lub profilowanie API, takich jak VTune / CodeAnalyst), można profilować w ten sposób, ale wszystko więcej wymaga rozszerzeń do rdzenia JIT LJ2, co nie powinno być zbyt trudne, ponieważ kod jest jasny i skomentowany.


One The Trace recorder command line params (wzięte z tutaj):

Polecenia-jv i-jdump są modułami rozszerzeń napisanymi w Lua. Oni są używane głównie do debugowania samego kompilatora JIT. Na opis ich opcji i formatu wyjściowego, proszę przeczytać blok komentarzy na początku ich źródła. Można je znaleźć w katalog lib dystrybucji źródłowej lub zainstalowany pod jit katalog. Domyślnie jest to / usr / local / share / luajit-2.0.0-beta8/JIT w systemach POSIX.

Które oznacza, że możesz użyć kodu modułu z poleceń do utworzenia modułu profilującego dla LuaJIT 2.


Update

Wraz z aktualizacją pytania, odpowiedź na to pytanie staje się nieco łatwiejsza. Zacznijmy więc od źródła, LuaJIT.org:

Przed ręczną optymalizacją kodu, zawsze dobrym pomysłem jest sprawdzenie zasobów optymalizacji JIT:

Kompilacja

Ze strony Running możemy zobaczyć wszystkie spośród opcji ustawiania parametrów JIT, dla optymalizacji skupiamy się na opcji -O. Natychmiast Mike mówi nam, że włączenie wszystkich optymalizacji ma minimalny wpływ na wydajność, więc upewnij się, że uruchomisz -O3 (która jest teraz domyślna), więc jedynymi opcjami o rzeczywistej wartości dla nas są progi JIT i Trace.

Te opcje są bardzo specyficzne dla kodu, który piszesz, więc nie ma ogólnych "optymalnych ustawień" oprócz domyślnych, ale nie trzeba dodawać, że jeśli twój kod ma wiele pętli, eksperymentuj z rozwijaniem pętli i czasem wykonania (ale przepuść pamięć podręczną między każdym uruchomieniem, jeśli szukasz wydajności zimnego startu).

-jv jest również przydatny w pomaganiu uniknąć wiedzieć problemy / "fallbacks" , które spowodują JIT do bailout.

Strona sama w sobie nie oferuje zbyt wiele na temat tego, jak napisać lepszy lub bardziej zoptymalizowany kod, z wyjątkiem kilku drobnych ciekawostek w FFI tutorial :

Funkcja Buforowanie

Buforowanie funkcji jest dobrym wzmacniaczem wydajności w Lua, ale mniej ważne, aby skupić się na LuaJIT, ponieważ JIT sam wykonuje większość tych optymalizacji, to jest ważne, aby pamiętać, że buforowanie funkcji FFI C jest złe , preferowane jest buforowanie przestrzeni nazw, w której znajdują się.

Przykład ze strony:

Zły:

local funca, funcb = ffi.C.funcb, ffi.C.funcb -- Not helpful!
local function foo(x, n)
  for i=1,n do funcb(funca(x, i), 1) end
end

Dobre:

local C = ffi.C          -- Instead use this!
local function foo(x, n)
  for i=1,n do C.funcb(C.funca(x, i), 1) end
end

Problemy z wydajnością FFI

Status sekcja zawiera informacje o różnych konstrukcjach i operacjach, które obniżają wydajność kodu (głównie dlatego, że nie są kompilowane, ale zamiast tego używają zapasowej maszyny wirtualnej).

Teraz przechodzimy do źródła wszystkich klejnotów LuaJIT, Lista dyskusyjna Lua :

  • Unikanie wywołań C i NYI Lua w pętlach: jeśli chcesz, aby znacznik LJ2 zadziałał i dał przydatne informacje zwrotne, musisz unikać funkcji NYI (jeszcze nie zaimplementowanych) lub wywołań C, w których śledzenie kompilator nie może iść. Więc jeśli masz jakieś małe wywołania C, które mogą być importowane do lua i są używane w pętlach, zaimportuj je, w najgorszym wypadku mogą być '6% wolniejsze' niż implementacja kompilatora C, w najlepszym wypadku jest szybsze.

  • Używaj tablic liniowych przez ipairs: Według Mike ' a, pairs / next zawsze będzie wolniejszy w porównaniu do innych metod (jest tam również mała ciekawostka o buforowaniu symboli dla znacznika).

  • Unikaj zagnieżdżonych pętli : każda poziom zagnieżdżania wymaga dodatkowego przejścia do śledzenia i będzie nieco mniej zoptymalizowany, w szczególności unikaj pętli wewnętrznych z niższymi iteracjami.

  • Możesz używać tablic 0-bazowych: Mike mówi tutaj, że LuaJIT nie ma kary za wydajność dla tablic 0, w przeciwieństwie do standardowego Lua.

  • Deklaruj mieszkańców w najbardziej wewnętrznym zakresie, jak to możliwe: nie ma prawdziwego wyjaśnienia dlaczego, ale IIRC ma to związek z analizą żywotności SSA. zawiera również kilka ciekawe informacje na temat tego, jak uniknąć zbyt wielu mieszkańców (co jest analizą żywotności breaka).

  • Unikaj wielu małych pętli: to psuje heurystykę rozwijania i spowalnia kod.

Mniejsze Ciekawostki:

Narzędzia do profilowania są dostępne dla zwykłego Lua, jednak jest jeden nowszy projekt, który jest oficjalnie kompatybilny z LuaJIT (wątpię, że będzie w przeciwieństwie do innych języków, luajit może być używany w wielu językach. Lua wiki ma również stronę na porady optymalizacyjne dla normalnego Lua, które muszą być przetestowane pod kątem ich skuteczności pod LuaJIT( większość tych optymalizacji jest prawdopodobnie wykonywana wewnętrznie), jednak LuaJIT nadal używa domyślnego GC, co pozostawia go jako jeden obszar, w którym zyski z ręcznej optymalizacji mogą być nadal świetne(dopóki Mike nie doda niestandardowego GC, o którym wspominał tu i tam).

Źródło Luajita zawiera kilka ustawień do majstrowania z wewnętrznymi JIT, jednak wymagałoby to rozległych testów, aby dostroić je do konkretnego kodu, w rzeczywistości może po prostu lepiej ich całkowicie unikać, zwłaszcza dla tych, którzy nie są zaznajomieni z wewnętrznymi elementami JIT.

 30
Author: Necrolis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-25 22:27:14

Nie do końca to, czego szukasz, ale udało mi się odtworzyć jit.* śledzenie obiektów. To, co poniżej jest nieco szorstkie, niedokładne, może ulec zmianie i bardzo niekompletne. Wkrótce zacznę z niego korzystać w luatrace. Funkcje te są używane w kilku plikach biblioteki-J. dump.lua to chyba dobry początek.

Jit.Dołącz

Możesz dołączyć wywołania zwrotne do wielu zdarzeń kompilatora za pomocą jit.attach. Wywołanie zwrotne może być nazwa:

    W przypadku, gdy funkcja została skompilowana do bajtkodu ("bc"); Kiedy nagrywanie śledzenia zaczyna się lub zatrzymuje ("śledzenie");
  • Jak zapisuje się ślad ("zapis");
  • lub gdy ślad wychodzi przez boczne wyjście ("texit").

Ustaw wywołanie zwrotne za pomocą jit.attach(callback, "event") i wyczyść to samo wywołanie zwrotne za pomocą jit.attach(callback)

Argumenty przekazywane do wywołania zwrotnego zależą od zgłaszanego zdarzenia:

  • "bc": callback(func). func jest funkcją, która jest tylko zostały nagrane.
  • " ślad": {]}
    • what jest opisem zdarzenia trace: "flush"," start"," stop","abort". Dostępne na wszystkie wydarzenia.
    • tr to numer śledzenia. Nie jest dostępny dla koloru.
    • func jest funkcją śledzoną. Dostępne do startu i przerwania.
    • pc jest licznikiem programu - numerem bajtowym rejestrowanej funkcji (jeśli jest to funkcja Lua). Dostępne do startu i przerwania.
    • otr start: the parent trace number jeśli jest to ślad boczny, abort: abort code (integer)?
    • oex start: numer wyjścia dla nadrzędnego śladu, abort: abort reason (string)
  • " rekord": callback(tr, func, pc, depth). Pierwsze argumenty są takie same jak dla Trace start. depth jest głębokością inliningu bieżącego kodu bajtowego.
  • "texit": callback(tr, ex, ngpr, nfpr).
    • tr to numer śledzenia jak poprzednio
    • ex to numer wyjścia
    • ngpr i nfpr są liczbą rejestry ogólnego przeznaczenia i zmiennoprzecinkowe, które są aktywne na wyjściu.

Jit.util.funcinfo (func, pc)

Po przejściu func i pc z wywołania zwrotnego jit.attach , jit.util.funcinfo zwraca tabelę informacji o funkcji, podobnie jak debug.getinfo.

Pola tabeli to:

  • linedefined: co do debug.getinfo
  • lastlinedefined: co do debug.getinfo
  • params: Liczba parametrów, które przyjmuje funkcja
  • stackslots: Liczba slotów stosu w zmiennej lokalnej funkcji
  • upvalues: Liczba wartości użytych przez funkcję
  • bytecodes: liczba bajtăłw, ktĂłrych jest kompilowana funkcja
  • gcconsts: ??
  • nconsts: ??
  • currentline: co do debug.getinfo
  • isvararg: jeśli funkcja jest funkcją vararg`
  • source: co do debug.getinfo
  • Na przykład, jeśli źródło nie jest w stanie odczytać bieżącej linii, to nie jest w stanie odczytać bieżącej linii.]}
  • ffid: szybki identyfikator funkcji (jeśli jest). W tym przypadku ważne są tylko upvalues powyżej i addr poniżej
  • addr: adres funkcji (jeśli nie jest to funkcja Lua). Jeśli jest to funkcja C, a nie Funkcja szybka, tylko upvalues powyżej jest poprawna
 7
Author: Geoff Leyland,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-29 23:23:18

Używałem ProFi w przeszłości i uznałem go za bardzo przydatny!

 2
Author: tommitytom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-07 19:45:12