📜 ⬆️ ⬇️

DataBinder and speed

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

So, a small test was made. To begin with, we made 2 classes (for testing virtual and redefined properties), whose properties will be addressed 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:

Wow, 17 times slower! Although something similar was supposed.
To increase the speed, it was first suggested to cache the calls to Reflection (where without it in such a task), but this did not give a serious acceleration. Then the approach was tested with the generation of the circulation code on the fly, that is:
  1. At the first appeal, 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 of an object of a certain type
  4. 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 ));
}

Result of performance:

Excellent result! The speed is comparable to a direct call, with several launches this time was sometimes less than 100%. I called the call cached because the class with the method was memorized and there was a constant call without checking for 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" ));
}

Result of performance:

Only 38% slower than a direct call. Several launches gave an average result of 150%, but still this is not 1746%.
Full test output:
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]
=========================


2 million calls:
=========================
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%)
=========================


And another 10 times more:
=========================
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%)
=========================


What else can you add:
  1. Make methods to change the value of a property
  2. Extend the property access class 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 to one property, but to the PEval.GetValue(o, "Prop1.Prop2.Prop4") chain PEval.GetValue(o, "Prop1.Prop2.Prop4")


Addition 1.
More advanced version class diagram:
Class diagram for access to properties

Code Generator Class Diagram:
image

')

Source: https://habr.com/ru/post/73352/


All Articles