Use emit to reduce reflection performance loss

Source: Internet
Author: User
Tags allkeys

I likeProgramUse reflection to make things simpler and more flexible. However, the benefits brought about by performance loss. In a rough test, access members by member name through reflection is about 250 times slower than direct access. It sounds quite scary, but in terms of time, it takes only 3 microseconds to access a member using the P4 2.66g image. In view of the running cycle of the entire program, this time is negligible.

However, the degree of reflection is indeed too high. I have done this thing a long time ago, using reflection to implement bidirectional binding between Asp.net controls and data. It is expected that someone will question the use of reflection to assign values .. The net camp is not as rich as Java, and many people are still very concerned about the performance. zookeeper Deep Blue is even banned from using the factory mode by the boss, saying that activator. createinstance will affect the performance...

As a result, I began to think about how to reduce the performance loss caused by reflection. First, metadata can be cached, so that you do not have to repeat gettypes (), getmembers (), and other methods. However, these operations consume very little time and are rarely used in programs, the most important thing is how to quickly access attributes by passing in the attribute names of an object and an object. The only way to significantly improve performance is not to use reflection...

What else can we do? I think of a class with such a structure: this class has the getvalue and setvalue methods, and reads or writes corresponding attributes based on parameters through a bunch of cases, this method can achieve the desired effect without reflection.

However, for each class, there must be another corresponding class to handle such a thing, so these classes responsible for get/set should be dynamically generated at runtime, this relies on the most advanced Reflection Feature-emit.

The class in system. reflection. emit can be used to construct types during runtime and construct members through Il. Of course, generate C #CodeCodedom can also be used for compiling, but the speed must be greatly reduced.

Start with me and define the ivaluehandler interface first:

Using System;

Namespace Noreflection
{
Internal   Interface Ivaluehandler {
Object Getvalue ( Object O, String Expression );
Void Setvalue ( Object O, String Expression, Object Value );
Object Createinstance ();
}
}

Define an objectutil class, and use dynamic construction to construct the implementation class for processing specific types of ivaluehandler, and then put it in the cache. Call the getvalue/setvalue of valuehandler as needed.

Do not think too much about constructing Il. In fact, you only need to have a certain understanding of CLI and msil and can understand it, because I can first write the code with the familiar C, after compilation, we can look at the disassembly. Most of them can be copied. Of course, this process took me a lot of time, because I had to manage the packaging/unpacking in Il, and the Code for conversion of different types of data were different, then, if the short form command is used, if the offset of the target address exceeds 8 bits/* The PISCES correction is 7 ...*/An error occurs. Therefore, XX. s commands cannot be copied. All commands are replaced by XX...

Take sqlconnection as an example. The dynamically generated class looks like this:

Public   Class Valuehandler: ivaluehandler
{
// Methods
Public Valuehandler ()
{
}

Public Override ObjectCreateinstance ()
{
Return NewSqlconnection ();
}

Public   Override   Object Getvalue ( Object OBJ, String Property)
{
Switch (Property)
{
Case   " Connectionstring " :
{
Return (Sqlconnection) OBJ). connectionstring;
}
Case   " Connectiontimeout " :
{
Return (Sqlconnection) OBJ). connectiontimeout;
}
Case   " Database " :
{
Return (Sqlconnection) OBJ). database;
}
Case   " Datasource " :
{
Return (Sqlconnection) OBJ). datasource;
}
Case   " Packetsize " :
{
Return (Sqlconnection) OBJ). packetsize;
}
Case   " Workstationid " :
{
Return (Sqlconnection) OBJ). workstationid;
}
Case   " Serverversion " :
{
Return (Sqlconnection) OBJ). serverversion;
}
Case   " State " :
{
Return (Sqlconnection) OBJ). State;
}
Case   " Site " :
{
Return (Sqlconnection) OBJ). Site;
}
Case   " Container " :
{
Return (Sqlconnection) OBJ). container;
}
}
Throw   New Exception ( " The property named "   + Property +   " Does not exists " );
}

Public   Override   Void Setvalue ( Object OBJ, String Property, Object Value)
{
Switch (Property)
{
Case   " Connectionstring " :
{
(Sqlconnection) OBJ). connectionstring = ( String ) Value;
Return ;
}
Case   " Site " :
{
(Sqlconnection) OBJ). Site = (Isite) value;
Return ;
}
}
Throw   New Exception ( " The property named "   + Property +   " Does not exists " );
}
}

Objectutil is similar to databinder in ASP. NET, but databinder can only get and objectutil can get/set. To maximize performance, I treat simple attribute EXPRESSIONS differently from complex cross-object expressions. For example, the commandtext of sqlcommand should use objectutil. getvalue (CMD, "commandtext"); to obtain the connectionstring of his connection, call objectutil. getcomplexvalue (CMD, "connection. connectionstring "). Because the getcomplexvalue method has multiple pairs of parameter split ('. '). If it is a cross-object expression, you need to get the object reference step by step from left to right, and then get/set the final object attribute value. Don't underestimate split (). It will spend nearly 50% more time in the process. In addition, access to the indexer is also supported. Here I have slightly changed the expression rules. databinder uses expressions consistent with the development language syntax and is compatible with VB.. NET and C # syntax. For example, you can use databinder. eval (mylist, "[0]") or databinder. eval (mylist, "(0)") accesses the element whose index is 0, while objectutil specifies that integer indexes use numbers directly, such as ojbectutil. getvalue (mylist, "0"), character-type index directly input characters, such as ojbectutil. getvalue (mylist, "somekey "). The reason for this is that it saves the time for analyzing square brackets, and I also save a lot of trouble.

This is the generated class for processing namevaluecollection, Which is representative. The indexer can be Int or string:

Public   Override   Object Getvalue ( Object Obj3, String Text1)
{
Switch (Text1)
{
Case   " Allkeys " :
{
Return (Namevaluecollection) obj3). allkeys;
}
Case   " Count " :
{
Return (Namevaluecollection) obj3). count;
}
Case   " Keys " :
{
Return (Namevaluecollection) obj3). Keys;
}
}
If ( Char . Isdigit (text1 [ 0 ])
{
Return (Namevaluecollection) obj3) [convert. toint32 (text1)];
}
Return (Namevaluecollection) obj3) [text1];
}

The following is the test performance. I will compare objectutil with objectnavigator of reflection, databinder, and spring. net. The comparison result is:
For a simple expression, the speed is 3.5 times that of reflection, 7 times that of databinder, and 15 times that of objectnavigator... This is really special with Java.
Complex expressions are 1.5 times of reflection, 3.5 times of databinder, and 6.5 times of objectnavigator.
Since objectutil usage is similar to databinder and objectnavigator, it is much simpler than direct reflection, and it is more appropriate to use them as a reference. The score is good.

Createinstance was added later, and the default constructor is used to create objects. It seems quite necessary that some types of objects are created using reflection for a very long time. I don't know the reason. For sqlcommand, using objectutil to create objects is faster than 40 times faster than using reflection.

Of course, in practical applications, the effect is usually not obvious because, as I mentioned earlier, reflection takes only a small portion of time during the entire program execution. For example, I used nhibbench 0.8 for testing, and the performance only increased by about 3%, because most of the time was spent on analyzing hql and database reading. However, it is used on the reflected objectcomparer for most of the time, and the effect is good. After replaced with objectutil, the speed is three times faster.

Source code/files/Yok/noreflection.rar
Generated classes/files/Yok/emitteddlls.rar

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.