Generacja kodu natywnego

*Ta zawartość została przetłumaczona przy użyciu narzędzi AI (w wersji beta) i może zawierać błędy. Aby wyświetlić tę stronę w języku angielskim, kliknij tutaj.

Dzięki wsparciu Luau dla generowania kodu natywnego serwerowe skrypty w twoim doświadczeniu mogą być bezpośrednio skompilowane do instrukcji maszynowych, które wykonują CPU, zamiast zwykłego kodu binarnego, na którym działa Luau VM.Funkcja ta może być używana do poprawy szybkości wykonania dla niektórych skryptów na serwerze, w szczególności tych, które mają dużo obliczeń liczbowych bez użycia zbyt wielu ciężkich bibliotek Luau lub wezwań API Roblox.

Włącz generowanie kodu natywnego

Aby włączyć generację kodu natywnego dla Script , dodaj komentarz --!native na górze:¹


--!native
print("Hello from native code!")

Umożliwia to generowanie kodu natywnego dla wszystkich funkcji w skrypcie i zakresu najwyższego poziomu, jeśli uzna się to za opłacalne.Nie są wymagane dodatkowe zmiany; zachowanie wykonywanych natywnie skryptów jest dokładnie takie samo jak przedtem, a tylko wydajność jest inna.Wszystkie funkcje języka Luau i wszystkie interfejsy API Roblox pozostają wspierane.

Alternatywnie możesz włączyć generowanie kodu natywnego dla pojedynczej funkcji, dodając atrybut @native:


@native
local function f(x)
return (x + 1)
end
1 W przyszłości niektóre skrypty mogą automatycznie rozpocząć uruchamianie natywnie, jeśli zostanie określone, że będą opłacalne, ale ręcznie umieszczone komentarze --!native są obecnie wymagane.

Najlepsze praktyki

Następujące wskazówki pomogą Ci najbardziej skorzystać z generacji kodu natywnego:

  • Najlepiej włączyć tę funkcję w skryptach, które wykonują dużo obliczeń bezpośrednio w Luau.Jeśli masz wiele operacji matematycznych na tabelach i szczególnie typach buffer, skrypt może być dobrym kandydatem.

  • Tylko funkcje skryptu są kompilowane natywnie.Kod w górnej zewnętrznej zakresie często jest wykonywany tylko raz i nie korzysta tak bardzo, jak funkcje, które są wywoływane wiele razy, szczególnie te, które są wywoływane za każdym razem.

  • Zaleca się, abyś mierzył czas, jaki zajmuje skrypt lub funkcja bez natywnej kompilacji, aby osądzić, kiedy najlepiej go używać.Narzędzie Profilowanie skryptów może mierzyć wydajność funkcji, aby podejmować świadome decyzje.

  • Może być pokusa, aby umieścić komentarz --!native w każdym skrypcie, na wypadek, gdy niektóre z nich będą wykonywane szybciej, ale generacja kodu natywnego ma pewne wady:

    • Wymagany jest czas kompilacji kodu, który może zwiększyć czas uruchamiania serwerów.
    • Dodatkowa pamięć jest zajęta do przechowywania skompilowanego kodu natywnego.
    • Istnieje ograniczenie w całkowitej dozwolonej ilości skompilowanego kodu natywnego w doświadczeniu.

Te problemy można rozwiązać przez umiejętne wykorzystanie atrybutu @native.

Kod do uniknięcia

Chociaż wszystkie funkcje będą zachowywać się tak samo z włączoną lub wyłączoną generacją kodu natywnego, niektóre z nich nie będą uruchamiane natywnie i mogą spowodować deoptymalizację lub powrót do interpretowanej wykonania.Obejmują one:

  • Użycie przestarzałych wezwań getfenv() / setfenv().
  • Użycie różnych wbudowanych funkcji Luau, takich jak math.asin() z nienumericznymi argumentami.
  • Przekazywanie nieprawidłowo wpisanych parametrów do funkcji typowanych, na przykład wzywanie foo(true), gdy foo jest deklarowane jako function foo(arg: string).Pamiętaj, aby zawsze używać poprawnych anotacji typu .

Podczas używania Profilera skryptów, możesz porównać czas potrzebny do wykonania zwykłej wersji funkcji z tym, który został skompilowany natywnie.Jeśli funkcja wewnątrz skryptu nie wygląda na uruchamianą natywnie, jeden lub więcej czynników z listy powyżej może uruchamiać deoptymalizację.

Użyj anotacji typu

Natomiast próby generowania kodu nativego próbują wywnioskować najbardziej prawdopodobny typ dla danego zmiennego, aby zoptymalizować ścieżki kodu.Na przykład, zakłada się, że a + b jest wykonywany na liczbach lub że tabela jest dostępna w t.X .Jednak ze względu na przeciążenie operatora a i b mogą być tablicami lub typami Vector3 lub t mogą być typami danych Roblox.

Choć generacja kodu natywnego będzie wspierać dowolny wpisywać, błędne przewidywania mogą wywołać niepotrzebne kontrolki, co skutkuje powolniejszym wykonywaniem kodu.

Aby rozwiązać niektóre wspólne problemy, Luau sprawdza anotacje typu na argumentach funkcji, ale szczególnie zaleca się annotowanie argumentów Vector3:


--!native
-- domniemuje się, że "v" jest tablicą; funkcja działa wolniej ze względu na kontrolki tablicy
local function sumComponentsSlow(v)
return v.X + v.Y + v.Z
end
-- „v” jest deklarowany jako Vector3; generowany jest kod specjalizowany dla wektorów
local function sumComponentsFast(v: Vector3)
return v.X + v.Y + v.Z
end

Narzędzia do studia

Następujące narzędzia Studio są wspierane dla --!native skryptów i @native funkcji.

Debugowanie

Ogólne debugowanie skryptów jest wspierane, ale widoki dla lokalnych/wartości maksymalnych mogą być niekompletne i brakować zmiennych z stosu wezwania, które są wykonywane natywnie.

Zauważ też, że podczas debugowania kodu wybranego do kompilacji natywnej umieszczenie punktów przerwania wyłączy wykonanie natywne dla tych funkcji.

Profilownik skryptów

W Profilu skryptów, funkcje wykonywane natywnie wyświetlają <native> obok nich:

Example of native functions flagged in the Script Profiler

Jeśli funkcja oznaczona @native lub wewnątrz --!native skryptu nie pokazuje anotacji <native> , może to być spowodowane natywnym wykonaniem z powodu punktu przerwania , użyciem odrzuconego kodu lub niezgodnych typów anotacji.

Kopalnia Luau

W profilu Luau heap pamięć pobierana przez native funkcje wyświetla się jako [native] elementy w grafice.

Example of native memory usage flagged in the Luau Heap profiler

Analiza rozmiaru

Każdy skrypt skompilowany natywnie zużywa pamięć.Gdy rozmiar skompilowanego kodu osiągnie określony limit, kompilacja natywna zatrzymuje się, a pozostały kod jest uruchamiany bezpośrednio.To sprawia, że ważne jest staranne wybieranie skryptów do kompilacji natywnej.

Aby monitorować rozmiar kodu natywnego poszczególnych funkcji i skryptów:

  1. Upewnij się, że jesteś w widoku serwera poprzez przycisk przełączania klienta/serwera.
  2. Wywołaj debug.dumpcodesize() z paska poleceń.

W oknie Wyjście, zobaczysz ogólną liczbę skryptów i funkcji, które zostały skompilowane natywnie do punktu wezwania, zużywaną przez nich pamięć kodu natywnego i limit rozmiaru kodu natywnego.Po podsumowaniu zobaczysz tabelę dla każdego skryptu skompilowanego natywnie w kolejności malejącego rozmiaru kodu.

Example of native code size displayed in the Output window.

Dla każdego skryptu wyświetlany jest wynik liczby skompilowanych funkcji i zużycie pamięci kodu natywnego.Każda funkcja jest następnie wymieniona w kolejności malejącego rozmiaru kodu natywnego, z anonimowymi funkcjami pokazanymi jako [anonymous] i całymi skryptami pokazanymi jako [top level].W ostatniej kolumnie odsetek jest obliczany w odniesieniu do ograniczenia rozmiaru kodu natywnego.Zauważ, że rozmiar kodu natywnego funkcji jest dokładnie zgłaszany, ale zużycie pamięci dla skryptów jest zaokrąglane do najbliższego rozmiaru strony.

Ograniczenia i rozwiązywanie problemów

Kompilacja kodu do instrukcji dla określonego procesora wymaga dodatkowej pamięci przechowywania.Ponadto optymalizacje dla złożonych funkcji mogą zająć zbyt dużo czasu na wykonanie.Uderzenie w wewnętrzną granicę zgłosi błąd w oknie Wyjście w Studio, w tym:

Funkcja 'f' na linii 20 przekroczyła limit instrukcji bloku jednokodowego

Błąd ten oznacza, że pojedynczy blok kodu w funkcji użył więcej niż 64K instrukcji.Można tego uniknąć, uprościwszy funkcję lub rozdzielając ją na poszczególne mniejsze funkcje.

Funkcja 'f' na linii 20 przekroczyła limit kodu funkcji

Błąd ten oznacza, że pojedyncza funkcja zawiera więcej niż 32K wewnętrznych bloków kodu.Wewnętrzne bloki kodu nie dokładnie mapują się do bloków przepływu kontroli w twoim skrypcie, ale ten błąd można uniknąć poprzez uproszczenie przepływu kontroli w funkcji lub rozdzielenie jej na poszczególne mniejsze funkcje.

Funkcja 'f' na linii 200 przekroczyła całkowity limit instrukcji modułu

Błąd ten oznacza, że łącznie funkcja osiągnęła limit 1 miliona instrukcji dla całego skryptu.W niektórych przypadkach sama zgłoszona funkcja może mieć wiele instrukcji, a limit może zostać osiągnięty przez funkcje wcześniej w skrypcie.Aby uniknąć tego problemu, zaleca się przeniesienie szczególnie dużych funkcji do oddzielnego nieskompatybilnego skryptu lub użycie @native na pozostałych funkcjach.Możesz także spróbować oznaczyć ten oddzielny skrypt za pomocą --!native , ale 1 milion instrukcji zajmuje dużo pamięci i możesz przekroczyć limit pamięci

*Funkcja 'f' na linii 20 spotkała się z wewnętrzną awarią obniżenia *(lub) Błąd wewnętrzny: Generacja kodu natywnego nie powiodła się (obniżanie zbioru)

Czasami funkcja zawiera skomplikowane bity kodu, których kompilator kodu natywnego nie może obecnie obsłużyć.Aby uniknąć tego błędu, sprawdź skomplikowane wyrażenia w kodzie i rozdziel je lub uprość je, ale rozważ również otwarcie raportu o błędzie z przykładem kodu, który zawiódł z tego powodu.

Osiągnięto limit alokacji pamięci dla generacji kodu natywnego

Błąd ten oznacza, że ogólny limit pamięci dla danych kodu natywnego został osiągnięty.Aby tego uniknąć, spróbuj usunąć --!native z bardziej wymagających pamięci skryptów, umożliwiając większej liczbie mniejszych skryptów zmieszczenie się w granicach.Alternatywnie przenieś duże lub rzadko wywoływane funkcje do oddzielnego niez nativego modułu.