【问题标题】:Is LINQ slower than foreach loop for aggregation operations?LINQ 是否比聚合操作的 foreach 循环慢?
【发布时间】:2016-12-02 06:17:32
【问题描述】:

简短的问题:为什么 LINQ 比通过 foreach 循环进行聚合操作慢?

说明 -

我正在尝试优化一些旧代码,虽然 LINQ 查询在整个代码中被广泛使用,但像在 Enumerable 中求和数字这样的琐碎操作是使用循环完成的。

所以,我运行了一个小测试来比较这两种方法的性能。 下面是使用 LINQ 的 .Sum() 方法和使用 foreach 循环并手动总结每个出现次数的 1000 个可枚举长的数字总和的代码。

List<Double> numbers = new List<Double>();
Double[] sums1 = new Double[1000];
Double[] sums2 = new Double[1000];

for (int i = 0; i < 1000; i++)
{
    numbers.Add(i * i);
}

Int64 startTime1 = Stopwatch.GetTimestamp();
for (int i = 0; i < 1000; i++)
{
    Double sum = 0;
    sum = numbers.Sum();
    sums1[i] = sum;
}
Int64 endTime1 = Stopwatch.GetTimestamp();

Int64 startTime2 = Stopwatch.GetTimestamp();
for (int i = 0; i < 1000; i++)
{
    Double sum = 0;
    foreach (Double number in numbers)
    {
        sum += number;
    }
    sums2[i] = sum;
}
Int64 endTime2 = Stopwatch.GetTimestamp();

Console.WriteLine("LINQ.    Start = {0}, End = {1}: Diff = {2}", startTime1, endTime1, endTime1 - startTime1);
Console.WriteLine("ForEach. Start = {0}, End = {1}: Diff = {2}", startTime2, endTime2, endTime2 - startTime2);

我运行了这个测试几(十)次,结果是:

LINQ.    Start = 117385428996, End = 117385462197: Diff = 33201
Foreach. Start = 117385462203, End = 117385476329: Diff = 14126
LINQ.    Start = 117385478555, End = 117385499802: Diff = 21247
Foreach. Start = 117385499808, End = 117385520756: Diff = 20948
LINQ.    Start = 117385521426, End = 117385546256: Diff = 24830
Foreach. Start = 117385546260, End = 117385567052: Diff = 20792
LINQ.    Start = 117385572791, End = 117385602149: Diff = 29358
Foreach. Start = 117385602156, End = 117385622367: Diff = 20211
LINQ.    Start = 117385623153, End = 117385652563: Diff = 29410
Foreach. Start = 117385652568, End = 117385673733: Diff = 21165
LINQ.    Start = 117385674403, End = 117385705028: Diff = 30625
Foreach. Start = 117385705035, End = 117385725552: Diff = 20517
LINQ.    Start = 117385726094, End = 117385753161: Diff = 27067
Foreach. Start = 117385753166, End = 117385771824: Diff = 18658
LINQ.    Start = 117385772341, End = 117385793726: Diff = 21385
Foreach. Start = 117385793733, End = 117385811332: Diff = 17599
LINQ.    Start = 117385811768, End = 117385837204: Diff = 25436
Foreach. Start = 117385837209, End = 117385852670: Diff = 15461
LINQ.    Start = 117385853003, End = 117385874410: Diff = 21407
Foreach. Start = 117385874416, End = 117385891874: Diff = 17458

请注意,foreach 循环总是执行得更好。这可能是什么原因?

编辑:answer 到这个question 有很多很好的信息,说明为什么与常规内联操作相比性能可能会很差。但我无法看到它与这里的确切关系。

【问题讨论】:

  • LINQ performance FAQ的可能重复
  • 我用for (int x = 0; x &lt; numbers.Count; x++)添加了一个外观,它在LINQ和foreach之间
  • 顺便说一句。你编码日志ForEach,结果有Foreach。魔术;)
  • 当然,您在发布模式下运行此测试并进行了优化,不是吗?
  • Linq 正在执行动态解析,而 for 循环正在使用固定的编译代码。动态解决方案总是比经过优化的编译代码慢。

标签: c# performance linq foreach


【解决方案1】:

这似乎与您正在迭代的内容有关。我稍微改变了你的代码。首先它迭代 IEnumerable(你说 linq),然后迭代 List(你说 foreach)。 我和你得到了同样的结果。

请检查方法withIEnumerablewithList。除了签名不同之外,它们做的事情完全相同。 LINQ 扩展方法获取IEnumerable 作为参数。

编辑:Performance between Iterating through IEnumerable<T> and List<T> 很好地解释了为什么列表枚举速度更快。

class Program
{
    static void Main(string[] args)
    {
        for (int i = 0; i < 10; i++)
        {
            calculate();
        }

    }

    private static void calculate()
    {
        List<Double> numbers = new List<Double>();
        Double[] sums1 = new Double[1000];
        Double[] sums2 = new Double[1000];

        for (int i = 0; i < 1000; i++)
        {
            numbers.Add(i * i);
        }

        Int64 startTime1 = Stopwatch.GetTimestamp();
        for (int i = 0; i < 1000; i++)
        {
            sums1[i] = withIEnumerable(numbers);
        }
        Int64 endTime1 = Stopwatch.GetTimestamp();

        Int64 startTime2 = Stopwatch.GetTimestamp();
        for (int i = 0; i < 1000; i++)
        {
            sums2[i] = withList(numbers);
        }
        Int64 endTime2 = Stopwatch.GetTimestamp();


        Console.WriteLine("withIEnumerable.    Start = {0}, End = {1}: Diff = {2}", startTime1, endTime1, endTime1 - startTime1);
        Console.WriteLine("withList. Start = {0}, End = {1}: Diff = {2}", startTime2, endTime2, endTime2 - startTime2);
    }

    private static double withIEnumerable(IEnumerable<double> numbers)
    {
        double sum = 0;
        foreach (Double number in numbers)
        {
            sum += number;
        }

        return sum;
    }

    private static double withList(List<double> numbers)
    {
        double sum = 0;
        foreach (Double number in numbers)
        {
            sum += number;
        }

        return sum;
    }
}

【讨论】:

    【解决方案2】:

    这不是正确的答案,但我将把它留在这里 参考而不是删除它。请阅读 serdar 的回答( 接受一个)。如果这违反社区的工作方式,请告诉我或编辑。

    调用方法会增加非常显着的时间开销。 根据我的发现,这是在这种特殊情况下性能变慢的最大原因。

    我添加了一个扩展方法来用相同(更快)的代码总结列表,但在扩展方法中。

    public static class MyExtensionMethods
    {
        public static Double SumX(this IEnumerable<Double> list)
        {
            Double sum = 0;
            foreach (Double number in list)
            {
                sum += number;
            }
            return sum;
        }
    }
    

    然后使用我刚刚创建的扩展方法,

    ...
    Int64 startTime2 = Stopwatch.GetTimestamp();
    for (int i = 0; i < 1000; i++)
    {
        Double sum = numbers.SumX();
        sums2[i] = sum;
    }
    Int64 endTime2 = Stopwatch.GetTimestamp();
    ...
    

    结果是:

    LINQ.    Start = 129087370675, End = 129087395309: Diff = 24634
    Foreach. Start = 129087395312, End = 129087420320: Diff = 25008
    LINQ.    Start = 129087422887, End = 129087447541: Diff = 24654
    Foreach. Start = 129087447547, End = 129087465859: Diff = 18312
    LINQ.    Start = 129087466278, End = 129087484777: Diff = 18499
    Foreach. Start = 129087484784, End = 129087505378: Diff = 20594
    LINQ.    Start = 129087506425, End = 129087526134: Diff = 19709
    Foreach. Start = 129087526141, End = 129087552013: Diff = 25872
    LINQ.    Start = 129087552500, End = 129087578445: Diff = 25945
    Foreach. Start = 129087578451, End = 129087601858: Diff = 23407
    LINQ.    Start = 129087602371, End = 129087630873: Diff = 28502
    Foreach. Start = 129087630880, End = 129087674495: Diff = 43615
    LINQ.    Start = 129087675028, End = 129087702841: Diff = 27813
    Foreach. Start = 129087702849, End = 129087732360: Diff = 29511
    LINQ.    Start = 129087732974, End = 129087760529: Diff = 27555
    Foreach. Start = 129087760536, End = 129087785590: Diff = 25054
    LINQ.    Start = 129087786096, End = 129087813331: Diff = 27235
    Foreach. Start = 129087813336, End = 129087842947: Diff = 29611
    LINQ.    Start = 129087843471, End = 129087870633: Diff = 27162
    Foreach. Start = 129087870639, End = 129087896678: Diff = 26039
    

    底线:在这种情况下,LINQ 速度较慢,因为扩展方法增加了开销。我也尝试使用简单的静态方法而不是扩展方法,结果与扩展方法的结果没有任何不同。

    【讨论】:

    • 你的静态方法的输入参数是什么? IEnumerable 还是 List?我认为是 List。请尝试另一个。
    • 现在,知道为什么 IEnumerable 列表的迭代速度比列表慢吗?
    • 感谢您的接受。事实上,我不知道它的迭代速度比列表慢。但我只是试图定位问题,然后看到情况。如果您找到任何原因,请分享。
    • stackoverflow.com/questions/23536541/… 很好地解释了为什么列表枚举速度更快。
    猜你喜欢
    • 2011-03-10
    • 2022-11-17
    • 2013-06-02
    • 2020-12-02
    • 2022-01-04
    • 2020-07-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多