lambda 函数比委托/匿名函数更快吗?
我假设具有相同主体的 lambda 函数、委托和匿名函数具有相同的“速度”,但是运行以下简单程序:
static void Main(string[] args)
{
List<int> items = new List<int>();
Random random = new Random();
for (int i = 0; i < 10000000; i++)
{
items.Add(random.Next());
}
Stopwatch watch;
IEnumerable<int> result;
Func<int, bool> @delegate = delegate(int i)
{
return i < 500;
};
watch = Stopwatch.StartNew();
result = items.Where(@delegate);
watch.Stop();
Console.WriteLine("Delegate: {0}", watch.Elapsed.TotalMilliseconds);
Func<int, bool> lambda = i => i < 500;
watch = Stopwatch.StartNew();
result = items.Where(lambda);
watch.Stop();
Console.WriteLine("Lambda: {0}", watch.Elapsed.TotalMilliseconds);
watch = Stopwatch.StartNew();
result = items.Where(i => i < 500);
watch.Stop();
Console.WriteLine("Inline: {0}", watch.Elapsed.TotalMilliseconds);
Console.ReadLine();
}
我得到:
委托:4.2948 毫秒
Lambda:0.0019 毫秒
匿名:0.0034 毫秒
虽然可以忽略不计,但为什么这三个(显然相同)方法以不同的速度运行?幕后发生了什么?
更新:
正如评论所建议的,以下内容通过调用 ToList()
来“强制” Where
。此外,还添加了一个循环以提供更多运行数据:
while (true)
{
List<int> items = new List<int>();
Random random = new Random();
for (int i = 0; i < 10000000; i++)
{
items.Add(random.Next());
}
Stopwatch watch;
IEnumerable<int> result;
Func<int, bool> @delegate = delegate(int i)
{
return i < 500;
};
watch = Stopwatch.StartNew();
result = items.Where(@delegate).ToList();
watch.Stop();
Console.WriteLine("Delegate: {0}", watch.Elapsed.TotalMilliseconds);
Func<int, bool> lambda = i => i < 500;
watch = Stopwatch.StartNew();
result = items.Where(lambda).ToList();
watch.Stop();
Console.WriteLine("Lambda: {0}", watch.Elapsed.TotalMilliseconds);
watch = Stopwatch.StartNew();
result = items.Where(i => i < 500).ToList();
watch.Stop();
Console.WriteLine("Inline: {0}", watch.Elapsed.TotalMilliseconds);
Console.WriteLine(new string('-', 12));
}
上述代码导致每个函数大约需要 120 毫秒。
I assumed lambda functions
, delegates
and anonymous functions
with the same body would have the same "speed", however, running the following simple program:
static void Main(string[] args)
{
List<int> items = new List<int>();
Random random = new Random();
for (int i = 0; i < 10000000; i++)
{
items.Add(random.Next());
}
Stopwatch watch;
IEnumerable<int> result;
Func<int, bool> @delegate = delegate(int i)
{
return i < 500;
};
watch = Stopwatch.StartNew();
result = items.Where(@delegate);
watch.Stop();
Console.WriteLine("Delegate: {0}", watch.Elapsed.TotalMilliseconds);
Func<int, bool> lambda = i => i < 500;
watch = Stopwatch.StartNew();
result = items.Where(lambda);
watch.Stop();
Console.WriteLine("Lambda: {0}", watch.Elapsed.TotalMilliseconds);
watch = Stopwatch.StartNew();
result = items.Where(i => i < 500);
watch.Stop();
Console.WriteLine("Inline: {0}", watch.Elapsed.TotalMilliseconds);
Console.ReadLine();
}
I get:
Delegate: 4.2948 ms
Lambda: 0.0019 ms
Anonymous: 0.0034 ms
Although negligible, why are these three - apparently identical - methods running at different speeds? What's happening under the hood?
Update:
As suggested by the comments, the following "forces" the Where
by calling ToList()
on it. In addition, a loop is added to offer more run data:
while (true)
{
List<int> items = new List<int>();
Random random = new Random();
for (int i = 0; i < 10000000; i++)
{
items.Add(random.Next());
}
Stopwatch watch;
IEnumerable<int> result;
Func<int, bool> @delegate = delegate(int i)
{
return i < 500;
};
watch = Stopwatch.StartNew();
result = items.Where(@delegate).ToList();
watch.Stop();
Console.WriteLine("Delegate: {0}", watch.Elapsed.TotalMilliseconds);
Func<int, bool> lambda = i => i < 500;
watch = Stopwatch.StartNew();
result = items.Where(lambda).ToList();
watch.Stop();
Console.WriteLine("Lambda: {0}", watch.Elapsed.TotalMilliseconds);
watch = Stopwatch.StartNew();
result = items.Where(i => i < 500).ToList();
watch.Stop();
Console.WriteLine("Inline: {0}", watch.Elapsed.TotalMilliseconds);
Console.WriteLine(new string('-', 12));
}
The above code results in ~120 ms for each function.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
其他人的结果表明性能是相同的:
http://blogs.microsoft.co.il/blogs/alex_golesh/archive/2007/12/11/anonymous-delegates-vs-lambda-expressions-vs-function-calls-performance.aspx< /a>
正如评论中所指出的,微基准常常具有误导性。有太多你无法控制的因素,JIT 优化、垃圾收集周期等......
请参阅此相关问题:
何时不使用 lambda 表达式
最后,我认为您的测试存在根本缺陷!您使用 Linq
Where
扩展方法来执行你的代码。然而,Linq 使用惰性求值,只有当您开始迭代结果时,您的代码才会被执行!Other peoples results suggest that the performance is the same:
http://blogs.microsoft.co.il/blogs/alex_golesh/archive/2007/12/11/anonymous-delegates-vs-lambda-expressions-vs-function-calls-performance.aspx
As noted in the comments, micro-benchmarks are often misleading. There are too many factors over which you have no control, JIT optimisation, garbage collection cycles, etc ...
See this related question:
When not to use lambda expressions
Finally, I think your test is fundamentally flawed! You use a Linq
Where
extension method to execute your code. However, Linq uses lazy-evaluation, your code will only be executed if you start iterating over the results!lambda 表达式是一个匿名函数。 “匿名函数”指的是 lambda 表达式或匿名方法(这就是您在代码中所谓的“委托”)。
所有三个操作都使用委托。第二个和第三个都使用 lambda 表达式。这三者将以相同的方式执行,具有相同的性能特征。
请注意,以下两者之间的性能可能存在差异:
和
这取决于编译器是否能够缓存由 lambda 表达式创建的委托。这又取决于它是否捕获变量等。
例如,考虑这段代码:
编译器很聪明,但仍然有区别。使用 Reflector,我们可以看到
AllocateInLoop
被有效地编译为:因此仍然只创建了一个委托实例,但循环内有额外的逻辑 - 基本上每次迭代都有一个额外的无效测试。
在我的机器上,性能大约有 15% 的差异。
A lambda expression is an anonymous function. "Anonymous function" refers to either a lambda expression or an anonymous method (which is what you've called a "delegate" in your code).
All three operations are using delegates. The second and third are both using lambda expressions. All three will execute in the same way, with the same performance characteristics.
Note that there can be a difference in performance between:
and
It depends on whether the compiler is able to cache the delegate created by the lambda expression. That will in turn depend on whether it captures variables etc.
For example, consider this code:
The compiler is smart, but there's still a difference. Using Reflector, we can see that
AllocateInLoop
is effectively compiled to:So still only a single delegate instance is created, but there's extra logic within the loop - an extra nullity test on each iteration, basically.
On my machine that makes about a 15% difference in performance.