英文原文:
https://www.jacksondunstan.com/articles/3001
Unity 4.6.2 和 5.0 中的新 IL2CPP 脚本后端应该比旧的 Mono 后端快得多。我运行了一些基准测试,但与 Mono 相比,大部分都发现了速度下降。今天的文章展示了我运行的测试,我得到的结果,并想知道为什么 IL2CPP 版本看起来这么慢。亲爱的读者,也许你们中的一个人知道原因。更新:部分原因已被发现。继续阅读以获取更新的结果。
今天测试中的所有基准测试均取自 The Computer Language Benchmarks Game。您可以从他们的网站下载 C# 和许多其他语言的源代码。我检查了每个基准测试,并采用了最新的具有在 Unity 中运行的 C# 版本的每个基准测试。这是我最终完成的测试列表:
- binarytrees
- chameneosredux
- fannkuchredux
- fasta-2
- fastaredux
- knucleotide-3
- mandlebrot-3
- nbody-3
- regexdna-6
- revcomp-3
- spectralnorm-2
然后我尽可能地修改了每个版本。这包括为每个创建一个公共 Main 方法,以便可以从我的基准测试运行程序中调用它们,并禁用对控制台的任何写入以支持对 MemoryStream 的写入。随意下载生成的测试。
然后,我创建了一个基准运行程序脚本,在后台线程中运行测试,以免阻塞主 UI 线程。主线程只显示测试结果报告。测试使用 benchmarksgame/nanobench/makefiles/u32.ini 配置文件中指定的程序参数反复运行。该脚本只是简单地附加到一个空的 Unity 场景中的主摄像机游戏对象。
这是基准运行程序源代码:
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using UnityEngine;
public class TestScript : MonoBehaviour
{
private class Test
{
public string Name;
public Action<string[]> Main;
public string[] Args;
public long TotalTime;
public Test(string name, Action<string[]> main, string[] args)
{
Name = name;
Main = main;
Args = args;
}
}
private static readonly Test[] tests = new Test[]{
new Test("BinaryTrees", BinaryTrees.Main, new string[]{"12"}),
new Test("chameneosredux", chameneosredux.Main, new string[]{"60000"}),
new Test("FannkuchRedux", FannkuchRedux.Main, new string[]{"10"}),
new Test("Fasta", Fasta.Main, new string[]{"250000"}),
new Test("FastaRedux", FastaRedux.Main, new string[]{"250000"}),
new Test("knucleotide", knucleotide.Main, new string[]{"250000"}),
new Test("MandelBrot", MandelBrot.Main, new string[]{"1000"}),
new Test("NBody", NBody.Main, new string[]{"500000"}),
new Test("regexdna", regexdna.Main, new string[]{"50000"}),
new Test("revcomp", revcomp.Main, new string[]{"250000"}),
new Test("SpectralNorm", SpectralNorm.Main, new string[]{"500"})
};
private Rect reportDrawArea;
private string report;
private object reportMutex;
private Thread reportThread;
void Awake()
{
reportDrawArea = new Rect(0, 0, Screen.width, Screen.height);
reportMutex = new object();
reportThread = new Thread(TestThread);
reportThread.Start();
}
void TestThread()
{
var reportBuilder = new StringBuilder();
var numRuns = 0;
var stopwatch = new Stopwatch();
while (true)
{
numRuns++;
reportBuilder.Length = 0;
reportBuilder.Append("Num Runs,");
reportBuilder.Append(numRuns);
reportBuilder.Append('\n');
reportBuilder.Append("Test,Time\n");
foreach (var test in tests)
{
stopwatch.Reset();
stopwatch.Start();
test.Main(test.Args);
var elapsedMillis = stopwatch.ElapsedMilliseconds;
test.TotalTime += elapsedMillis;
var averageMillis = test.TotalTime / (float)numRuns;
reportBuilder.Append(test.Name);
reportBuilder.Append(',');
reportBuilder.Append(averageMillis);
reportBuilder.Append('\n');
}
lock (reportMutex)
{
report = reportBuilder.ToString();
UnityEngine.Debug.Log(report);
}
}
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
reportThread.Abort();
}
}
void OnApplicationQuit()
{
reportThread.Abort();
}
void OnGUI()
{
lock (reportMutex)
{
if (report != null)
{
GUI.TextArea(reportDrawArea, report);
}
}
}
}
然后,我使用针对 ARMv7 的 Mono 和 IL2CPP 构建了适用于 iOS 的应用程序,而不是使用“开发构建”。这是我在运行 iOS 8.2 的 iPad 3 上得到的结果。
IL2CPP 完成任何大于 100% 的结果比 Mono 需要更长的时间。任何低于 100% 的速度都比 Mono 快。
如您所见,与 Mono 相比,IL2CPP 中除了两个测试之外的所有测试都较慢。这两个测试是 BinaryTrees 快 19%,而 revcomp 快得无法测量。
其他测试都显示放缓。这些范围从非常轻微到极端。在非常小的方面,knucleotide 和正则表达式仅慢了大约 20%。 MandelBrot、NBody 和 SpectralNorm 次之,慢了大约 80%。然后事情变得非常糟糕。 Fasta 和 FannkuchRedux 慢 400-500%,FastaRedux 慢 628%。到目前为止,速度之冠以惊人的 10580% 的速度进入 chameneosredux。
正如您可以在“基准游戏”网站上反复阅读的那样,这些内容应该持保留态度。它们显然不是在 Unity 中运行的真实应用程序或游戏,差异很容易蔓延。再说一次,这 11 项测试应该相当代表常见的编程任务。
我希望做这个测试并写一篇文章来展示 Unity 4.6.2 和 5.0 中新的 IL2CPP 脚本后端的速度有多快。不幸的是,我看到的结果完全相反。我也不知道为什么会这样。如果你这样做,请在评论中告诉我。
更新
感谢评论中的 Ralph Hauwert,已经发现了放缓的部分原因。事实证明,即使在 Unity 构建设置中没有选中“开发构建”,您也需要显式设置 Xcode 以进行发布构建。为此,请单击项目名称,选择“Edit Scheme…”,然后将“Build Configuration”更改为“Release”。顺便说一句,我在 Mac OS X 10.10.2 上使用 Xcode 6.2 构建了测试。
以下是更新的结果:
正如预期的那样,发布模式下的 IL2CPP 确实比调试模式下快得多。 chameneosredux、fasta 和 fastaredux 仍然非常慢,比 Mono 慢 3 到 64 倍,但其余的要快得多。 knucleotide 的速度几乎与 Mono 完全相同。 FannkuchRedux、MandleBrot 和 regexdna 只慢了大约 10-15%。
但是发布模式也有一些亮点。使用 IL2CPP 的 BinaryTrees、NBody 和 SpectralNorm 的速度都大约是 Mono 的两倍。 revcomp 仍然非常快,而在 Mono 中,它偶尔会记录几毫秒的时间。
鉴于切换到 IL2CPP 时性能可能会从非常慢到非常快不等,因此您最终看到的性能将在很大程度上取决于您的应用程序具有的代码类型。不要简单地假设您的应用程序会看到全面的加速。
如果您在实际代码中看到显着的加速或减速,尤其是特定类型的代码,我真的很想在评论中听到它。