Lua 与 Unreal Script

lua-users home
wiki

注意:此文档正在进行中

简介

UnrealScript 是一种编程语言,旨在自然地映射到 [Epic Games] 的 Unreal 引擎中游戏编程的需求。

UnrealScript 是基于字节码的:代码被编译成一系列类似于 p-code 或 Java 字节码的字节码。这样,UnrealScript 保持平台独立性,并简化了移植到其他平台(如 Mac 或游戏机)的过程。 UnrealScript 也是一种垃圾收集语言。Unreal 中的所有对象和角色都使用类似于 Java VM 的树遍历垃圾收集器进行垃圾收集。

调查 UnrealScript 与 Lua 的相对性能的原因是,看看 Lua 是否可以在游戏引擎中以与 UnrealScript 在基于 Unreal 引擎的游戏中成功使用的方式相同的方式使用。

特性

Lua 和 UnrealScript 是截然不同的语言。它们都编译成字节码,通过虚拟机执行,提供无指针环境和自动垃圾收集,并提供安全的执行沙箱。它们的相似之处到此为止。

UnrealScript 是一种强类型语言,类似于 Ada 和 C++。这提供了在编译时捕获意外类型错误的好处,同时牺牲了一些灵活性。另一方面,Lua 是动态类型的。在实践中,这可能会导致某些情况下浪费一些调试时间,但并不像最初看起来那么大问题。在我看来,Lua 在编译之前可以使用类似 lint 的检查。

虚幻脚本 还通过显式语言特性支持时间、状态、属性和网络等主要概念。这消除了在脚本级别实现时所需的许多复杂性。当然,Lua 缺乏这些特性,因为它旨在用于通用目的,但它对元机制的支持可用于提供类似的特性。

性能

根据广为引用的性能近似值,Lua 比 C 代码慢 10 倍。Epic 声称 虚幻脚本 比 C++ 代码的性能损失 20 倍。下面,我将几个来自 [伟大的 Win32 计算机语言对决] 的基准移植到 虚幻脚本 中,以尝试进行直接的性能比较。

为了进行所有计时,我使用 Lua 中的一个小型测试函数,该函数使用 os.clock() 在循环中多次对测试代码进行计时。然后它会丢弃最快和最慢的结果,并对时间进行平均以获得最终计时。 虚幻脚本 更加复杂,因为所有用于计时代码的函数都没有记录一致的结果。因此,我改为通过 UCC 和一个 [命令行程序] 启动它们。为了消除 UCC 启动开销,我计时了一个空虚幻函数,然后从测试中减去它。

使用的 Lua 版本是 5.0.2(最新的稳定版本),虚幻脚本 在 Unreal Tournament 2004 build 3323 中运行,C++ 测试是在 Microsoft Visual C++ .NET 2003 中完成的。

所有测试都在 Intel P4 2.8GHz、1GB 内存的机器上进行。

数组访问

对决中 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.0306s
n = 3000, 0.03188s
n = 5000, 0.03564s
n = 7000, 0.03876s

虚幻脚本

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.27252s
n = 3000, 0.81316s
n = 5000, <运行时循环错误>
n = 7000, <运行时循环错误>

虚幻脚本 具有防止运行时循环的保护机制,该机制在测试超过 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.21872s
n = 3000, 0.65432s
n = 5000, 1.09124s
n = 7000, 1.52688s

对于 n=1000,Lua 的性能大约是 C++ 的 1/7,对于 n=7000,Lua 的性能大约是 C++ 的 1/40。它比 虚幻脚本 快约 24%。

堆排序

方法调用

嵌套循环

对象实例化

字符串连接

结论

评论

请评论结果并在此处添加您的反馈。

* 我在一台英特尔 P4 2G(带 512 兆字节内存)上进行了相同的数组测试,n=1000 时得到 0.31 秒,n=3000 时得到 0.94 秒。我的机器比你的快很多吗? (-- Roberto)

* 哎呀... 是个愚蠢的错误。我的分析器正在对 lua.exe 进行检测。我重新运行了测试并将我的结果放在上面。我认为这些数字看起来很奇怪。我的新问题是在 Unreal 中找到一个准确的计时器。Clock/UnClock? 在我的机器上不可靠 -- Tom

* 在 UnrealScript 中,您可以使用 stopwatch() 函数作为计时器。它不是没有错误的(在构建 3355 中),因此循环它和代码大约 20 次以获得更大的结果集。您可以使用 -norunaway 参数禁用循环失控检查。 -- Switch`

* 如果您使用 Clock 和 UnClock? 记录结果,请记住,输出的实际上是毫秒而不是秒。因此,如果您 clock(f) 然后 unclock(f),f 则以毫秒为单位。我不确定是否考虑了这一点。 -- Solid Snake

* Clock 使用 CPU 周期,不安全,会绕回。因此,您只能将其用于非常小的计时。另一个需要注意的是,UnrealEngine? 存在一个启动“问题”,整个环境需要设置,诸如此类。在开始时,UnrealScript 速度相当慢,第二次运行相同的代码会得到更好的结果,甚至在运行基准测试之前等待几秒钟也会得到更好的结果。在现实世界中,这个启动问题不是问题,它可能只是扭曲了基准测试结果。我不了解 Lua,但 UnrealScript 在编译期间没有优化。编写 ++i 比 i++ 快。当然,这些事情无关紧要,因为普通用户也不会知道这些。-- elmuerte

* 当您进行性能计时时,您 _不应该_ 取所有运行时间的平均值,而应该取 _最佳_ 值:可以安全地假设实际执行时间每次都相同,因此您得到的唯一差异是由于操作系统调度器干扰造成的。因此,最接近脚本“真实”执行时间的运行时间是最短的运行时间。

^^^^^^ 注意这条建议。根据我的经验,这很误导。例如,您将假设应用程序中最轻量级的执行部分运行起来性能最佳,这显然是误导的。您 _应该_ 对多个相同且可重复运行的性能测试进行平均,以便您有可比较的基准测试。除了操作系统调度器之外,还有更多因素会影响脚本运行的性能。因此,请注意上述建议。-- Grover.

另请参阅


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2014 年 1 月 6 日下午 5:25 GMT (差异)