原生代碼生成

*此內容是使用 AI(Beta 測試版)翻譯,可能含有錯誤。若要以英文檢視此頁面,請按一下這裡

使用 Luau 支持原生代碼生成時,您體驗中的服務器側腳本可直接編譯為機器代碼指令,而不是 CPUs 運行的正常 bytecode。此功能可用於提高服務伺服器上某些腳本的執行速度,特別是那些沒有使用太多重型 Luau 圖書館或 Roblox API 呼叫的數學計算量大的腳本。

啟用原生代碼生成

要啟用原生代碼生成對 的支持,請在頂部添加 评论:¹


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

這會啟用腳指令碼中所有功能的原生代碼生成,以及頂層範圍,如果被認為有利可圖。不需要進行額外變更;以原生方式執行的腳本的行為與之前一樣,只有執行效率不同。Luau 語言的所有功能和所有 Roblox API 仍然受支持。

或者,您可以通過添加 @native 屬性來啟用個別功能的原生代碼生成:


@native
local function f(x)
return (x + 1)
end
1 在未來,一些腳本可能會自動以原生方式運行,如果被確定為有利可圖,但目前手動放置 --!native 評論是必需的。

最佳實踐

以下提示將幫助您最大限度地從原生代碼生成中受益:

  • 最好在 Luau 內直接執行大量計算的腳本中啟用此功能。如果您在表上和特別是 buffer 類型上有很多數學運算,那麼腳本可能是一個很好的候選人。

  • 只有指令碼的 功能 會以原生形式編譯。外部頂層的 代碼 經常只執行一次,並不像經常被呼叫的函數那麼受益,特別是那些每一個框架都被呼叫的函數。

  • 建議您測量腳本或功能所需的時間,包括使用原生編譯與否,以判斷何時最好使用它。腳本分析儀工具可以測量功能的性能,以便做出明智的決定。

  • 可能有誘惑將 --!native 評論放在 每個 腳本中,只是為了防止一些腳本會更快地執行,但原生代碼生成有一些缺點:

    • 需要編譯代碼的時間,可能會增加服務器的啟動時間。
    • 額外記憶體用於儲存自動編譯的代碼。
    • 體驗中的總允許編譯代碼數量有限。

這些問題可以通過適當使用 @native 屬性來解決。

需避免的代碼

雖然所有功能都會在啟用原生代碼生成或無啟用原生代碼生成時與或無關,但有些功能不會以原生方式運行,可能會導致過度最佳化或返回到解釋執行。這些包括:

  • 使用過時的 getfenv() / setfenv() 呼叫。
  • 使用各種 Luau 內置功能,例如 math.asin() 使用非數字參數。
  • 將未正確輸入的參數傳給 ти化的函數,例如當 foo 被宣言為 function foo(arg: string) 時,呼叫 foo(true) 。請記得總是使用正確的 類型注解

當使用 腳本檢查器 時,您可以比較函數的正常版本與編譯為原生的版本之間的時間。如果內部的 --!native 腳本中的函數或標記為 @native 不會自動執行,上面的列表中的一個或多個因素可能會導致降級。

使用類型標記

原生代碼生成嘗試推測給定變量的最可能類型,以優化代碼路徑。例如,假設 a + b 在數字上執行,或者表在 t.X 中存取。然而,由於操作過載,ab可能是表或Vector3類型,或t可能是Roblox數據類型。

雖然原生代碼生成將支持任何輸入,但誤判可能會導致不必要的檢查,導致代碼執行速度降低。

為了解決一些常見問題,Luau 類型的注解在函數參數上檢查,但特別建議標記 Vector3 參數:


--!原生
-- 「v」假設是一個表;函數由於表檢查而執行速度較慢
local function sumComponentsSlow(v)
return v.X + v.Y + v.Z
end
-- 「v」被宣言為 Vector3;專門為向量編寫的代碼被生成
local function sumComponentsFast(v: Vector3)
return v.X + v.Y + v.Z
end

工作室工具

下列 Studio 工具支持 --!native 腳本和 @native 功能。

偵錯

對腳本的一般 調試 是支持的,但本地/上值的觀點可能不完整,缺少從 呼叫堆 框架中執行的原生變量。

請注意,當在原生編譯代碼選擇的代碼進行偵錯時,放置 斷點 會禁用這些函數的原生執行。

腳本偵測器

腳本偵測器 中,執行原生的功能會顯示<native>

Example of native functions flagged in the Script Profiler

如果標示 或在 腳本內的函數沒有顯示 標記,那個函數可能不會以原生方式執行,因為 斷點放置 、使用 被禁止代碼 或匹配不同的 類型標記 。

盧埃堆

Luau 堆 調試器中,原生函數所取得的記憶顯示為圖表中的 [native] 元素。

Example of native memory usage flagged in the Luau Heap profiler

尺寸分析

每個原生編譯的腳本都會消耗記憶。當編譯的代碼大小達到預定限制時,原生編譯停止,剩餘的代碼將以非原生方式運行。這使得選擇本地編譯的腳本非常重要。

要監控個別功能和腳本的原始碼大小:

  1. 確保你使用 服務器 視圖通過 客戶端/服務器切換 按鈕。
  2. 從 指令欄 呼叫 。

輸出 窗口中,您將看到到達呼叫點的所有內生式編譯的腳本和函數總數、其原生代碼所使用的記憶以及原生代碼大小限制。在摘要之後,您會看到一個表,其中包含每個原生編譯的腳本的代碼尺寸排序為下降順序。

Example of native code size displayed in the Output window.

對於每個指令碼,輸出會顯示編譯的功能數量和原生代碼記憶使用量。每個功能然後列出在原生代碼大小下降的順序中,匿名函數顯示為 [anonymous] ,整個腳本顯示為 [top level] 。在最後一個柱中,百分比是以原生代碼大小限制為基礎計算的。請注意,函數的原生代碼大小準確報告,但對腳本的記憶使用量會被回合到最接近的頁面尺寸。

限制與故障排除

將代碼轉換為特定 CPU 的指令需要額外的儲存記憶。此外,對複雜功能的最佳化可能需要太多時間來執行。達到內部限制將在 Studio 的 輸出 窗口中報告錯誤,包括:

功能 'f' 在第 20 行超出了單一代碼方塊指令限制

這個錯誤意味著一個函數內的單一代碼塊使用了超過 64K 個指令。這可以通過簡化功能或將其分解為個別較小的功能來避免。

功能 'f' 在第 20 行超出了功能代碼方塊限制

這個錯誤意味著單一函數包含超過 32K 個內部代碼塊。內部代碼的方塊與腳指令碼中的控制流方塊不完全匹配,但這個錯誤可以通過簡化函數中的控制流或將其分解為更小的單一功能來避免。

功能 'f' 在第 200 行超過了總模組指令限制

這個錯誤意味著,在總計上,函數已達到整個指令碼本的 100 萬個指令限制。在某些情況下,報告的功能本身可能有很多指令,或限制可能已由腳指令碼中的更早功能達到。為了避免這個問題,建議將特別大的功能移至獨立的非原生腳本或使用 @native 在其他功能上。您也可以嘗試使用 來標記那個獨立的腳本,但 100 萬個指令會佔用大量記憶,您可能會超過記憶限制。

在第 20 行的功能 'f' 遇到了內部降低失敗 (或) 內部錯誤:原生代碼生成失敗(組裝降低)

有時候功能包含複雜的代碼片段,原生代碼編譯器目前無法處手把。為了避免這個錯誤,在代碼中檢查複雜的表達式,將它們分開或簡化,但也考慮開啟一個錯誤報告,包含這個原因失敗的代碼的範例。

已達到原生代碼生成的記憶配置限制

這個錯誤意味著原生代碼數據的整體記憶限已達到。為了避免這種情況,請嘗試從更資源密集的腳本中移除 --!native ,以允許更多較小的腳本在限制下匹配。或者,將大型或不常呼叫的功能移至獨立的非本地模組。