如果你在Linq中搜索得更快,那么答案永远不会是一个foreach。我还发现了另一个stackoverflow问题问题asker had not done a "warmup"所以我已经包含了#34;热身"在我的代码中。
由于某些原因,我的代码示例没有按照我的预期行事。我正在思考我所做的是让无linq路径循环两次 - 一次是第一次,一次是总和。其中linq示例在它结束时仅在结尾处循环一次。你怎么看?我的测试是否存在缺陷,或者这是linq实际上为我们带来了良好的性能提升的情况?
public class NumberObject { public Int32 Number { get; set; } }
public IEnumerable<NumberObject> GetNumbersWithoutLambda()
{
IEnumerable<Int32> numberRange = Enumerable.Range(0,10);
List<NumberObject> numberList = new List<NumberObject>();
foreach (Int32 item in numberRange)
{
numberList.Add(new NumberObject() { Number = item });
}
return numberList;
}
public IEnumerable<NumberObject> GetNumbersWithLambda()
{
IEnumerable<Int32> numberRange = Enumerable.Range(0, 10);
IEnumerable<NumberObject> numbers = numberRange.
Select(number => new NumberObject() { Number = number });
return numbers;
}
private void runGetNumbers(Func<IEnumerable<NumberObject>> getNumbersFunction, Int32 numberOfTimesToRun)
{
for (int i = 0; i < numberOfTimesToRun; i++)
{
IEnumerable<NumberObject> numbers = getNumbersFunction();
//numbers.Count();
numbers.Sum(item => item.Number);
//numbers.Average(item => item.Number);
}
}
[TestMethod]
public void record_speed_of_GetNumbers()
{
Int32 numberOfTimes = 10000000;
Console.WriteLine("Doing warmup... " +
TimeMethod(() => runGetNumbers(GetNumbersWithLambda, numberOfTimes)));
Console.WriteLine("GetNumbersWithoutLambda: " +
TimeMethod(() => runGetNumbers(GetNumbersWithoutLambda, numberOfTimes)) + " milliseconds");
Console.WriteLine("GetNumbersWithLambda: " +
TimeMethod(() => runGetNumbers(GetNumbersWithLambda, numberOfTimes)) + " milliseconds");
}
static long TimeMethod(Action methodToTime)
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
methodToTime();
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
以下是测试的输出:
做热身...... 7560
GetNumbersWithoutLambda: 14779毫秒
GetNumbersWithLambda: 7626毫秒
有趣的是,#34;热身&#34;在这种情况下,实际上似乎并不适用。
答案 0 :(得分:11)
当LINQ可以利用延迟执行时,它通常会更快;就像它在这里一样。
你怀疑; foreach
完全枚举代码中的集合。 Select
只是构建一个查询来执行该枚举。
然后当您调用Sum
时,它会枚举先前生成的集合。我们foreach
共有2个枚举,而Select
只有一个枚举,因此它更快(按2倍!)
还有其他例子; Take
和First
将提前停止执行(foreach
可以提前停止,但大多数人都不会这样编码)。
基本上,当实际需要枚举整个集合以及您想要枚举(使用)LINQ查询时,请使用foreach
。在运行查询和操作时使用LINQ,其中延迟执行将为您带来性能优势。当该查询返回集合时,使用foreach
迭代 it 。
答案 1 :(得分:6)
您正在比较苹果和橘子,Linq不使用List&lt;&gt;就像你的“非lambda”版本一样。该清单不是免费的。
你需要这样写:
public IEnumerable<NumberObject> GetNumbersWithoutLambda() {
IEnumerable<Int32> numberRange = Enumerable.Range(0, 10);
foreach (Int32 item in numberRange) {
yield return new NumberObject() { Number = item };
}
}
现在需要相同的时间。是的,Linq也使用了迭代器。
然而,对于未经过验证的版本而言,它并没有结束,它的强度五倍
:static int sum; // Ensures summing doesn't get optimized away
private void runGetNumbers2(Int32 numberOfTimesToRun) {
for (int i = 0; i < numberOfTimesToRun; i++) {
foreach (var number in Enumerable.Range(0, 10)) {
sum += number;
}
}
}
通过删除Enumerable.Range使其快三次次:
for (int i = 0; i < numberOfTimesToRun; i++) {
for (int j = 0; j < 10; ++j) {
sum += j;
}
}
这表明迭代器使用的状态机也不是免费的。这里的基本前提是简单的代码很快。
答案 2 :(得分:5)
不同之处在于,即使List实现了IEnumerable,它也必须在方法返回之前完全填充,Linq方法只需要在返回之前构造表达式树。
考虑并计算以下时间:
public IEnumerable<NumberObject> GetNumbersWithLambdaToList()
{
IEnumerable<Int32> numberRange = Enumerable.Range(0, 10);
IEnumerable<NumberObject> numbers = numberRange.
Select(number => new NumberObject() { Number = number });
return numbers.ToList();
}
public IEnumerable<NumberObject> GetNumbersWithYield()
{
IEnumerable<Int32> numberRange = Enumerable.Range(0,10);
foreach (Int32 item in numberRange)
{
yield return (new NumberObject() { Number = item });
}
}
在我的机器上:
GetNumbersWithoutLambda: 9631 milliseconds
GetNumbersWithLambda: 7285 milliseconds
GetNumbersWithLambdaToList: 12998 milliseconds
GetNumbersWithYield: 9236 milliseconds
答案 3 :(得分:0)
不完全是。这取决于你对Linq做了什么
如果您只是使用foreach
来迭代和修改集合中的项目而不创建新集合。然后Linq一般比较慢
但是大多数人倾向于在漫长的逻辑中创建集合以进行迭代。分配内存并使其比使用Linq慢,因为Linq不创建真正的集合,它只记得在foreach
如果没有必要,过度使用Linq有时会减速。因为使用Linq创建委托对象并导致堆栈分配