简短的问题 :为什么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
循环始终表现得更好。这可能是什么原因?
编辑:此answer的question有很多关于为什么与常规内联操作相比性能可能不好的良好信息。但我无法确定它与此有何关联。
答案 0 :(得分:3)
似乎是关于你在迭代的内容。我略微改变了你的代码。首先,它迭代IEnumerable(你说linq
),然后迭代List(你说foreach
)。 我和你的结果相同。
请检查方法withIEnumerable
和withList
。除了签名不同之外,它们完全相同。 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较慢,因为扩展方法增加了开销。我也尝试使用简单的静态方法而不是扩展方法,并且它的结果与扩展方法的结果没有任何不同。