lambdas vs. method groups
Update: Due to a glitch in my code I miscalculated the difference. It has been updated. See full history of the code in the Gist. The outcome is still essentially the same :)
I was talking with David Fowler when he mentioned something I found surprising: “Lambdas are more efficient than Method Groups”. My initial reaction was that he was entirely wrong, but he explained why and then I decided to do some analysis. Here’s what I found.
I wrote a simple program that would pass a method group in to a method in a big loop:
for (int i = 0; i < Iterations; i++)
{
Call(Dump);
}
And later on, it tried the same with a simple lambda
for (int i = 0; i < Iterations; i++)
{
Call(s => Dump(s));
}
Where Call takes an Action<string> and Dump takes a single string argument and just does nothing:
private static void Call(Action<string> act)
{
act("Hi!");
}
private static void Dump(string str)
{
}
See the full code for the program as a Gist.
I put in some simple Stopwatch-based timing and Console.ReadLine calls to allow me to advance the app through the steps slowly. The results from the Stopwatch (which timed the whole process and took an average) were interesting on their own. I pre-JIT the methods by calling them outside the loops first, and with 100,000,000 iterations, I got this output:
Running Method Group Test
Finished Method Group Test
Elapsed: 1.5171E+006ns
Average: 1.5171E-002ns
High-Precision? Yes
Press Enter to Continue
Running Lambda Test
Finished Lambda Test
Elapsed: 9.9125E+005ns
Average: 9.9125E-003ns
High-Precision? Yes
Press Enter to End
The lambda case is noticibly faster! Note the difference in exponent (we’re dealing with tiny numbers). Ok, so not much faster, but still, that’s a significant result.
Now, what about memory. So I pulled up perfmon and added the .NET “Allocated Bytes/sec” counter. I’m not sure if that’s the right one, but it certainly seemed to illustrate the point. This is what I saw:
The big spike at the beginning (highlighted in Orange) is the Method Group round. The flat line (highlighted in Green) is the Lambda round. This must be where the problem lies.
Sure enough, looking at the code, we see something interesting. To simplify, I’ve made simple instance methods that just perform the call with a method group and a lambda (see the Gist). Now to decompile them.
.method private hidebysig instance void MethodGroup() cil managed
{
.maxstack 8
L_0000: nop
L_0001: ldnull
L_0002: ldftn void Curious.Program::Dump(string)
L_0008: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int)
L_000d: call void Curious.Program::Call(class [mscorlib]System.Action`1<string>)
L_0012: nop
L_0013: ret
}
First the one with the Method Group (above). Hmm… at L_0008 we see that we’re creating a new delegate and putting the function pointer to Dump in. Make sense. So why doesn’t the lambda version create the same amount of memory?
.method private hidebysig instance void Lambda() cil managed
{
.maxstack 8
L_0000: nop
L_0001: ldsfld class [mscorlib]System.Action`1<string> Curious.Program::CS$<>9__CachedAnonymousMethodDelegate5
L_0006: brtrue.s L_001b
L_0008: ldnull
L_0009: ldftn void Curious.Program::<Lambda>b__4(string)
L_000f: newobj instance void [mscorlib]System.Action`1<string>::.ctor(object, native int)
L_0014: stsfld class [mscorlib]System.Action`1<string> Curious.Program::CS$<>9__CachedAnonymousMethodDelegate5
L_0019: br.s L_001b
L_001b: ldsfld class [mscorlib]System.Action`1<string> Curious.Program::CS$<>9__CachedAnonymousMethodDelegate5
L_0020: call void Curious.Program::Call(class [mscorlib]System.Action`1<string>)
L_0025: nop
L_0026: ret
}
Well, it certainly does more. But the key parts start at L_0001. The first thing this code does is check if the “CS$<>9__CachedAnonymousMethodDelegate5” member has a value. If not, it fills it in, at L_0014. But if it DOES have a value, the code just invokes that cached value! BINGO! The Method Group version allocates a new object every single time it is run whereas the lambda version uses an instance (or static, as necessary) field to cache the delegate.
So, what does this mean for you? Well, probably not much. Notice that all these numbers were tiny, so we’re talking about micro-optimizations. In fact, the most important take-away here is to remember that just because something seems faster, doesn’t mean it is faster. In David’s case, he discovered this in SignalR because they have a non-trivial number of lambdas allocated per connection, and there can be a LOT of connections. I know that I’ll probably switch to using Lambdas, just because it doesn’t seem to have any ill effects and if it saves us a few bytes, why not.
Please do note that I’m no performance expert. If you see a problem in this post, please tell me! It’s possible I’ve gotten this completely wrong somehow, so if this kind of performance is crucial to your app, you should definitely do your own profiling! And remember, the only way to make performance gains is to measure measure measure. You can’t just eyeball perf ;).
refer to:http://vibrantcode.com/2013/02/19/lambdas-vs-method-groups/
lambdas vs. method groups的更多相关文章
- What's New for Visual C# 6.0
https://msdn.microsoft.com/en-us/library/hh156499.aspx nameof You can get the unqualified string nam ...
- C#6.0 VS2015
https://msdn.microsoft.com/en-us/library/hh156499(v=vs.140).aspx This page lists key feature names f ...
- New Language Features in C# 6
Source:https://github.com/dotnet/roslyn/wiki/New-Language-Features-in-C%23-6 This document describes ...
- TestNG官方文档中文版(5)-测试方法/类和组
5 - Test methods, Test classes and Test groups 5.1 - Test groups TestNG容许执行复杂的测试方法分组.不仅可以申明方法属于组,而且可 ...
- Java 8特性探究(1):通往lambda之路与 lambda表达式10个示例
本文由 ImportNew 函数式接口 函数式接口(functional interface 也叫功能性接口,其实是同一个东西).简单来说,函数式接口是只包含一个方法的接口.比如Java标准库中的ja ...
- 【转】Java 8十个lambda表达式案例
1. 实现Runnable线程案例 使用() -> {} 替代匿名类: //Before Java 8: new Thread(new Runnable() { @Override public ...
- Kotlin 语言高级安卓开发入门
过去一年,使用 Kotlin 来为安卓开发的人越来越多.即使那些现在还没有使用这个语言的开发者,也会对这个语言的精髓产生共鸣,它给现在 Java 开发增加了简单并且强大的范式.Jake Wharton ...
- TestNG官方文档中文版(2)-annotation(转)
1. 介绍 TestNG是一个设计用来简化广泛的测试需求的测试框架,从单元测试(隔离测试一个类)到集成测试(测试由有多个类多个包甚至多个外部框架组成的整个系统,例如运用服务器). 编写一个测试的 ...
- [LINQ]查询关键字
摘自https://msdn.microsoft.com/zh-cn/library/bb310804.aspx,方便以后翻阅. from子句 查询表达式必须以 from 子句开头.另外,查询表达式还 ...
随机推荐
- django显示SQL语句
django显示SQL语句 有时候我们使用模型查询数据,但是并不知道具体执行的SQL语句到底对不对.那么可以通过下面的方法打印出具体执行的SQL语句.这样有助于调试: queryset = MyMod ...
- vue2.0读书笔记1-基础
一.概述 二.模版语法 三.计算属性 四.class与style绑定 五.条件渲染 六.列表渲染 七.事件处理器 八.表单控件绑定 九.组件 一.概述 在底层的实现上, Vue 将模板编译成虚 ...
- Physik
0.重点词汇 Der Ortsvektor:位置矢量 Die Verschiebung:位移 Die Geschwendigkeit:速度 Die Beschleunigung:加速度 Die Kre ...
- Codeforces 985G. Team Players
Description 有 \(n\) 个人 , \(m\) 对人有冲突 , 你要从这 \(n\) 个人中选出三个人成为一组 , 使得同一组的人不存在一对有冲突 题面 Solution 容斥 答案=总 ...
- 移动端的touchstart,touchmove,touchend事件中的获取当前touch位置
前提:touchstart,touchmove,touchend这三个事件可以通过原生和jq绑定. 原生:document.querySelector("#aa").addEven ...
- Win7系统下网站发布IIS配置
*本帖为个人收集贴,所有版权归:西门的后花园 http://ons.me* Technorati 标记: IIS,网站,发布,配置 一.首先是安装IIS.打开控制面板,找到“程序与功能”,点进去 二. ...
- 十一、curator recipes之联锁InterProcessMultiLock
简介 curator实现了一个类似容器的锁InterProcessMultiLock,它可以把多个锁包含起来像一个锁一样进行操作,简单来说就是对多个锁进行一组操作.当acquire的时候就获得多个锁资 ...
- 01-Web客户端与服务器详解
1.CS与BS 软件使用方式上两种划分 C/S架构 Client/ServerPC客户端.服务器架构 特点: 在服务器当中就主要是一个数据库,把所有的业务逻辑以及界面都交给客户端完成 优点: 较为安全 ...
- HDU 2045 RPG难题
http://acm.hdu.edu.cn/showproblem.php?pid=2045 这道题也是用倒推: 先假设前n-2个块都已经涂好,涂第n-1块时有以下两种情况: 1.n-1和1相同,则n ...
- 教程:让你的表单升级到CSS3和HTML5客户端验证
今天我们一起来看看如何创建一个实用并且功能强大的表单,表单使用如今最热门的技术HTML5和css3来创建,并且可以通过HTML5进行客户端验证. 查看预览下载附件 第一步:策划表单功能 首先,我们得为 ...
