lambda 函数是否比委托/匿名函数更快? [英] Are lambda functions faster than delegates/anonymous functions?

查看:22
本文介绍了lambda 函数是否比委托/匿名函数更快?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我假设 lambda 函数delegatesanonymous 函数 具有相同的主体将具有相同的速度",但是,运行以下简单程序:

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();
}

我明白了:

委托:4.2948 毫秒

Delegate: 4.2948 ms

Lambda:0.0019 毫秒

Lambda: 0.0019 ms

匿名:0.0034 毫秒

Anonymous: 0.0034 ms

虽然可以忽略不计,但为什么这三种(显然相同)方法以不同的速度运行?幕后发生了什么?

Although negligible, why are these three - apparently identical - methods running at different speeds? What's happening under the hood?

更新:

正如评论所建议的,以下通过调用 ToList() 来强制"Where.此外,添加了一个循环以提供更多运行数据:

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));

}

上面的代码导致每个函数大约需要 120 毫秒.

The above code results in ~120 ms for each function.

推荐答案

lambda 表达式一个匿名函数.匿名函数"指的是 lambda 表达式或匿名方法(这就是在代码中所说的委托").

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).

所有三个操作都使用委托.第二个和第三个都使用 lambda 表达式.三者都将以相同的方式执行,具有相同的性能特征.

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:

Func<int, int> func = x => ...;
for (int i = 0; i < 10000; i++) {
    CallFunc(func);
}

for (int i = 0; i < 10000; i++) {
    CallFunc(x => ...) // Same lambda as before
}

这取决于编译器是否能够缓存由 lambda 表达式创建的委托.这反过来又取决于它是否捕获变量等.

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.

例如,考虑以下代码:

using System;
using System.Diagnostics;

class Test
{
    const int Iterations = 1000000000;

    static void Main()
    {
        AllocateOnce();
        AllocateInLoop();
    }

    static void AllocateOnce()
    {
        int x = 10;

        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        Func<int, int> allocateOnce = y => y + x;
        for (int i = 0; i < Iterations; i++)
        {
            sum += Apply(i, allocateOnce);
        }
        sw.Stop();
        Console.WriteLine("Allocated once: {0}ms", sw.ElapsedMilliseconds);
    }

    static void AllocateInLoop()
    {
        int x = 10;

        Stopwatch sw = Stopwatch.StartNew();
        int sum = 0;
        for (int i = 0; i < Iterations; i++)
        {
            sum += Apply(i, y => y + x);
        }
        sw.Stop();
        Console.WriteLine("Allocated in loop: {0}ms", sw.ElapsedMilliseconds);
    }

    static int Apply(int loopCounter, Func<int, int> func)
    {
        return func(loopCounter);
    }
}

编译器很聪明,但还是有区别的.使用 Reflector,我们可以看到 AllocateInLoop 被有效地编译为:

The compiler is smart, but there's still a difference. Using Reflector, we can see that AllocateInLoop is effectively compiled to:

private static void AllocateInLoop()
{
    Func<int, int> func = null;
    int x = 10;
    Stopwatch stopwatch = Stopwatch.StartNew();
    int sum = 0;
    for (int i = 0; i < Iterations; i++)
    {
        if (func == null)
        {
            func = y => y + x;
        }
        sum += Apply(i, func);
    }
    stopwatch.Stop();
    Console.WriteLine("Allocated in loop: {0}ms", stopwatch.ElapsedMilliseconds);
}

所以仍然只创建了一个委托实例,但循环中有额外的逻辑 - 基本上每次迭代都进行额外的无效测试.

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.

在我的机器上,性能大约有 15% 的差异.

On my machine that makes about a 15% difference in performance.

这篇关于lambda 函数是否比委托/匿名函数更快?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆