Lua 對比虛幻指令碼

lua-users home
維基

注意:此為進行中工作

引言

UnrealScript 是一種程式設計語言,專門設計為自然地對應到遊戲編寫 [Epic Games] 的 Unreal 引擎中的需求。

UnrealScript 是一種基於位元組碼的語言:此程式碼編譯成一系列類似於 p 碼或 Java 位元組碼的位元組碼。透過這種方式,UnrealScript 仍然保持平台無依賴性,而且簡化了移植到 Mac 或主機等其他平台的過程。 UnrealScript 也是一種垃圾收集語言。Unreal 中的所有物件和動作者都使用與 Java VM 類似的樹後垃圾收集程式進行垃圾收集。

調查 UnrealScript 和 Lua 的相對效能的理由是:了解 Lua 是否可以在遊戲引擎中以 UnrealScript 成功地用於基於 Unreal 引擎的遊戲的方式來使用。

功能

Lua 和 UnrealScript 是相當不同的語言。它們都被編譯成位元組碼,透過虛擬機器執行,提供一個具有自動垃圾收集的無指標環境,並提供一個安全的執行沙盒。而這也是它們的相似性結束之處。

UnrealScript 是一種強類型語言,例如 Ada 和 C++。這提供了在編譯時捕捉到非預期的類型錯誤的好處,同時犧牲了一些靈活性。另一方面,Lua 是一種動態類型語言。在實務上,這在某些情況下可能會導致一些浪費的除錯時間,但並不如它一開始看來的那麼嚴重。在我看來,Lua 可以在編譯之前使用一個類似 lint 的步驟。

UnrealScript 也支援主要概念,例如時間、狀態、屬性以及透過明確的語言功能進行網路處理。這消除了在指令碼層級實作時所需的大量複雜度。自然地,Lua 缺乏這些功能,因為它是為一般用途而設計的,但它對元機制的支援可用來提供類似的功能。

效能

根據廣為引用的效能近似基準,Lua 比 C 程式碼慢 10 倍。Epic 宣稱 UnrealScript 的效能比 C++ 程式碼低 20 倍。以下是我已將 [The Great Win32 Computer Language Shootout] 中的幾個基準移植到 UnrealScript 中,以進行直接的效能比較。

為了進行所有計時,我在 Lua 中撰寫了一個小型測試函式,使用 os.clock() 在迴圈中多次計時受測程式碼。然後它會拋出最快速和最慢的結果,並平均計時以取得最終計時。UnrealScript 的情況更為複雜,因為沒有文件記載的任何計時程式碼回報函式有提供一致的結果。因此,我透過 UCC 和 [Commandlet] 來執行它們。為了除去 UCC 啟動後續處理,我計時一個空的非真實函式,然後從測試中扣除這個時間。

所使用的 Lua 發行版為 5.0.2(最新的穩定發行版),UnrealScript 在非真實錦標賽 2004 版次 3323 內執行,且 C++ 測試在 Microsoft Visual C++ .NET 2003 中完成。

所有測試均在配備 1GB 記憶體的 Intel P4 2.8GHz 的電腦上執行。

陣列存取

Lua5 基準中的陣列存取測試存在一個錯誤(第 10 行中的多餘「+ 1」)。我在下方的測試中已修正這個錯誤。

C++

// -*- mode: c++ -*-
// $Id: ary3.g++,v 1.2 2001/06/20 03:20:02 doug Exp $ // http://www.bagley.org/~doug/shootout/

#include <iostream>
#include <vector>

using namespace std;

int main(int argc, char *argv[]) {
    int i, k, n = ((argc == 2) ? atoi(argv[1]) : 1);
    typedef vector<int> ARY;
    ARY x(n);
    ARY y(n);

    for (i=0; i<n; i++) {
    x[i] = i + 1;
    }
    for (k=0; k<1000; k++) {
    for (int i = n - 1; i >= 0; --i) {
        y[i] += x[i];
    }
    }

    cout << y[0] << " " << y.back() << endl; } 
結果
n = 1000, 0.0306 秒
n = 3000, 0.03188 秒
n = 5000, 0.03564 秒
n = 7000, 0.03876 秒

UnrealScript

static final function string ArrayTest( optional int n ) {
	local int i, k;
	local array<int> x;
	local array<int> y;

	if ( n == 0 ) {
		n = 1;
	}

	x.Length = n;
	y.Length = n;

    for ( i = 0; i < n; i++ ) {
    	x[i] = i + 1;
    }

    for ( k=0; k<1000; k++ ) {
    	for (i = n-1; i >= 0; i--) {
         	y[i] += x[i];
    	}
    }

	return ( y[0]$ " " $y[n-1] );
}
結果
n = 1000, 0.27252 秒
n = 3000, 0.81316 秒
n = 5000,
n = 7000,

UnrealScript 有防範暴走迴圈的機制,在超過 3000 時會觸發。因此,這是在這個測試中能得到的全部結果了。

Lua

function array_test( n )

	local x, y = {}, {}

	for i=1,n do
		x[i] = i
		y[i] = 0
	end

	for k=1,1000 do
		for j=n,1,-1 do
			y[j] = y[j] + x[j]
		end
	end

	return y[1] .. " " .. y[n]
end
結果
n = 1000, 0.21872 秒
n = 3000, 0.65432 秒
n = 5000, 1.09124 秒
n = 7000, 1.52688 秒

Lua 在 n=1000 時執行速度約為 C++ 的 1/7,在 n=7000 時執行速度約為 C++ 的 1/40。它比 UnrealScript 快約 24%。

堆排序

方法呼叫

巢狀迴圈

物件實例化

字串串接

結論

評論

請針對結果進行評論並在此處加入你的意見回饋。

* 我在搭載英特爾 P4 2G(搭配 512 MB 記憶體)的電腦執行相同的陣列測試,n=1000 時得到 0.31 秒,n=3000 時得到 0.94 秒。我的電腦是否比你的快這麼多?(-- Roberto)

* 真是見鬼了... 問題出在一個很笨的錯誤。我的剖析器正在執行 lua.exe。我重新執行測試並在上面放上我的結果。我原本以為這些數字很奇怪。現在我的新問題是在非真實中尋找一個精確的計時器。Clock/UnClock? 在我的電腦上不可靠 -- Tom

* 在非真實腳本中,你可以使用碼表函式作為計時器。它並非完美無缺(在 3355 版次中),因此請迴圈執行它和程式碼約 20 次以取得較多的結果集合。你可以使用 -norunaway 參數停用暴走迴圈檢查。-- Switch`

* 如果使用時脈及解除時脈?記錄結果,請記住產出的資料實際上是毫秒,而非秒。因此如果執行時脈 (f) 接著解除時脈 (f),則 f 是毫秒。我不確定是否已將此考量在內。-- Solid Snake

* 時脈使用中央處理器週期,不安全,會環繞。因此僅可用於極短的計時。此外,需要留意 UnrealEngine? 有個啟動「問題」,整個環境需要設定以及執行類似的工作。一開始非真實指令碼相當慢,執行相同程式碼第 2 次會產生較好的結果,甚至等候幾秒再開始基準測試,也會產生較好的結果。在實際情況下,這個啟動問題不是問題,只是可能會扭曲基準測試結果。我不知道 Lua 的情況,但非真實指令碼編譯期間未最佳化。寫成 ++i 比 i++ 快。當然這些事項應該無妨,因為一般使用者不可能知道。-- elmuerte

* 進行效能計時時,請勿取全部執行時間的平均值,而應僅取最好的執行時間:可以安全地假設實際執行時間每次都相同,因此唯一的差異來自作業系統排程器干擾。因此最接近指令碼的「真實」執行時間,就是執行時間最短的執行時間。

^^^^^^ 請注意此建議。以我的經驗,此建議相當誤導人。例如,您接著會假設應用程式最輕量的執行部分是效能最佳的部分,這很顯然是誤導。您應透過多組相同且可重現的執行,計算效能測試的平均值,如此方可獲得相對的基準測試作為比較基準。除了作業系統排程器以外,還有更多因素可能會影響指令碼執行的效能。因此請注意上述建議。-- Grover.

另請參閱


最近變更 · 喜好設定
編輯 · 歷程記錄
最近編輯於 2014 年 1 月 6 日下午 5:25 GMT (比較)