对于Linq - 性能与未来

Jaq*_*aqq 36 c# linq performance

非常简短的问题.我有一个随机排序的大字符串数组(100K +条目),我想找到所需字符串的第一次出现.我有两个解决方案.

从我读到我可以猜到的是'for循环'目前会提供略微更好的性能(但这个余量总是会改变),但我也发现linq版本更具可读性.总的来说哪种方法通常被认为是目前最好的编码实践,为什么?

string matchString = "dsf897sdf78";
int matchIndex = -1;
for(int i=0; i<array.length; i++)
{
    if(array[i]==matchString)
    {
        matchIndex = i;
        break;
    }
}
Run Code Online (Sandbox Code Playgroud)

要么

int matchIndex = array.Select((r, i) => new { value = r, index = i })
                         .Where(t => t.value == matchString)
                         .Select(s => s.index).First();
Run Code Online (Sandbox Code Playgroud)

usr*_*usr 50

最佳实践取决于您的需求:

  1. 开发速度和可维护性:LINQ
  2. 性能(根据分析工具):手动代码

LINQ确实会在所有间接情况下减慢速度.不要担心,因为99%的代码不会影响最终用户的性能.

我从C++开始,真正学会了如何优化一段代码.LINQ不适合充分利用CPU.因此,如果您测量LINQ查询是一个问题,那就放弃它.但只有这样.

对于您的代码示例,我估计会减少3倍的速度.分配(以及随后的GC!)和通过lambdas的间接真的很痛苦.

  • @ozgur JVM HotSpot编译器经常可以做到这一点..NET JITs*永远不会*默认化调用,即使调用目标类型是静态已知的,通常也是如此.在任何情况下,代理呼叫都不是虚拟化的. (2认同)

Mat*_*son 29

稍微更好的性能?一个循环将给予显着更好的性能!

请考虑以下代码.在我的系统上进行RELEASE(非调试)构建,它给出:

Found via loop at index 999999 in 00:00:00.2782047
Found via linq at index 999999 in 00:00:02.5864703
Loop was 9.29700432810805 times faster than linq.
Run Code Online (Sandbox Code Playgroud)

故意设置代码,以便找到的项目在最后.如果一开始就是对的,情况就会大不相同.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Demo
{
    public static class Program
    {
        private static void Main(string[] args)
        {
            string[] a = new string[1000000];

            for (int i = 0; i < a.Length; ++i)
            {
                a[i] = "Won't be found";
            }

            string matchString = "Will be found";

            a[a.Length - 1] = "Will be found";

            const int COUNT = 100;

            var sw = Stopwatch.StartNew();
            int matchIndex = -1;

            for (int outer = 0; outer < COUNT; ++outer)
            {
                for (int i = 0; i < a.Length; i++)
                {
                    if (a[i] == matchString)
                    {
                        matchIndex = i;
                        break;
                    }
                }
            }

            sw.Stop();
            Console.WriteLine("Found via loop at index " + matchIndex + " in " + sw.Elapsed);
            double loopTime = sw.Elapsed.TotalSeconds;

            sw.Restart();

            for (int outer = 0; outer < COUNT; ++outer)
            {
                matchIndex = a.Select((r, i) => new { value = r, index = i })
                             .Where(t => t.value == matchString)
                             .Select(s => s.index).First();
            }

            sw.Stop();
            Console.WriteLine("Found via linq at index " + matchIndex + " in " + sw.Elapsed);
            double linqTime = sw.Elapsed.TotalSeconds;

            Console.WriteLine("Loop was {0} times faster than linq.", linqTime/loopTime);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 好家伙!您的linq查询错误!正确的是以下,这个慢了不到10%.`matchIndex = a.Where(t => t == matchString).Select((r,i)=> i).first();` (4认同)
  • 问题是新的运算符,它会减慢linq查询的速度.如果数组可以转换为列表而不是linq可以与FindIndex结合使用,这次for循环只需要快1.5倍左右.'matchIndex = a.ToList().FindIndex(x => x.Equals(matchString));' (3认同)

Rys*_*gan 6

根据声明式范式,LINQ 表达了计算的逻辑,而不描述其控制流。该查询是面向目标的、自描述的,因此易于分析和理解。也是简洁。此外,使用 LINQ,高度依赖于数据结构的抽象。这涉及高可维护性和可重用性。

迭代方法解决了命令式范式。它提供了细粒度的控制,从而轻松获得更高的性能。代码也更易于调试。有时,结构良好的迭代比查询更具可读性。