lundi 13 mai 2019

Execution time: direct vs reflection vs delegate

While studying reflection and delegates, I stumbled upon the first example of this Jon Skeet's post.

While Reflection is notorious for being slow, especially for repeated invocations, I do not really understand how "wrapping" the reflected method into a Delegate can help in avoiding its high computational cost, like suggested in that post first example:

MethodInfo method = typeof(string).GetMethod("IndexOf", new Type[] { typeof(char) });

Func<char, int> converted = (Func<char, int>)
            Delegate.CreateDelegate(typeof(Func<char, int>), str, method);

So I've built a short comparision test for myself based on that example. I'm trying to compare different calls to string's IndexOf method: A) Direct; B) Using reflection; C) Using a delegate.

/*
Based on 
https://codeblog.jonskeet.uk/2008/08/09/making-reflection-fly-and-exploring-delegates/
*/
public class Test
{
    public static void Main()
    {
        // Testing the performance of different calls to string method "IndexOf"

        string str = "Hello World";

        Stopwatch s = new Stopwatch();
        float elapsed = 0;
        int iterations = 10000000;

        //---------
        // A) Direct
        //---------
        for (int i = 0; i < iterations; i++)
        {
            // Average to reduce noise error
            s.Start();
            str.IndexOf('H');
            s.Stop();
            elapsed += s.Elapsed.Ticks;
            s.Reset();
        }

        Console.WriteLine("A) Direct: " + (elapsed / iterations).ToString());

        //---------
        // B) Reflection
        //---------

        MethodInfo method = typeof(string).GetMethod("IndexOf", new Type[] { typeof(char) });

        for (int i = 0; i < iterations; i++)
        {
            // Average to reduce noise error
            s.Start();
            method.Invoke(str, new object[] { 'H' });
            s.Stop();
            elapsed += s.Elapsed.Ticks;
            s.Reset();
        }

        Console.WriteLine("B) Reflection: " + (elapsed / iterations).ToString());


        //---------
        // C) Delegate
        //---------

        Func<char, int> converted = (Func<char, int>)
            Delegate.CreateDelegate(typeof(Func<char, int>), str, method);

        for (int i = 0; i < iterations; i++)
        {
            // Average to reduce noise error
            s.Start();
            converted('H');
            s.Stop();
            elapsed += s.Elapsed.Ticks;
            s.Reset();
        }

        Console.WriteLine("C) Delegate: " + (elapsed / iterations).ToString());
    }
}


On my machine, a typical run would return the following:

A) Direct: 0.3110177  
B) Reflection: 3.13324  
C) Delegate: 3.154247

Having read that blog post, I was expecting that kind of difference between A) and B); however, I do not understand why I don't get any improvement between B) and C).

Questions

1) Is my testing/measurement strategy correct for this purpose?
1a) If yes, why I am not getting a good difference between B) and C)?

2) I would like an in-depth explanation of why using the delegate form C) should be faster than B). What is happening behind the curtains?

Additional notes

When answering, especially question 2), consider that I am kind of a beginner in both reflection and delegates. Please try to be exhaustive but understandable.

I have done my research on other Stackoverflow questions pivoting around these topics: Performance of reflection method call vs delegate call; What is the "cost" of .NET reflection?; How costly is .NET reflection?

However, none of them was enough for me to get my head around the example above.





Aucun commentaire:

Enregistrer un commentaire