Build the fastest universal property accessors using generic delegation

Source: Internet
Author: User

Recently, I made a small program that assigned values to attributes of the parent class to the attributes of the subclass. I used the automapper component. It feels good. I want to explore its principle and try it by myself. To implement this function, first retrieve the attributes of the parent class object through reflection, and then set the attributes with the same name as the subclass object. However, when we think about the reflection efficiency, we plan to use another method.

After searching the following materials, we found that artech's Performance Comparison of Three Types of attribute operations: propertyinfo + Expression Tree + delegate. createdelegate "http://www.cnblogs.com/artech/archive/2011/03/26/Propertyaccesstest.html, the test results in this paper, the use of commission is the fastest way, but the original into the principles of the description, the Code is not universal, so refer to the original method, changed to the generic method:

First, define a generic delegate to get the property value and set the property value:

 public delegate T GetPropertyValue<T>(); public delegate void SetPropertyValue<T>(T Value);

Then, write two methods to construct the delegated instance:

        private static ConcurrentDictionary<string, Delegate> myDelegateCache = new ConcurrentDictionary<string, Delegate>();        public static GetPropertyValue<T> CreateGetPropertyValueDelegate<TSource, T>(TSource obj, string propertyName)        {            string key = string.Format("Delegate-GetProperty-{0}-{1}", typeof(TSource).FullName, propertyName);            GetPropertyValue<T> result = (GetPropertyValue<T>)myDelegateCache.GetOrAdd(                key,                 newkey =>                {                    return Delegate.CreateDelegate(typeof(GetPropertyValue<T>), obj, typeof(TSource).GetProperty(propertyName).GetGetMethod());                }                );            return result;        }        public static SetPropertyValue<T> CreateSetPropertyValueDelegate<TSource, T>(TSource obj, string propertyName)        {            string key = string.Format("Delegate-SetProperty-{0}-{1}", typeof(TSource).FullName, propertyName);            SetPropertyValue<T> result = (SetPropertyValue<T>)myDelegateCache.GetOrAdd(               key,               newkey =>               {                   return Delegate.CreateDelegate(typeof(SetPropertyValue<T>), obj, typeof(TSource).GetProperty(propertyName).GetSetMethod());               }               );            return result;        }

Note that the. Net 4.0 thread-safe dictionary is used to cache the generated delegate type instances:
Private Static concurrentdictionary <string, delegate> mydelegatecache = new concurrentdictionary <string, delegate> ();

Now, let's write a test code:

 

            CarInfo info = new CarInfo();            info.CID = 999;            ////////////////            //info.ID==999;            SetPropertyValue<int> set = CreateSetPropertyValueDelegate<CarInfo, int>(info, "CID");            set(100);//100;            GetPropertyValue<int> get = CreateGetPropertyValueDelegate<CarInfo, int>(info, "CID");            var r = get();//100            GetPropertyValue<int> get2 = CreateGetPropertyValueDelegate<CarInfo, int>(info, "CID");            var r2 = get2();//100

 

After testing, the result is normal. In this way, the most common attribute accessors will be available.

How is the performance of this method? I heard that the dictionary search performance of. NET is not high enough. Continue to write the test code and run it:

(Note: The nocache method is added to the test code. The Code is as follows:

 public static GetPropertyValue<T> CreateGetPropertyValueDelegateNoCache<TSource, T>(TSource obj, string propertyName)        {            return (GetPropertyValue<T>)Delegate.CreateDelegate(typeof(GetPropertyValue<T>), obj, typeof(TSource).GetProperty(propertyName).GetGetMethod()); ;        }        public static SetPropertyValue<T> CreateSetPropertyValueDelegateNoCache<TSource, T>(TSource obj, string propertyName)        {            return (SetPropertyValue<T>)Delegate.CreateDelegate(typeof(SetPropertyValue<T>), obj, typeof(TSource).GetProperty(propertyName).GetSetMethod()); ;        }

 

)

Console. writeline ("{0,-15} {1,-10} {2,-8} {3,-8} {4,-8}", "Times ", "direct access", "Delegate (non-Cache)", "Delegate (Dictionary cache)", "Delegate (variable cache )"); // for performance testing, directly access int times = 1000000; var stopwatch = new stopwatch (); stopwatch. start (); For (INT I = 0; I <times; I ++) {int oldvalue = info. CID; info. cid = I;} var duration1 = stopwatch. elapsedmilliseconds; // use delegate, non-Cache stopwatch. restart (); For (INT I = 0; I <times; I ++) {int oldvalue = creategetpropertyvaluedelegatenocache <carinfo, int> (Info, "CID ")(); createsetpropertyvaluedelegatenocache <carinfo, int> (Info, "CID") (I);} var duration2 = stopwatch. elapsedmilliseconds; // delegate used, Dictionary cache stopwatch. restart (); For (INT I = 0; I <times; I ++) {int oldvalue = creategetpropertyvaluedelegate <carinfo, int> (Info, "CID ")(); createsetpropertyvaluedelegate <carinfo, int> (Info, "CID") (I);} var duration3 = stopwatch. elapsedmilliseconds; // use the delegate to directly cache stopwatch. restart (); For (INT I = 0; I <times; I ++) {int oldvalue = get (); Set (I) ;}var duration4 = stopwatch. elapsedmilliseconds; stopwatch. stop (); console. writeline ("{0,-15} {1,-15} {2,-15} {3,-15} {4,-15}", times, duration1, duration2, duration3, duration4); console. writeline ("-------------------- unit (MS )--------------------------");

The running result is as follows:

============================= Debug mode ======================== = times direct access to delegation (non-Cache) delegate (Dictionary cache) Delegate (variable cache) 1000000 17 12421 949 16 -------------------- unit (MS) -------------------------------------------- ============================ ========= times direct access to delegation (non-Cache) delegate (Dictionary cache) Delegate (variable cache) 1000000 9 11696 867 8 -------------------- unit (MS )--------------------------

The results are amazing: directly executing a delegate call is faster than calling the method itself. It also proves that the dictionary search performance is indeed not high. In this test, the number of dictionary elements is relatively small. Some friends suggested that it may be because it consumes a lot of performance to calculate the hash of the dictionary key, therefore, the length of the cache dictionary is reduced to DGP-{0}-{1} and DSP-{0}-{1}. test again:

========================================= Long key ==================== =============================== times direct access to delegation (non-Cache) delegate (Dictionary cache) Delegate (variable cache) 1000000 19 11817 971 18 ---------------------- unit (MS) ------------------------ times direct access delegate (non-Cache) Delegate (Dictionary cache) Delegate (variable cache) 1000000 24 11210 882 16 -------------------- unit (MS) -------------------------- times direct access delegate (non-Cache) Delegate (Dictionary cache) Delegate (variable cache) 1000000 22 11168 895 16 -------------------- unit (MS) -------------------------- ======================== short key ============ ================================== times direct access to delegation (non-Cache) delegate (Dictionary cache) Delegate (variable cache) 1000000 20 11727 689 18 ---------------------- unit (MS) ------------------------ imes direct access delegate (non-Cache) Delegate (Dictionary cache) Delegate (variable cache) 1000000 18 11804 738 17 --------------------- unit (MS) -------------------------- times direct access delegate (non-Cache) Delegate (Dictionary cache) Delegate (variable cache) 1000000 23 11234 16 -------------------- unit (MS) bytes )--------------------------

The test results show that the length of the dictionary key does affect the performance. Therefore, we need to note that the longer the key, the better.

 

Supplement:

A friend of the following replied that to compare the efficiency of the test program in 10 times, 1000000, and, the test program was slightly rewritten. The following is the running result:

 

Times direct access delegate (non-Cache) Delegate (Dictionary cache) Delegate (variable cache) 10 0 3 0 0 -------------------- unit (MS) ------------------------ * times direct access delegate (non-Cache) delegate (Dictionary cache) Delegate (variable cache) 100 0 1 0 0 ---------------------- unit (MS) ------------------------ * times direct access delegate (non-Cache) Delegate (Dictionary cache) Delegate (variable cache) 10000 0 165 8 0 -------------------- unit (MS) -------------------------- * times direct access delegate (non-Cache) Delegate (Dictionary cache) Delegate (variable cache) 1000000 31 11556 17 -------------------- unit (MS) --------------------------*

From the test, the efficiency difference is very small in the number of executions within several hundred times, which can be ignored, so you do not need to cache the delegate results.

 

What can it do?

It is useful to dynamically set object attribute values, for example, object class attribute assignment of ORM.

PS: today's test found that the code

Delegate. createdelegate (typeof (getpropertyvalue <t>), OBJ, typeof (tsource). getproperty (propertyname). getgetmethod ());
The created delegate method is only valid for the object OBJ of the current instance. Unless it is a static attribute, it cannot be used as a generic type attribute accessor. Therefore, caching it is of little significance, however, it can be used as a means to optimize attribute access.

 

------------------ Demarcation line ---------------------------

Welcome to the PDF. Net open-source technical team to build the lightest and fastest data framework!

 

 

 

 

 

 

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.