[C#] LINQ 性能更新

本文比较了Unity2021.3.29中LINQ在Where和Select操作上的性能,发现尽管有所进步,但相较于手动编写代码,LINQ仍然较慢,尤其在创建结果数组时。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

英文原文: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已经超越了手动方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值