Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / C# / April 2008

Tip: Looking for answers? Try searching our database.

Delegates vs. MethodInfo When Calling Code Dynamically

Thread view: 
Enable EMail Alerts  Start New Thread
Thread rating: 
Tom Corcoran - 09 Nov 2007 18:48 GMT
I've been led to believe by several articles, particularly Eric Gunnerson's
C# Calling Code Dynamically, that calling a method dynamically through
Reflection was much slower than through a Delegate.  My testing showed that
actually it was six times faster: 0.5 seconds for 100,000 iterations versus
3.1 seconds.

Can anyone explain why?  Something in the way I coded it?  I'd appreciate
any insights.

Here's the code (in a Windows Form with two buttons).  Operating system is
Windows XP Pro.  Visual Studio 2005.  .Net Framework 2.0.  

const string OP_GREATER_THAN_OR_EQUAL = "op_GreaterThanOrEqual";
const string OP_LESS_THAN_OR_EQUAL = "op_LessThanOrEqual";
const decimal TEST_VAL = 100000.00M;
const decimal BENCHMARK_VAL = 101000.00M;
const int TEST_LIMIT = 100000;

private delegate bool OperatorTest( decimal pTestVal, decimal pBenchmarkVal );

private void btnMethodInfo_Click( object sender, EventArgs e ){
  DateTime start = DateTime.Now;
  for( int i = 0; i < TEST_LIMIT; i++ )
  {
     Type t1 = TEST_VAL.GetType();
     Type t2 = BENCHMARK_VAL.GetType();
     MethodInfo mi = typeof( decimal ).GetMethod(  
OP_GREATER_THAN_OR_EQUAL, new Type[] { t1, t2 } );
     bool isValid = ( bool )mi.Invoke( null, new object[] { TEST_VAL,
BENCHMARK_VAL } );
  }
  DateTime stop = DateTime.Now;
  Console.WriteLine( "Elapsed time by MethodInfo = " + ( stop- start ) );
}

private void btnDelegate_Click( object sender, EventArgs e ){
  DateTime start = DateTime.Now;
  for( int i = 0; i < TEST_LIMIT; i++ )
  {
     Delegate d = Delegate.CreateDelegate( typeof( OperatorTest ), typeof(
decimal ), OP_GREATER_THAN_OR_EQUAL );
     bool isValid = ( bool )d.DynamicInvoke( new object[] { TEST_VAL,
BENCHMARK_VAL } );
  }
  DateTime stop = DateTime.Now;
  Console.WriteLine( "Elapsed time by Delegate = " + ( stop - start ) );
}

Signature

Tom Corcoran

Peter Bromberg [C# MVP] - 09 Nov 2007 21:52 GMT
I think you need to go back and revisit your code. Your delegate is being
created multiple times inside the loop, and this is eating up CPU cycles.
Only need to create the delegate one time.  Haven't looked further, but there
could be other gotchas too.
-- Peter
http://www.eggheadcafe.com
unBlog: http://petesbloggerama.blogspot.com
BlogMetaFinder:    http://www.blogmetafinder.com

> I've been led to believe by several articles, particularly Eric Gunnerson's
> C# Calling Code Dynamically, that calling a method dynamically through
[quoted text clipped - 43 lines]
>    Console.WriteLine( "Elapsed time by Delegate = " + ( stop - start ) );
> }
Nicholas Paldino [.NET/C# MVP] - 10 Nov 2007 15:21 GMT
I see a few other things going on here.

   First, you (the OP) should be using the Stopwatch class in the
System.Diagnostics namespace to perform the timing.  It will provide a much
more accurate result than the DateTime class.

   Regarding the loop that calls through reflection, every iteration
through the loop is getting the decimal type three times, and then getting
the method, and then calling the method.  You want to test just calling the
method, not all those other things.  To that end, the code in that event
handler should look like this:

private void btnMethodInfo_Click( object sender, EventArgs e )
{
// The stopwatch instance.
Stopwatch sw = new Stopwatch();

// Get the type of decimal.
Type decimalType = typeof(decimal);

// Get the method info.
MethodInfo mi = decimalType.GetMethod(OP_GREATER_THAN_OR_EQUAL, new Type[]
{ decimalType, decimalType });

// NOW start the timer.
sw.Start();

// Iterate.
for( int i = 0; i < TEST_LIMIT; i++ )
{
 // Invoke the method.
 bool isValid = (bool) mi.Invoke(null, new object[] { TEST_VAL,
BENCHMARK_VAL } );
}

// Stop the timer.
sw.Stop();

// Write the result.
Console.WriteLine( "Elapsed time by MethodInfo = " + sw.Elapsed );
}

   Regarding the loop that calls through the delegate, most of what applies
in the other method applies here.  You only need to create the delegate
once, the types once, etc, etc:

private void btnDelegate_Click( object sender, EventArgs e )
{
// The stopwatch instance.
Stopwatch sw = new Stopwatch();

// Get the delegate.
Delegate d = Delegate.CreateDelegate(typeof(OperatorTest), typeof(decimal),
OP_GREATER_THAN_OR_EQUAL);

// NOW start the timer.
sw.Start();

// Iterate.
for( int i = 0; i < TEST_LIMIT; i++ )
{
 // Invoke the delegate.
 bool isValid = (bool) d.DynamicInvoke(new object[] { TEST_VAL,
BENCHMARK_VAL } );
}

// Stop the stopwatch.
sw.Stop();

// Output the result.
Console.WriteLine("Elapsed time by Delegate = " + sw.Elapsed);
}

   I think that doing it this way will produce very different (and
expected) results for the OP.

Signature

         - Nicholas Paldino [.NET/C# MVP]
         - mvp@spam.guard.caspershouse.com

>I think you need to go back and revisit your code. Your delegate is being
> created multiple times inside the loop, and this is eating up CPU cycles.
[quoted text clipped - 60 lines]
>>    Console.WriteLine( "Elapsed time by Delegate = " + ( stop - start ) );
>> }
Tom Corcoran - 12 Nov 2007 13:50 GMT
Nicholas and Peter, thank you both very much.  Nicholas, thank you in
particular for taking the time to code a better version and for teaching me
about Stopwatch.  Using your code and Stopwatch, the elapsed time of the
Delegates method was still about 3.5 times SLOWER than that of the MethodInfo
method.  This seems counter to all that I have heard about Reflection and
Delegates.  I'd appreciate any other ideas you might have, as I'm putting a
change into an Xml web service that will likely get a high traffic needing a
dynamically called comparison operator.
Signature

Tom Corcoran

>     I see a few other things going on here.
>
[quoted text clipped - 136 lines]
> >>    Console.WriteLine( "Elapsed time by Delegate = " + ( stop - start ) );
> >> }
Jon Skeet [C# MVP] - 12 Nov 2007 13:57 GMT
On Nov 12, 1:50 pm, Tom Corcoran
<TomCorco...@discussions.microsoft.com> wrote:
> Nicholas and Peter, thank you both very much.  Nicholas, thank you in
> particular for taking the time to code a better version and for teaching me
[quoted text clipped - 4 lines]
> change into an Xml web service that will likely get a high traffic needing a
> dynamically called comparison operator.

Could you show your new complete code? It would be useful to have a
copy that we can run ourselves. Was your timing performed within the
debugger or not?

Jon
Tom Corcoran - 12 Nov 2007 14:11 GMT
Jon,

Thanks for your reply.  Here is the new code as rewritten by Nicholas.  The
timing was performed within the debugger.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

namespace DelegatesProblem
{
   public partial class Form1 : Form
   {
       const string OP_GREATER_THAN_OR_EQUAL = "op_GreaterThanOrEqual";
       const string OP_LESS_THAN_OR_EQUAL = "op_LessThanOrEqual";
       const decimal TEST_VAL = 100000.00M;
       const decimal BENCHMARK_VAL = 101000.00M;
       const int TEST_LIMIT = 100000;

       private delegate bool OperatorTest( decimal pTestVal, decimal
pBenchmarkVal );

       public Form1()
       {
           InitializeComponent();
       }

       private void btnMethodInfo_Click( object sender, EventArgs e )
       {
           this.Cursor = Cursors.WaitCursor;
           Stopwatch sw = new Stopwatch();
           Type decimalType = typeof( decimal );
           MethodInfo mi = decimalType.GetMethod(OP_GREATER_THAN_OR_EQUAL,
new Type[]{decimalType, decimalType} );
           sw.Start();
           for( int i = 0; i < TEST_LIMIT; i++ )
           {
               bool isValid = ( bool )mi.Invoke( null, new object[] {
TEST_VAL, BENCHMARK_VAL } );
           }
           sw.Stop();
           Console.WriteLine( "Elapsed time by MethodInfo = " + sw.Elapsed );
           this.Cursor = Cursors.Default;
       }

       private void btnDelegate_Click( object sender, EventArgs e )
       {
           this.Cursor = Cursors.WaitCursor;
           Stopwatch sw = new Stopwatch();
           Delegate d = Delegate.CreateDelegate( typeof( OperatorTest ),
typeof( decimal ), OP_GREATER_THAN_OR_EQUAL );
           sw.Start();
           for( int i = 0; i < TEST_LIMIT; i++ )
           {
               bool isValid = ( bool )d.DynamicInvoke( new object[] {
TEST_VAL, BENCHMARK_VAL } );
           }
           sw.Stop();
           Console.WriteLine( "Elapsed time by Delegate = " + sw.Elapsed );
           this.Cursor = Cursors.Default;
       }

       private void btnDone_Click( object sender, EventArgs e )
       {
           this.Close();
           this.Dispose();
       }
   }
}
Signature

Tom Corcoran

> On Nov 12, 1:50 pm, Tom Corcoran
> <TomCorco...@discussions.microsoft.com> wrote:
[quoted text clipped - 12 lines]
>
> Jon
Jon Skeet [C# MVP] - 12 Nov 2007 14:32 GMT
On Nov 12, 2:11 pm, Tom Corcoran
<TomCorco...@discussions.microsoft.com> wrote:
> Thanks for your reply.  Here is the new code as rewritten by Nicholas.  The
> timing was performed within the debugger.

Four things:

1) Code within a Windows Form (or any MarshalByRefComponent) has a few
optimisations disabled.
2) It's easier to compile and run short console apps :)
3) Performance testing in a debugger is a really bad idea
4) Calling DynamicInvoke isn't the same as directly calling the
delegate in a strongly-typed manner

Here's the equivalent code, rewritten as a console app, including a
direct invocation, and with more iterations to give more useful
results:

using System;
using System.Diagnostics;
using System.Reflection;

class Benchmark
{
   const string OpGreaterThanOrEqual = "op_GreaterThanOrEqual";

   const int Iterations = 1000000;
   const decimal FirstValue = 100000.00m;
   const decimal SecondValue = 101000.00m;

   private delegate bool OperatorTest (decimal first, decimal
second);

   static void Main()
   {
       RunDelegateDynamic();
       RunDelegateDirect();
       RunReflection();
   }

   static void RunDelegateDirect()
   {
       OperatorTest d = (OperatorTest) Delegate.CreateDelegate
           (typeof(OperatorTest), typeof(decimal),
OpGreaterThanOrEqual);

       Stopwatch sw = Stopwatch.StartNew();
       for (int i = 0; i < Iterations; i++)
       {
           d(FirstValue, SecondValue);
       }
       sw.Stop();
       Console.WriteLine ("Direct invoke: "+sw.ElapsedMilliseconds);
   }

   static void RunDelegateDynamic()
   {
       Delegate d = Delegate.CreateDelegate (typeof(OperatorTest),
                                             typeof(decimal),
                                             OpGreaterThanOrEqual);
       Stopwatch sw = Stopwatch.StartNew();
       for (int i = 0; i < Iterations; i++)
       {
           d.DynamicInvoke(new object[] { FirstValue, SecondValue });
       }
       sw.Stop();
       Console.WriteLine ("Dynamic invoke: "+sw.ElapsedMilliseconds);
   }

   static void RunReflection()
   {
       MethodInfo mi =
typeof(decimal).GetMethod(OpGreaterThanOrEqual,
           new Type[] {typeof(decimal), typeof(decimal) });
       Stopwatch sw = Stopwatch.StartNew();
       for (int i = 0; i < Iterations; i++)
       {
           mi.Invoke(null, new object[] { FirstValue, SecondValue });
       }
       sw.Stop();
       Console.WriteLine ("Reflection: "+sw.ElapsedMilliseconds);
   }
}

Here are the results on my box, not running
Dynamic invoke: 5433
Direct invoke: 82
Reflection: 2048

So yes, calling DynamicInvoke on a delegate is still slower than using
reflection - I suspect it's using reflection under the hood, but with
some extra hoops. However, calling the delegate *directly* (which is
what you should be aiming to do normally) is much, much faster than
reflection.

Jon
Tom Corcoran - 12 Nov 2007 16:22 GMT
Jon,

I got comparable results.  Thank you for showing me these things.  I know a
bit more about Delegates now and much more about running benchmark tests.  I
appreciate your time on it.

Sincerely,
Tom Corcoran

> On Nov 12, 2:11 pm, Tom Corcoran
> <TomCorco...@discussions.microsoft.com> wrote:
[quoted text clipped - 92 lines]
>
> Jon
Jon Skeet [C# MVP] - 12 Nov 2007 19:39 GMT
> I got comparable results.  Thank you for showing me these things.  I know a
> bit more about Delegates now and much more about running benchmark tests.  I
> appreciate your time on it.

No problem. I'm a big fan of small console apps, because you basically
don't need any more code than what you're actually trying to run - no
messing around with buttons, events and the like :)

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Creativ - 13 Nov 2007 20:08 GMT
I've seen three ways. MethodInfo and strong type delegate make sense.
In which situation do you need dynamicInvoke
Jon Skeet [C# MVP] - 13 Nov 2007 20:15 GMT
> I've seen three ways. MethodInfo and strong type delegate make sense.
> In which situation do you need dynamicInvoke  

When you don't know the type of the delegate at compile-time.

Signature

Jon Skeet - <skeet@pobox.com>
http://www.pobox.com/~skeet   Blog: http://www.msmvps.com/jon.skeet
If replying to the group, please do not mail me too

Creativ - 15 Nov 2007 08:00 GMT
> > I've seen three ways. MethodInfo and strong type delegate make sense.
> > In which situation do you need dynamicInvoke  
[quoted text clipped - 4 lines]
> Jon Skeet - <sk...@pobox.com>http://www.pobox.com/~skeet  Blog:http://www.msmvps.com/jon.skeet
> If replying to the group, please do not mail me too

Can you give me an example? I don't see it. I thought
MethodInfo( reflection) will just do it and faster.
Marc Gravell - 15 Nov 2007 08:33 GMT
But MethodInfo is a different beast to a Delegate.

I'm not sure it is a good example (I would use typed On{blah} method
myself), but one /potential/ candidate for this would be a
general-purpose event invoke from EventHandlerList...

       protected void OnEvent(object key, object sender, EventArgs
args) {
           Delegate handler = Events[key];
           if (handler != null) {
               handler.DynamicInvoke(sender, args);
           }
       }

Other use-cases might include scenarios such as implementing
ISynchronizeInvoke:

   public interface ISynchronizeInvoke {
       bool InvokeRequired { get; }
       IAsyncResult BeginInvoke(Delegate method, object[] args);
       object EndInvoke(IAsyncResult result);
       object Invoke(Delegate method, object[] args);
   }

I'm not saying that they are common to write, but certainly the latter
gets /used/ very often (think System.Windows.Forms.Control)

Marc
Jon Skeet [C# MVP] - 15 Nov 2007 08:34 GMT
> > > I've seen three ways. MethodInfo and strong type delegate make sense.
> > > In which situation do you need dynamicInvoke
[quoted text clipped - 3 lines]
> Can you give me an example? I don't see it. I thought
> MethodInfo( reflection) will just do it and faster.

Consider trying to implement ISynchronizeInvoke.Invoke - you're
presented with a Delegate, and an object[]. How are you going to
execute the delegate? I suppose you *could* get the invocation list
and execute it that way, but I suspect that's basically what
DynamicInvoke does under the hood anyway.

Jon
Valeriu Lacatusu - 13 Apr 2008 04:38 GMT
Hello
I've changed some of the values used in your benchmark test(i.e: the delegate type and some of the constants) to match a more realistic situation(upon my opinion...) for the type of function we are trying to execute using invocation and i've obtained different results regarding the difference between the direct invocation and the other 2 types.

Dynamic invoke: 12976
Direct invoke: 12505
Reflection: 13332

Could someone confirm my results and suggest some reason for that?

using System;
using System.Diagnostics;
using System.Reflection;

class Benchmark
{
   const string OpGreaterThanOrEqual = "op_GreaterThanOrEqual";

   const int Iterations = 100000;//changed
   const int V1 = 100000;
   const int V2 = 100000;
   const decimal FirstValue = 100000.00m;
   const decimal SecondValue = 101000.00m;

   private delegate bool OperatorTest(decimal first, decimal second);
   private delegate void Blah(int first, int second);//added

   static void Main()
   {
       RunDelegateDynamic();
       RunDelegateDirect();
       RunReflection();
       Console.ReadLine();
   }

   //Some more time consuming function
   public static void BlahTst(int first, int second)
   {
       int res = 0;
       for (int i = 0; i < 100000; i++)
       {
           if (first < second)
           {
               res = -1;
           }
           else
           {
               res = 1;
           }
       }
   }
   static void RunDelegateDirect()
   {              
       Blah d = (Blah)Delegate.CreateDelegate(typeof(Blah), (Type)typeof(Benchmark), "BlahTst");

       Stopwatch sw = Stopwatch.StartNew();
       for (int i = 0; i < Iterations; i++)
       {
           d(V1, V2);
       }
       sw.Stop();
       Console.WriteLine("Direct invoke: " + sw.ElapsedMilliseconds);
       
       /*
       OperatorTest d = (OperatorTest)Delegate.CreateDelegate
       (typeof(OperatorTest), typeof(decimal), OpGreaterThanOrEqual);

       Stopwatch sw = Stopwatch.StartNew();
       for (int i = 0; i < Iterations; i++)
       {
           d(FirstValue, SecondValue);
       }
       sw.Stop();
       Console.WriteLine("Direct invoke: " + sw.ElapsedMilliseconds);
        */
   }

   static void RunDelegateDynamic()
   {
       Delegate d = Delegate.CreateDelegate(typeof(Blah), (Type)typeof(Benchmark), "BlahTst");
       Stopwatch sw = Stopwatch.StartNew();
       for (int i = 0; i < Iterations; i++)
       {
           d.DynamicInvoke(new object[] { V1, V2 });
       }
       sw.Stop();
       Console.WriteLine("Dynamic invoke: " + sw.ElapsedMilliseconds);

       /*
       Delegate d = Delegate.CreateDelegate(typeof(OperatorTest), typeof(decimal), OpGreaterThanOrEqual);
       Stopwatch sw = Stopwatch.StartNew();
       for (int i = 0; i < Iterations; i++)
       {
           d.DynamicInvoke(new object[] { FirstValue, SecondValue });
       }
       sw.Stop();
       Console.WriteLine("Dynamic invoke: " + sw.ElapsedMilliseconds);
        */
   }

   static void RunReflection()
   {
       MethodInfo mi = typeof(Benchmark).GetMethod("BlahTst",
       new Type[] { typeof(int), typeof(int) });
       Stopwatch sw = Stopwatch.StartNew();
       for (int i = 0; i < Iterations; i++)
       {
           mi.Invoke(null, new object[] { V1, V2 });
       }
       sw.Stop();
       Console.WriteLine("Reflection: " + sw.ElapsedMilliseconds);

       /*
       MethodInfo mi = typeof(decimal).GetMethod(OpGreaterThanOrEqual,
       new Type[] { typeof(decimal), typeof(decimal) });
       Stopwatch sw = Stopwatch.StartNew();
       for (int i = 0; i < Iterations; i++)
       {
           mi.Invoke(null, new object[] { FirstValue, SecondValue });
       }
       sw.Stop();
       Console.WriteLine("Reflection: " + sw.ElapsedMilliseconds);
        */
   }
}
Jon Skeet [C# MVP] - 13 Apr 2008 15:35 GMT
> Hello
> I've changed some of the values used in your benchmark test(i.e: the delegate type and some of the constants) to match a more realistic situation(upon my opinion...) for the type of function we are trying to execute using invocation and i've obtained different results regarding the difference between the direct invocation and the other 2 types.
[quoted text clipped - 4 lines]
>
> Could someone confirm my results and suggest some reason for that?

Yes - the bulk of the time is now spent within the BlahTst method
itself. Calling the method dynamically doesn't make the method itself
run any slower - it just means the time taken to transfer execution
from the calling method to BlahTst is slower. When that becomes less
significant (because BlahTst is doing more work) you'll see less
difference in the results - as your numbers demonstrate.

Jon

Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.