DataBinder and Speed

    In many projects, there is a need to access the properties of objects using the mechanism System.Web.UI.DataBinder.Evaland the question arose: how fast does it work and is it possible to perform such an operation even faster?

    So, a little test was done. To begin with, we made 2 classes (for testing virtual and overridden properties), the properties of which we will refer to:
      public class c1
      {
        string _str;
        public c1(string str)
        {
          _str        = str;
        }
        public string Str1     { get { return "1: "+_str; } }
        public virtual string Str2 { get { return "2: "+_str; } }
      }

      public class c2 : c1
      {
        public c2(string str)
          : base(str)
        {
        }
        public override string Str2 { get { return "C"+base.Str2; } }
      }

    Now the actual test:
    c2 c        = new c2("str");

    // Прямой доступ к свойствам
    for (int i=0; i<ITERATIONS; i++)
    {
      String.Format("Str1: {0}, Str2: {1}", c.Str1, c.Str2);
    }
    // Доступ через DataBinder
    for (int i=0; i<ITERATIONS; i++)
    {
      String.Format("Str1: {0}, Str2: {1}", DataBinder.Eval(c, "Str1"), DataBinder.Eval(c, "Str2"));
    }

    Result:
    • Direct call: 00: 00: 00.065 (100%)
    • DataBinder.Eval: 00: 00: 01.135 ( 1746% )

    Wow, 17 times slower! Although something similar was supposed.
    To increase the speed, it was first proposed to cache calls to Reflection (where would it be without such a task), but this did not give serious acceleration. Then the approach was tested with the generation of the access code on the fly, that is:
    1. At the first call, we study the class and property through Reflection
    2. We go to the class where the property was defined or redefined
    3. We build a class that consists of one method: get the value of the desired property from an object of a certain type
    4. We call the constructed method

    I will not give the generation code in the topic, who are interested in downloading the Project (29Kb) .
    c2 c        = new c2("str");
    PropertyAccessor p1  = PEval.GetPropertyAccessor(c.GetType(), "Str1");
    PropertyAccessor p2  = PEval.GetPropertyAccessor(c.GetType(), "Str2");
    for (int i=0; i<ITERATIONS; i++)
    {
      String.Format("Str1: {0}, Str2: {1}", p1.GetValue( c ), p2.GetValue( c ));
    }

    Execution Result:
    • Cached Call: 00: 00: 00.065 (100%)

    Excellent result! The speed is comparable to a direct call, with several starts this time was sometimes less than 100%. He called the call cached since the class with the method was remembered and it was constantly accessed without checking the existence of such a class.
    To complete the experiment, a static class was written that cached the constructed classes and created them as needed.
    c2 c        = new c2("str");
    for (int i=0; i<ITERATIONS; i++)
    {
      String.Format("Str1: {0}, Str2: {1}", PEval.GetValue(c, "Str1"), PEval.GetValue(c, "Str2"));
    }

    Execution Result:
    • PEval.GetValue: 00: 00: 00.090 (138%)

    Just 38% slower than direct calling. Several launches gave an average result of 150%, but still this is not 1746%.
    Full test output: 2 million calls: And 10 times more: What else can I add:
    Create PropertyAccessor for TestDataBinding.c1.Str1
    Create PropertyAccessor for TestDataBinding.c2.Str2
    =========================
    ITERATIONS: 100000
    Прямой вызов: 00:00:00.065 (100%)
    PEval.GetValue: 00:00:00.090 (138%)
    Кэшированный вызов: 00:00:00.065 (100%)
    DataBinder.Eval: 00:00:01.135 (1746%)
    =========================
    TestDataBinding.c2 (TestDataBinding.PropEvaluator.PropertyAccessor) [200003]
    TestDataBinding.c1_Str1_Accessor => Str1 (G) [100002]
    TestDataBinding.c2_Str2_Accessor => Str2 (G) [100001]
    TestDataBinding.c1 (TestDataBinding.PropEvaluator.PropertyAccessor) [2]
    TestDataBinding.c1_Str1_Accessor => Str1 (G) [100002]
    =========================



    =========================
    ITERATIONS: 1000000
    Прямой вызов: 00:00:00.930 (100%)
    PEval.GetValue: 00:00:01.085 (116%)
    Кэшированный вызов: 00:00:00.738 (79%)
    DataBinder.Eval: 00:00:10.976 (1180%)
    =========================



    =========================
    ITERATIONS: 10000000
    Прямой вызов: 00:00:06.802 (100%)
    PEval.GetValue: 00:00:10.917 (160%)
    Кэшированный вызов: 00:00:07.017 (103%)
    DataBinder.Eval: 00:01:45.476 (1550%)
    =========================



    1. Make methods to change the value of a property
    2. Extend the class of access to the property with additional functions: the display name of the property, formatting the value into a string / parsing the value from the string, validating the value
    3. Make a call not of one property, but chains PEval.GetValue(o, "Prop1.Prop2.Prop4")


    Appendix 1.
    Class diagram of a more advanced version:
    Class Diagram for Property Access

    Class diagram of a code generator:
    image

    Also popular now: