LINQ比聚合操作的foreach循环慢吗?

时间:2016-12-02 06:17:33

标签: c# performance linq foreach

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

说明 -

我正在尝试优化一些旧代码,而LINQ查询在整个代码中被广泛使用,例如在Enumerable中对数字求和的简单操作是使用循环完成的。

所以,我进行了一些测试来比较两种方法的性能。 以下是使用LINQ' .Sum()方法计算1000个数字长度的总和,并使用foreach循环并手动汇总每个出现的代码。

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循环始终表现得更好。这可能是什么原因?

编辑:此answerquestion有很多关于为什么与常规内联操作相比性能可能不好的良好信息。但我无法确定它与此有何关联。

2 个答案:

答案 0 :(得分:3)

似乎是关于你在迭代的内容。我略微改变了你的代码。首先,它迭代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;
    }
}

答案 1 :(得分:0)

  

这不是正确的答案,但我会把它留在这里   引用而不是删除它。请阅读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较慢,因为扩展方法增加了开销。我也尝试使用简单的静态方法而不是扩展方法,并且它的结果与扩展方法的结果没有任何不同。