简体   繁体   English

这个LINQ性能来自哪里?

[英]Where does this LINQ performance come from?

I made a function to recursively find the first or default item that fit a condition (first code block). 我做了一个函数来递归地找到适合条件的第一个或默认项(第一个代码块)。

Resharper suggested me to change few lines in only one LINQ line (second code block). Resharper建议我只在一个LINQ行(第二个代码块)中更改几行。

I was wondering if the Resharper suggestion would give me same performance and same memory footprint. 我想知道Resharper的建议是否会给我相同的性能和相同的内存占用。 I made a test for the performance (3rd code block). 我对性能进行了测试(第3代码块)。 The result is just what I expected. 结果就是我的预期。 Why the difference is so large ? 为什么差异如此之大?

8156 milliseconds
Laure
23567 milliseconds
Laure LINQ

Where does come from that difference ??? 那个差异来自哪里? Why results are not the same?... or at least, closer ? 为什么结果不一样?......或者至少更接近?

public static T RecursiveFirstOrDefault<T>(this T item, Func<T, IEnumerable<T>> childrenSelector, Predicate<T> condition)
    where T : class // Hierarchy implies class. Don't need to play with "default()" here.
{
    if (item == null)
    {
        return null;
    }

    if (condition(item))
    {
        return item;
    }

    foreach (T child in childrenSelector(item))
    {
        T result = child.RecursiveFirstOrDefault(childrenSelector, condition);
        if (result != null)
        {
            return result;
        }
    }

    return null;
}

But Resharper suggested me to convert the foreach block to a LINQ query as follow: 但是Resharper建议我将foreach块转换为LINQ查询,如下所示:

public static T RecursiveFirstOrDefaultLinq<T>(this T item, Func<T, IEnumerable<T>> childrenSelector, Predicate<T> condition)
    where T : class // Hierarchy implies class. Don't need to play with "default()" here.
{
    if (item == null)
    {
        return null;
    }

    if (condition(item))
    {
        return item;
    }

    // Resharper change:
    return childrenSelector(item).Select(child => child.RecursiveFirstOrDefaultLinq(childrenSelector, condition)).FirstOrDefault(result => result != null);
}

Test: 测试:

private void ButtonTest_OnClick(object sender, RoutedEventArgs e)
{
    VariationSet varSetResult;
    Stopwatch watch = new Stopwatch();

    varSetResult = null;
    watch.Start();
    for(int n = 0; n < 10000000; n++)
    {
        varSetResult = Model.VariationRef.VariationSet.RecursiveFirstOrDefault((varSet) => varSet.VariationSets,
            (varSet) => varSet.Name.Contains("Laure"));
    }
    watch.Stop();
    Console.WriteLine(watch.ElapsedMilliseconds.ToString() + " milliseconds");
    Console.WriteLine(varSetResult.Name);

    watch.Reset();

    varSetResult = null;
    watch.Start();
    for(int n = 0; n < 10000000; n++)
    {
        varSetResult = Model.VariationRef.VariationSet.RecursiveFirstOrDefaultLinq((varSet) => varSet.VariationSets,
            (varSet) => varSet.Name.Contains("Laure"));
    }
    watch.Stop();
    Console.WriteLine(watch.ElapsedMilliseconds.ToString() + " milliseconds");
    Console.WriteLine(varSetResult.Name + " LINQ");

}

I have to go for today... Hope to answer properly about tests: x86, Release on a 12 core machine, windows 7, Framework.Net 4.5, 我今天必须去...希望正确回答测试:x86,在12核机器上发布,Windows 7,Framework.Net 4.5,

My conclusion: 我的结论:

In my case it is ~3x faster in non linq version. 在我的情况下,非linq版本的速度提高约3倍。 Readability is better in LINQ but who care WHEN it is in a library where you should only remember what it does and how to call it (in this case - not a general absolute case). LINQ中的可读性更好但是谁在关注它何时只能记住它的作用以及如何调用它(在这种情况下 - 不是一般的绝对情况)。 LINQ is almost always slower than good coded method. LINQ几乎总是比良好的编码方法慢。 I would personnaly flavor: 我会个人风味:

  • LINQ: Where performance is not really an issue (most cases) in specific project code LINQ:在特定项目代码中,性能不是一个问题(大多数情况下)
  • Non Linq: Where performance is an issue in specific project code, and where used in a library and where code should be stable and fixed where usage of the method should be well documented and we should not really need to dig inside. Non Linq:性能在特定项目代码中存在的问题,以及在库中使用的地方以及代码应该稳定和固定的地方,应该很好地记录方法的使用,我们不应该真正挖掘内部。

Here are some reasons there's a difference between non-LINQ and LINQ code performance: 以下是非LINQ和LINQ代码性能之间存在差异的一些原因:

  1. Every call to a method has some performance overhead. 每次调用方法都会产生一些性能开销。 Information has to be pushed onto the stack, the CPU has to jump to a different instruction line, etc. In the LINQ version you are calling into Select and FirstOrDefault, which you are not doing in the non-LINQ version. 必须将信息压入堆栈,CPU必须跳转到不同的指令行等。在LINQ版本中,您调用Select和FirstOrDefault,而您在非LINQ版本中没有这样做。
  2. There is overhead, both time and memory, when you create a Func<> to pass into the Select method. 当您创建Func<>以传递到Select方法时,会有时间和内存的开销。 The memory overhead, when multiplied many times as you do in your benchmark, can lead to the need to run the garbage collector more often, which can be slow. 当您在基准测试中多次乘以内存开销时,可能会导致需要更频繁地运行垃圾收集器,这可能很慢。
  3. The Select LINQ method you are calling produces an object that represents its return value. 您正在调用的Select LINQ方法生成一个表示其返回值的对象。 This also adds a little memory consumption. 这也增加了一点内存消耗。

why is the difference so large? 差异为何如此之大?

It's actually not that large. 它实际上并不那么大。 True, LINQ takes 50% longer, but honestly you're talking about only being able to complete this entire recursive operation 400 times in a millisecond . 没错,LINQ的使用时间延长了50%,但老实说,你所说的只是能够在一毫秒内完成整个递归操作400次。 That's not slow, and you're unlikely to ever notice the difference unless this is an operation you're doing all the time in a high-performance application like a video game. 这并不慢,你不可能注意到这种差异,除非这是你在视频游戏等高性能应用程序中一直在做的操作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM