Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are Func<> delegates so much slower

I was in the process of moving repeated arithmetic code into reusable chunks using funcs but when I ran a simple test to benchmark if it will be any slower, I was surprised that it is twice as slow.

Why is evaluating the expression twice as slow

using System;
using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Exporters;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Running;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var summary = BenchmarkRunner.Run<Calculations>();

            var logger = ConsoleLogger.Default;
            MarkdownExporter.Console.ExportToLog(summary, logger);

            Console.WriteLine(summary);
        }
    }

    public class Calculations
    {
        public Random RandomGeneration = new Random();

        [Benchmark]
        public void CalculateNormal()
        {
           var s =  RandomGeneration.Next() * RandomGeneration.Next();
        }

        [Benchmark]
        public void CalculateUsingFunc()
        {
            Calculate(() => RandomGeneration.Next() * RandomGeneration.Next());
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int Calculate(Func<int> expr)
        {
            return expr();
        }
    }
}

Below is the benchmark: enter image description here

like image 227
Ricky Gummadi Avatar asked Aug 03 '20 13:08

Ricky Gummadi


People also ask

What is the difference between Func and Action delegate?

An Action type delegate is the same as Func delegate except that the Action delegate doesn't return a value. In other words, an Action delegate can be used with a method that has a void return type. It can contain minimum 1 and maximum of 16 input parameters and does not contain any output parameter.

Are delegates slow?

Delegates are too damn slow; Delegates are faster because they are only a pointer to a method. Interfaces need to use a v-table to then find a delegate; They are equal, but delegates are easier to use.

What is the difference between func string string and delegate?

The basic difference between Func and Action delegates is that while the former is used for delegates that return value, the latter can be used for those delegates in which you don't have any return value.

Why we use Func delegates in C#?

Func is generally used for those methods which are going to return a value, or in other words, Func delegate is used for value returning methods. It can also contain parameters of the same type or of different types.

What is the difference between Func and Func delegate?

Func is generally used for those methods which are going to return a value, or in other words, Func delegate is used for value returning methods. It can also contain parameters of the same type or of different types.

What is the last parameter of the func delegate?

The last parameter of the Func delegate is the out parameter which is considered as return type and used for the result. Func is generally used for those methods which are going to return a value, or in other words, Func delegate is used for value returning methods. It can also contain parameters of the same type or of different types.

How to assign an anonymous method to a func delegate?

A Func delegate type can include 0 to 16 input parameters of different types. However, it must include an out parameter for the result. For example, the following Func delegate doesn't have any input parameter, and it includes only an out parameter. You can assign an anonymous method to the Func delegate by using the delegate keyword.

What is the difference between Action delegate and a delegate?

A Delegate is a variable that holds a reference to a method and is used for event and call-back methods. The methods that the Delegates reference need to have matching data types. An Action Delegate holds a reference to a method that can take in a value but doesn’t have to and does not return a value.


1 Answers

You're creating a new delegate object on every call. It's not surprising that that has a fair amount of overhead.

If you either use a lambda expression that doesn't capture this or any local variables (in which case the compiler can cache it in a static field) or if you explicitly create a single instance and store that in a field yourself, most of the overhead goes away.

Here's a modified version of your test:

public class Calculations
{
    public Random RandomGeneration = new Random();
    private Func<int> exprField;
    
    public Calculations()
    {
        exprField = () => RandomGeneration.Next() * RandomGeneration.Next();
    }
    
    [Benchmark]
    public void CalculateNormal()
    {
       var s =  RandomGeneration.Next() * RandomGeneration.Next();
    }

    [Benchmark]
    public void CalculateUsingFunc()
    {
        Calculate(() => RandomGeneration.Next() * RandomGeneration.Next());
    }
    
    [Benchmark]
    public void CalculateUsingFuncField()
    {
        Calculate(exprField);
    }

    
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int Calculate(Func<int> expr)
    {
        return expr();
    }
}

And the results on my machine:

|                  Method |     Mean |    Error |   StdDev |
|------------------------ |---------:|---------:|---------:|
|         CalculateNormal | 27.61 ns | 0.438 ns | 0.388 ns |
|      CalculateUsingFunc | 48.74 ns | 1.009 ns | 0.894 ns |
| CalculateUsingFuncField | 32.53 ns | 0.698 ns | 0.717 ns |

So there's still a bit of overhead, but much, much less than before.

like image 193
Jon Skeet Avatar answered Oct 23 '22 01:10

Jon Skeet