× {{alert.msg}} Never ask again
Get notified about new tutorials RECEIVE NEW TUTORIALS

What overhead do you pay when passing a function to a Func<> implicitly in a method call?

James Jensen
Feb 10, 2015
<blockquote> <p>Every time I call ForceDelegateConsumption(), what happens internally? Am I creating a new Func object for every single call?</p> </blockquote> <p>Yes, but this tends to be a pretty inexpensive operation: you're just converting the "Method Group" into a <code>Func&lt;&gt;</code>. It's likely as fast or faster than whatever alternative you were considering.</p> <p>Here's a benchmark to demonstrate. The times are so fast that I'm including a no-op so that we can capture how much of the time is spend in the benchmarking overhead:</p> <pre><code>void Main() { // Enter setup code here Func&lt;object&gt; cachedDoesSomething = DoesSomething; var actions = new[] { new TimedAction("No-op", () =&gt; { }), new TimedAction("method call", () =&gt; { DoesSomething(); }), new TimedAction("ForceDelegateConsumption", () =&gt; { ForceDelegateConsumption(); }), new TimedAction("ForceDelegateConsumptionInline", () =&gt; { ConsumesDelegate(DoesSomething); }), new TimedAction("DoesSomethingInlined", () =&gt; { ConsumesDelegate(() =&gt; null); }), new TimedAction("CachedLambda", () =&gt; { ConsumesDelegate(cachedDoesSomething); }), new TimedAction("Explicit lambda", () =&gt; { ConsumesDelegate(() =&gt; DoesSomething()); }), }; const int TimesToRun = 10000000; // Tweak this as necessary TimeActions(TimesToRun, actions); } public void ConsumesDelegate (Func&lt;object&gt; consumer) { consumer(); } public object DoesSomething() { return null; } public void ForceDelegateConsumption() { ConsumesDelegate(DoesSomething); } </code></pre> <p>Results:</p> <pre><code>Message DryRun1 DryRun2 FullRun1 FullRun2 No-op 0.046 0.0004 56.5705 57.3681 method call 0.1294 0.0004 96.169 98.9377 ForceDelegateConsumption 0.2555 0.0004 315.6183 284.0828 ForceDelegateConsumptionInline 0.1997 0.0012 278.4389 263.8278 DoesSomethingInlined 0.2909 0.0008 145.8749 152.2732 CachedLambda 0.1388 0.0004 125.7794 135.8126 Explicit lambda 0.2444 0.0004 308.683 304.2111 </code></pre> <p>All of the "FullRun" numbers are the milliseconds that it takes to run the method <em>ten million times</em>.</p> <p>So you can see that my benchmark framework takes 50 ms even if we do nothing at all. Simply calling the <code>DoSomething</code> method, assuming it does nothing at all, takes another 50 ms to run ten million times. It then takes an additional 200 ms or less to call <code>ConsumesDelegate</code>, passing <code>DoSomething</code> to it as a method group (also ten million times).</p> <p>If you're writing code that will get invoked many millions of times in a performance-critical path, you'll want to consider avoiding lambdas entirely. They add a tiny bit of memory overhead, and a tiny bit of extra CPU time. Otherwise, I wouldn't bother avoiding method-group conversion.</p> <p>This tip was originally posted on <a href="http://stackoverflow.com/questions/22490328/What%20overhead%20do%20you%20pay%20when%20passing%20a%20function%20to%20a%20Func&lt;&gt;%20implicitly%20in%20a%20method%20call?/22490412">Stack Overflow</a>.</p>
comments powered by Disqus