英文原文:https://www.jacksondunstan.com/articles/4819
译者测试结论:
Test,Manual Time,Normal Time,LINQ Time
Where,106,147,95
Select,162,214,202
2024年2月份,Unity版本:2021.3.29,
目前LINQ已经比原文作者的时候有了非常明显的进步
除了手动创建结果数组,其他任何方式和LINQ相比都没有明显的优势
甚至where运算Linq已经超越了手动方式
距离上一篇有关 LINQ 性能的文章已经过去三年多了。这要追溯到 Unity 5.0 时代,使用 Mono 作为脚本后端。今天,我们将使用 Unity 2018.1 和 IL2CPP 更新该文章的测试,看看 LINQ 最近的表现如何。好点了吗?请仔细阅读,找出答案!
上一篇文章有一个 LINQ 使用示例,其中包括两个最常用的函数:Where 和 Select。还有更多,但这些非常具有 LINQ 的代表性。为了与这些功能进行比较,测试包括完全“手动”版本和“正常”版本,该版本以更实用的方式使用相同的委托。现在,测试已更新为更加简单,并且更少依赖委托,以避免将测试框架的性能与正在测试的代码混淆。新版本的测试如下所示:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using UnityEngine;
public class TestScript : MonoBehaviour
{
private string report;
void Start()
{
int[] array = new int[1024];
const int numIterations = 10000;
Stopwatch stopwatch = new Stopwatch();
Func<int,bool> checker = val => val == 1;
List<int> found = null;
stopwatch.Reset();
stopwatch.Start();
for (int it = 0; it < numIterations; ++it)
{
int len = array.Length;
found = new List<int>(len);
for (int i = 0; i < len; ++i)
{
int elem = array[i];
if (elem == 1)
{
found.Add(elem);
}
}
}
long whereManualTime = stopwatch.ElapsedMilliseconds;
stopwatch.Reset();
stopwatch.Start();
for (int it = 0; it < numIterations; ++it)
{
int len = array.Length;
found = new List<int>(len);
for (int i = 0; i < len; ++i)
{
int elem = array[i];
if (checker(elem))
{
found.Add(elem);
}
}
}
long whereNormalTime = stopwatch.ElapsedMilliseconds;
stopwatch.Reset();
stopwatch.Start();
for (int it = 0; it < numIterations; ++it)
{
array.Where(checker).ToList();
}
long whereLINQTime = stopwatch.ElapsedMilliseconds;
Func<int,int> transformer = val => val * 2;
List<int> transformed = null;
stopwatch.Reset();
stopwatch.Start();
for (int it = 0; it < numIterations; ++it)
{
int len = array.Length;
transformed = new List<int>(len);
for (int i = 0; i < len; ++i)
{
transformed.Add(array[i] * 2);
}
}
long selectManualTime = stopwatch.ElapsedMilliseconds;
stopwatch.Reset();
stopwatch.Start();
for (int it = 0; it < numIterations; ++it)
{
int len = array.Length;
transformed = new List<int>(len);
for (int i = 0; i < len; ++i)
{
transformed.Add(transformer(array[i]));
}
}
long selectNormalTime = stopwatch.ElapsedMilliseconds;
stopwatch.Reset();
stopwatch.Start();
for (int it = 0; it < numIterations; ++it)
{
array.Select(transformer).ToList();
}
long selectLINQTime = stopwatch.ElapsedMilliseconds;
report =
"Test,Manual Time,Normal Time,LINQ Timen" +
$"Where,{whereManualTime},{whereNormalTime},{whereLINQTime}n" +
$"Select,{selectManualTime},{selectNormalTime},{selectLINQTime}";
// Don't allow these to be optimized away
print(transformed + " " + found);
}
void OnGUI()
{
GUI.TextArea(new Rect(0, 0, Screen.width, Screen.height), report);
}
}
如果您想自己进行测试,只需将上述代码粘贴到 Unity 项目 “资产”(Assets)目录下的 TestScript.cs 文件中,并将其附加到新的空项目中的主摄像头游戏对象上即可。然后在 64 位处理器的非开发模式下构建,并在 640-480 窗口下以最快图形运行。我就是在这种测试环境下运行的:
- 2.7 Ghz Intel Core i7-6820HQ
- Mac OS X 10.13.5
- Unity 2018.1.0f2
- Mac OS X Standalone
- .NET 4.x
- IL2CPP
- .NET Standard 2.0
- Non-development
- 640×480, Fastest, Windowed
并得到这些结果:
实现了 LINQ 但仍使用委托的 "普通 "版本仍然比 LINQ 版本稍便宜一些。主要的节省只是去掉了函数调用。然而,"手动 "版本要比其他版本快很多。其中,调用时间减少了约 75%,选择时间减少了约 60%。这些都是巨大的节省,但比之前使用 Unity 5.0 和 Mono 时分别节省 96% 和 90% 的时间要少得多。
但测试仅显示直接的性能比较。可用性完全是另一回事。 LINQ 在某些方面更有用,因为它有语言语法支持,并促进了可以说更具可读性和功能性的风格。从另一个角度来看,“手动”版本更有用。当用户想要优化性能时,LINQ 不提供任何选项,但可以调整“手动”版本以尽早退出循环、避免分配等。
这里的要点是 LINQ 性能仍然很差。如今稍微好一些,但仍然比自己编写代码要差得多。
译者测试结论:
Test,Manual Time,Normal Time,LINQ Time
Where,106,147,95
Select,162,214,202
2024年2月份,Unity版本:2021.3.29,
目前LINQ已经比原文作者的时候有了非常明显的进步
除了手动创建结果数组,其他任何方式和LINQ相比都没有明显的优势
甚至where运算Linq已经超越了手动方式