引言
在NLua開發中,我們常面臨一個重要選擇:將C#函數注冊到Lua環境調用,還是直接在Lua中實現邏輯? 直覺告訴我們,C#作為編譯型語言性能更高,但跨語言調用的開銷是否會影響整體性能?本文通過基準測試揭示真相。
測試場景
實現安全索引訪問函數At
,對比三種實現方式:
- NativeLuaAt:純Lua實現
- CSharpRegisterAt:C#實現并注冊到Lua
- NativeCSharpAt:純C#直接調用
測試環境:
- .NET Framework 4.8.1
- Intel Core i7-1260P
- BenchmarkDotNet v0.15.0
性能數據對比
方法 | 平均耗時 | 內存分配 |
---|---|---|
NativeLuaAt | 6,844 ns | 288 B |
CSharpRegisterAt | 9,585 ns | 552 B |
NativeCSharpAt | 106 ns | 32 B |
結論
出乎意料,直接在Lua中實現邏輯會更快,這里原因可能是將C#函數注冊到Lua環境調用涉及到上下文切換等耗時動作。
相關代碼見NLuaBenchmarkDotNetTest
代碼
Lua代碼
--[[
僅用于userdata索引訪問函數,C#定義
參數:tbl : 目標Lua表(數組形式)index: 索引值(支持正負索引)strict: [可選]嚴格模式,默認false,設為true時額外校驗元素連續性
返回值:對應索引位置的元素
異常:類型錯誤或索引越界時拋出錯誤
--]]
function At(tbl, index)-- 參數校驗階段 -- 檢查第一個參數是否為tableif type(tbl) ~= "userdata" thenerror("bad argument #1 (expected table, got "..type(tbl)..")", 2)end-- 檢查索引是否為整數if type(index) ~= "number" or math.floor(index) ~= index thenerror("index must be integer, got "..type(index), 2)end-- 長度計算策略 local len = tbl.Length-- 嚴格模式下驗證表連續性-- 索引轉換邏輯 local adjusted_index-- 處理正索引(userdata是 的0-based)if index >= 0 thenadjusted_index = index -- 處理負索引(從末尾倒數)elseadjusted_index = len + index end-- 邊界檢查與錯誤處理 -- 有效索引范圍:1 ≤ index ≤ lenif adjusted_index < 0 or adjusted_index >= len thenlocal direction = index >=0 and "positive" or "negative"error(string.format("Index %d (%s) out of range [%d, %d]", index, direction, -len, len-1), 2)end-- 最終元素獲取 return tbl[adjusted_index]
end
C#代碼
/// <summary>/// 安全索引訪問器(支持Lua數組的0-based索引規則)/// </summary>/// <param name="collection">目標集合(支持IList接口的集合)</param>/// <param name="index">索引值(支持負索引倒查)</param>/// <param name="strict">嚴格模式校驗元素連續性</param>/// <returns>索引位置的元素</returns>/// <exception cref="ArgumentNullException">輸入集合為空</exception>/// <exception cref="ArgumentException">集合類型不合法或索引無效</exception>public static object At(IEnumerable collection, int index, bool strict = false){// 參數基礎校驗if (collection == null)throw new ArgumentNullException(nameof(collection), "輸入集合不能為null");// 類型安全轉換IList list = collection as IList;if (list == null)throw new ArgumentException("輸入集合必須實現IList接口", nameof(collection));// 獲取有效長度int count = list.Count;if (count == 0)throw new ArgumentException("集合中不包含有效元素", nameof(collection));// 索引轉換邏輯int adjustedIndex = index >= 0 ? index : count + index;// 邊界校驗if (adjustedIndex < 0 || adjustedIndex >= count){string msg = $"索引 {index} 超出有效范圍 [{-count}, {count - 1}]";throw new ArgumentOutOfRangeException(nameof(index), msg);}// 嚴格模式校驗if (strict){// 校驗是否存在null元素for (int i = 0; i < count; i++){if (list[i] == null){throw new ArgumentException($"嚴格模式檢測到空元素 @ 位置 {i}");}}}return ConvertToDouble(list[adjustedIndex]);}