9

I would like to know if there is any overhead incurred through the use of anonymous methods when creating a Background worker.

for example:

public void SomeMethod()
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += (sender, e) =>
    {
        //large amount of code
    }

    worker.RunWorkerAsync();
}

Would the example above be any better or worse than defining the //large amount of code in a separate method?

Is there any overhead incurred in defining the background worker method in-line, particularly if SomeMethod() is called often?

Lewray
  • 1,164
  • 1
  • 11
  • 26
  • 1
    If there is a large amount of code you probably want to be refactoring it into a small amount of code calling other methods... – Chris Feb 22 '12 at 12:48

8 Answers8

6

There is a small difference in how named methods and anonumous methods are handled when you create a delegate from them.

Delegates for anonymous methods are cached, so there is a small overhead for checking if the delegate already exists in the cache. On the other hand, if you run the method more than once, it will reuse the cached delegate instead of creating a new one.

Delegates for named methods are not cached, so it will be created each time.

Other than that there is no difference. The anonumous method will be created at compile time and exists in the code just like a regular method, only with a name that only the compiler knows about.

Guffa
  • 687,336
  • 108
  • 737
  • 1,005
2

First, you probably shouldn't put a lot of code into an anonymous method. It would be more readable if you create a separate method for that, or even better, several methods.

As for the IL generated, if the lambda doesn't close over any variables, then the generated IL code is the same as if you put the code in normal named method (except that the generated method has an unspeakable name).

On the other hand, if you do close over some variable, the compiler creates a closure class to hold that variable in a field. And field access is slightly more expensive that local variable access.

To sum up, if you close over some variables, there is small overhead (including more objects that need to be garbage collected). In most situations, this doesn't matter and worrying about this would be premature optimization. But if you think it does matter, you should profile the code.

svick
  • 236,525
  • 50
  • 385
  • 514
1

Whenever an anonymous method (incl lambdas) close over variables the compiler creates a class to hold these variables for you. Whenever the delegate is created a new instance of this class is too. This obviously adds extra work for the runtime, but is generally negligible in most situations.

Rich O'Kelly
  • 41,274
  • 9
  • 83
  • 114
  • 1
    So I assume that if there is no closure that it won't create the class so it won't make any difference? – Chris Feb 22 '12 at 12:50
  • @Chris Yes, the compiler is smart enough to create an anonymous type (to hold the anonymous method plus variables closed over) only when it's required. – Rich O'Kelly Feb 22 '12 at 12:53
  • The closure class is not created when you create the delegate. It's created before you use the closed-over local, because accessing locals in the “parent” method has to actually use the closure class. – svick Feb 22 '12 at 12:54
0

There is no overhead. Anonymous method is more efficient because it is always resolved to static method but this only matters in very rare scenarios.

See delegates versus anonymous methods performance peculiarity and Performance of using static methods vs instantiating the class containing the methods.

itix
  • 59
  • 11
0

My concern has always been - using delegates in a loop. Turns out the compiler is smart enough to cache anonymous functions even when used in a loop.

Here's my code (10 million iterations):

var dummyVar = 0;
var sw = new Stopwatch();

//delegate inside loop
sw.Start();
for(int i=0; i<10000000; i++)
{
    Func<int, int> ax = delegate (int x) { return x++; };
    dummyVar = ax(i);
}
sw.Stop();
var ms = sw.ElapsedMilliseconds;

//delegate outside loop
Func<int, int> ax2 = delegate (int x) { return x++; };
sw.Restart();
for (int i = 0; i < 10000000; i++)
{
    dummyVar = ax2(i);
}
sw.Stop();
var ms2 = sw.ElapsedMilliseconds;

Results:

1st: 151 milliseconds

2nd: 148 milliseconds

2nd run

1st: 149 milliseconds

2nd: 175 milliseconds (SLOWER this time... who knew)

Alex from Jitbit
  • 53,710
  • 19
  • 160
  • 149
0

I was testing this out the other day (via use of the StopWatch class). As far as I could tell there was no noticeable difference in performance between invoking a method directly...

SomeMethod();

...or via an anonymous method...

() => SomeMethod();
Moonshield
  • 925
  • 9
  • 16
  • 2
    The compiler will optimise out the need to create an anonymous class to hold the anonymous method since no variables are closed over. – Rich O'Kelly Feb 22 '12 at 12:51
  • Interesting, I thought the compiler may be having a hand in that. Two questions: if the body of the anonymous method references variables outside its scope, will that introduce an overhead? And, if so, when would that overhead be encountered? During creation or execution of the anonymous method? – Moonshield Feb 22 '12 at 13:09
0

It's mainly affecting readability - large amounts of code in one place are almost never good ;-)

In terms of performance see When is optimization premature?

Community
  • 1
  • 1
Matthias
  • 12,053
  • 4
  • 49
  • 91
0

This is what decompiler said:

[CompilerGenerated]
private static DoWorkEventHandler CS$<>9__CachedAnonymousMethodDelegate1;

[CompilerGenerated]
private static void <SomeMethod1>b__0(object sender, DoWorkEventArgs e)
{
    throw new NotImplementedException();
}

public void SomeMethod1()
{
    BackgroundWorker worker = new BackgroundWorker();
    BackgroundWorker backgroundWorker = worker;
    backgroundWorker.DoWork += (object sender, DoWorkEventArgs e) => throw new NotImplementedException();
    worker.RunWorkerAsync();
}

public void SomeMethod2()
{
    BackgroundWorker worker = new BackgroundWorker();
    worker.DoWork += worker_DoWork;
    worker.RunWorkerAsync();
}

private void worker_DoWork(object sender, DoWorkEventArgs e)
{
    throw new NotImplementedException();
} 

Edit:

Looking at IL code there is only small overhead in creating/assigning method to delegate for the first time.

Meonester
  • 220
  • 1
  • 3