c# - Linq 查询的性能(按类型)

标签 c# .net performance linq

工作中出现了一个讨论:

我们有一个包含 IList 的类。 Fact 是一个抽象基类,还有几个具体的子类(PopulationFact、GdpFact 等)。

最初我们会以这种方式查询给定的事实,即按类型:

.Facts.FirstOrDefault(x => x.Year == 2011 && x is GdpFact)

然而,现在提出了一个问题,我们是否应该引入 FactType 枚举来代替

.Facts.FirstOrDefault(x => x.Year == 2011 && x.FactType == FactType.Gdp)

之所以提出这个建议,是因为它据说更快。我承认我没有编写任何测试来尝试辨别性能差异,但我有两个问题:

1) 这样的“查询类型”本质上是不好的吗?
2) 考虑到事实是强类型的,添加 FactType 枚举不是多余的吗?

更新 澄清一下,这是 LINQ to objects 和 GdpFact:Fact。

更新 2 我们使用当前的典型数据(4 个事实)进行了测量,结果如下:

枚举查找:0.29660000000000003 毫秒 查找类型:0.24530000000000002 毫秒

所以在这种情况下类型查找更快!我会仔细选择我接受的答案。

最佳答案

我做过测试,我的1000000次迭代的结果大约是

ByCast 166ms
ByType 84ms
ByEnum 98ms

所以 enum 实际上是多余的,而且速度较慢,但​​幅度不大。这应该不足为奇,类型系统是 .Net Framework 的基础。

下面是测试代码,对错误表示歉意

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

class Program
{
    private enum TypeOfFact
    {
        Gdp,
        Other
    }

    private abstract class Fact
    {
        public virtual int Year { get; set; }
        public abstract TypeOfFact FactType { get; }
    }

    private class GdpFact : Fact
    {
        public override TypeOfFact FactType
        {
            get { return TypeOfFact.Gdp; }
        }
    }

    private class OtherFact : Fact
    {
        public override TypeOfFact FactType
        {
            get { return TypeOfFact.Other; }
        }
    }

    static void Main()
    {
        Ilist<Fact> facts = new List<Fact>
            {
                new GdpFact { Year = 2010 },
                new OtherFact { Year = 2010 },
                new GdpFact { Year = 2009 },
                new OtherFact { Year = 2009 },
                new GdpFact { Year = 2011 },
                new OtherFact { Year = 2011 },
            };

        const int interations = 1000000;

        var funcs = new List<Func<IList<Fact>, Fact>>
            {
                ByList,
                ByType,
                ByEnum
            };

        // Warmup
        foreach (var func in funcs)
        {
           Measure(5, func, facts);
        }

        // Results
        foreach (var result in funcs.Select(f => new
            {
                Description = f.Method.Name,
                Ms = Measure(iterations, f, facts)
            }))
        {
            Console.WriteLine(
                "{0} time = {1}ms",
                result.Description,
                result.Ms);
        }
    }

    private static long Measure(
        int iterations,
        Func<IList<Fact>, Fact> func,
        IList<Fact> facts)
    {
        var stopwatch = new Stopwatch();
        stopwatch.Start();
        for (var i = 0; i < iterations; i++)
        {
            func.Invoke(facts);
        }

        stopwatch.Stop();
        return stopwatch.ElapsedMilliseconds;
    }

    private static Fact ByType(IList<Fact> facts)
    {
        return facts.FirstOrDefault(f =>
            f.Year == 2011 && f is GdpFact);
    }

    private static Fact ByEnum(IList<Fact> facts)
    {
        return facts.FirstOrDefault(f =>
            f.Year == 2011 && f.FactType == TypeOfFact.Gdp);
    }

    private static Fact ByCast(IList<Fact> facts)
    {
        return facts.OfType<GdpFact>()
            .FirstOrDefault(f => f.Year == 2011);
    }
}

question似乎相关。

关于c# - Linq 查询的性能(按类型),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13609343/

相关文章:

c++ - 用于蒙特卡洛模拟的 vector 优化累积

python - 几次循环后 GPU 速度减慢

C# 动态类型——天堂还是 hell ?

c# - Ninject 3 如何影响对象的生命周期?

c# - 无法将当前json数组反序列化为类型

.net - 如何使用 Serilog ForContext

c# - LINQ - 合并 3 个数据集的查询 - 改进

.net - 从脚本或批处理文件调用 Web 服务

.net - 批量插入 Dbase (.dbf) 文件的有效方法

javascript - 如何使用延迟和内联脚本保留脚本执行顺序?