编译的C#Lambda表达式性能

编程入门 行业动态 更新时间:2024-10-19 09:44:03
本文介绍了编译的C#Lambda表达式性能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

请考虑对集合进行以下简单操作:

Consider the following simple manipulation over a collection:

static List<int> x = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; var result = x.Where(i => i % 2 == 0).Where(i => i > 5);

现在让我们使用表达式.以下代码大致等效:

Now let's use Expressions. The following code is roughly equivalent:

static void UsingLambda() { Func<IEnumerable<int>, IEnumerable<int>> lambda = l => l.Where(i => i % 2 == 0).Where(i => i > 5); var t0 = DateTime.Now.Ticks; for (int j = 1; j < MAX; j++) var sss = lambda(x).ToList(); var tn = DateTime.Now.Ticks; Console.WriteLine("Using lambda: {0}", tn - t0); }

但是我想即时构建表达式,所以这是一个新的测试:

But I want to build the expression on-the-fly, so here's a new test:

static void UsingCompiledExpression() { var f1 = (Expression<Func<IEnumerable<int>, IEnumerable<int>>>)(l => l.Where(i => i % 2 == 0)); var f2 = (Expression<Func<IEnumerable<int>, IEnumerable<int>>>)(l => l.Where(i => i > 5)); var argX = Expression.Parameter(typeof(IEnumerable<int>), "x"); var f3 = Expression.Invoke(f2, Expression.Invoke(f1, argX)); var f = Expression.Lambda<Func<IEnumerable<int>, IEnumerable<int>>>(f3, argX); var c3 = f.Compile(); var t0 = DateTime.Now.Ticks; for (int j = 1; j < MAX; j++) var sss = c3(x).ToList(); var tn = DateTime.Now.Ticks; Console.WriteLine("Using lambda compiled: {0}", tn - t0); }

当然,它与上面的不完全一样,因此,为了公平起见,我对第一个进行了一些修改:

Of course it isn't exactly like the above, so to be fair, I modify the first one slightly:

static void UsingLambdaCombined() { Func<IEnumerable<int>, IEnumerable<int>> f1 = l => l.Where(i => i % 2 == 0); Func<IEnumerable<int>, IEnumerable<int>> f2 = l => l.Where(i => i > 5); Func<IEnumerable<int>, IEnumerable<int>> lambdaCombined = l => f2(f1(l)); var t0 = DateTime.Now.Ticks; for (int j = 1; j < MAX; j++) var sss = lambdaCombined(x).ToList(); var tn = DateTime.Now.Ticks; Console.WriteLine("Using lambda combined: {0}", tn - t0); }

现在得出MAX = 100000,VS2008,调试打开的结果:

Now comes the results for MAX = 100000, VS2008, debugging ON:

Using lambda compiled: 23437500 Using lambda: 1250000 Using lambda combined: 1406250

并关闭调试:

Using lambda compiled: 21718750 Using lambda: 937500 Using lambda combined: 1093750

惊奇.编译后的表达式比其他表达式慢大约17倍.现在出现了问题:

Surprise. The compiled expression is roughly 17x slower than the other alternatives. Now here comes the questions:

  • 我在比较非对等表达式吗?
  • 是否存在使.NET优化"编译表达式的机制?
  • 如何以编程方式表示相同的链调用l.Where(i => i % 2 == 0).Where(i => i > 5);?
  • 更多统计信息. Visual Studio 2010,调试打开,优化关闭:

    Some more statistics. Visual Studio 2010, debugging ON, optimizations OFF:

    Using lambda: 1093974 Using lambda compiled: 15315636 Using lambda combined: 781410

    调试打开,优化打开:

    Using lambda: 781305 Using lambda compiled: 15469839 Using lambda combined: 468783

    关闭调试,打开优化:

    Using lambda: 625020 Using lambda compiled: 14687970 Using lambda combined: 468765

    新惊喜.从VS2008(C#3)切换到VS2010(C#4),使UsingLambdaCombined比本地lambda更快.

    New Surprise. Switching from VS2008 (C#3) to VS2010 (C#4), makes the UsingLambdaCombined faster than the native lambda.

    好吧,我找到了一种将lambda编译性能提高一个数量级以上的方法.这是一个提示;运行探查器后,有92%的时间用于:

    Ok, I've found a way to improve the lambda compiled performance by more than an order of magnitude. Here's a tip; after running the profiler, 92% of the time is spent on:

    System.Reflection.Emit.DynamicMethod.CreateDelegate(class System.Type, object)

    Hmmmm ...为什么要在每次迭代中创建一个新的委托?我不确定,但是解决方案会在另一篇文章中介绍.

    Hmmmm... Why is it creating a new delegate in every iteration? I'm not sure, but the solution follows in a separate post.

    推荐答案

    是不是内部lambda尚未编译?!?这是概念证明:

    Could it be that the inner lambdas are not being compiled?!? Here's a proof of concept:

    static void UsingCompiledExpressionWithMethodCall() { var where = typeof(Enumerable).GetMember("Where").First() as System.Reflection.MethodInfo; where = where.MakeGenericMethod(typeof(int)); var l = Expression.Parameter(typeof(IEnumerable<int>), "l"); var arg0 = Expression.Parameter(typeof(int), "i"); var lambda0 = Expression.Lambda<Func<int, bool>>( Expression.Equal(Expression.Modulo(arg0, Expression.Constant(2)), Expression.Constant(0)), arg0).Compile(); var c1 = Expression.Call(where, l, Expression.Constant(lambda0)); var arg1 = Expression.Parameter(typeof(int), "i"); var lambda1 = Expression.Lambda<Func<int, bool>>(Expression.GreaterThan(arg1, Expression.Constant(5)), arg1).Compile(); var c2 = Expression.Call(where, c1, Expression.Constant(lambda1)); var f = Expression.Lambda<Func<IEnumerable<int>, IEnumerable<int>>>(c2, l); var c3 = f.Compile(); var t0 = DateTime.Now.Ticks; for (int j = 1; j < MAX; j++) { var sss = c3(x).ToList(); } var tn = DateTime.Now.Ticks; Console.WriteLine("Using lambda compiled with MethodCall: {0}", tn - t0); }

    现在计时是:

    Using lambda: 625020 Using lambda compiled: 14687970 Using lambda combined: 468765 Using lambda compiled with MethodCall: 468765

    哇!它不仅速度快,而且比本地lambda更快. (抓头).

    Woot! Not only it is fast, it is faster than the native lambda. (Scratch head).

    当然,上面的代码写起来实在太痛苦了.让我们做一些简单的魔术:

    Of course the above code is simply too painful to write. Let's do some simple magic:

    static void UsingCompiledConstantExpressions() { var f1 = (Func<IEnumerable<int>, IEnumerable<int>>)(l => l.Where(i => i % 2 == 0)); var f2 = (Func<IEnumerable<int>, IEnumerable<int>>)(l => l.Where(i => i > 5)); var argX = Expression.Parameter(typeof(IEnumerable<int>), "x"); var f3 = Expression.Invoke(Expression.Constant(f2), Expression.Invoke(Expression.Constant(f1), argX)); var f = Expression.Lambda<Func<IEnumerable<int>, IEnumerable<int>>>(f3, argX); var c3 = f.Compile(); var t0 = DateTime.Now.Ticks; for (int j = 1; j < MAX; j++) { var sss = c3(x).ToList(); } var tn = DateTime.Now.Ticks; Console.WriteLine("Using lambda compiled constant: {0}", tn - t0); }

    以及一些时间安排,VS2010,优化打开,调试关闭:

    And some timings, VS2010, Optimizations ON, Debugging OFF:

    Using lambda: 781260 Using lambda compiled: 14687970 Using lambda combined: 468756 Using lambda compiled with MethodCall: 468756 Using lambda compiled constant: 468756

    现在您可能会说我不是动态生成整个表达式;而是只是链接调用.但是在上面的示例中,我生成了整个表达式.和时间匹配.这只是编写更少代码的捷径.

    Now you could argue that I'm not generating the whole expression dynamically; just the chaining invocations. But in the above example I generate the whole expression. And the timings match. This is just a shortcut to write less code.

    据我所知,正在发生的事情是.Compile()方法不会将编译传播到内部lambda,因此会不断调用CreateDelegate.但是要真正理解这一点,我很乐意让.NET专家对正在进行的内部工作发表一些评论.

    From my understanding, what is going on is that the .Compile() method does not propagate the compilations to inner lambdas, and thus the constant invocation of CreateDelegate. But to truly understand this, I would love to have a .NET guru comment a little about the internal stuff going on.

    为什么为什么,哦为什么现在比本地lambda快!?

    And why, oh why is this now faster than a native lambda!?

    更多推荐

    编译的C#Lambda表达式性能

    本文发布于:2023-11-08 07:33:49,感谢您对本站的认可!
    本文链接:https://www.elefans.com/category/jswz/34/1568740.html
    版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
    本文标签:表达式   性能   Lambda

    发布评论

    评论列表 (有 0 条评论)
    草根站长

    >www.elefans.com

    编程频道|电子爱好者 - 技术资讯及电子产品介绍!