Last time I tested the class and struct respectively: struct. Now we will further analyze and interpret the MSIL (Microsoft intermediate language) code generated by the compiler.
I. Preparations
Find "IL disassembly program" (start \ Program \ Microsoft Visual Studio 2010 \ Microsoft Windows SDK Tools \)--
Run the "IL disassembly program" and open the compiled exe. Expand the node and double-click the leaf node to view the MSIL code --
Ii. Result Analysis
Then we will copy and extract the line of code for the test function call. For example, the line "IL_004c.
During the copy extraction process, we found that the function call code generated by VS2005 and VS2010 is exactly the same. Delete the long-winded namespace and organize the result into a table --
Mode |
MSIL |
Highlights |
Static call |
Call uint8 * TryIt_Static_Ptr (uint8 *) |
Static Functions |
Call a derived class |
Callvirt instance uint8 * PointerCall: Ptr (uint8 *) |
Virtual Method |
Call sealing class |
Callvirt instance uint8 * SldPointerCallAdd: Ptr (uint8 *) |
Virtual Method |
Call struct |
Call instance uint8 * SPointerCallAdd: Ptr (uint8 *) |
Method (non-virtual) |
Call base class |
Callvirt instance uint8 * PointerCall: Ptr (uint8 *) |
Virtual Method |
Call the interface of the derived class |
Callvirt instance uint8 * IPointerCall: Ptr (uint8 *) |
Virtual Method |
Call the sealed Interface |
Callvirt instance uint8 * IPointerCall: Ptr (uint8 *) |
Virtual Method |
Call the struct Interface |
Callvirt instance uint8 * IPointerCall: Ptr (uint8 *) |
Virtual Method |
Base class generic call derived class |
Call uint8 * CallClassPtr <class PointerCallAdd> (!! 0, uint8 *) |
Class |
Base class generic call base class |
Call uint8 * CallClassPtr <class PointerCall> (!! 0, uint8 *) |
Class |
Interface generic call derived class |
Call uint8 * CallPtr <class PointerCallAdd> (!! 0, uint8 *) |
Class |
Interface generic call sealing class |
Call uint8 * CallPtr <class SldPointerCallAdd> (!! 0, uint8 *) |
Class |
Interface generic call struct |
Call uint8 * CallPtr <valuetype SPointerCallAdd> (!! 0, uint8 *) |
Valuetype |
Interface generic call struct reference |
Call uint8 * CallRefPtr <valuetype SPointerCallAdd> (!! 0 &, uint8 *) |
Valuetype |
Interface generic call base class |
Call uint8 * CallPtr <class PointerCall> (!! 0, uint8 *) |
Class |
Interface generic call the interface of the derived class |
Call uint8 * CallPtr <class IPointerCall> (!! 0, uint8 *) |
Class |
Interface generic call sealing Interface |
Call uint8 * CallPtr <class IPointerCall> (!! 0, uint8 *) |
Class |
Interface generic interface for calling struct |
Call uint8 * CallPtr <class IPointerCall> (!! 0, uint8 *) |
Class |
Observe the above table and we find that --
1. The compiled IL code is not inline (inline. Expand the sub-function) for optimization, and compile all the sub-functions into different calls according to the semantics ). It seems that the optimization work is the responsibility of JIT (instant compiler.
2. call an instance ). JIT can arrange inline optimization based on this information.
3. Calling the derived class is a virtual method call (callvirt instance ). Because it is compiled to call the virtual method (PointerCall: Ptr) of the base class, JIT considers it a normal virtual method call without optimization.
4. The call sealing class is a virtual method call (callvirt instance), which is consistent with the call of the derived class. However, since it leaves the type information (SldPointerCallAdd: Ptr), JIT finds that it is a sealed class, so it arranges inline optimization.
5. Although a generic method uses call commands, it carries generic parameters, so its behavior is different from that of a common call.
6. The valuetype keyword is used when struct calls a generic method. JIT can be optimized based on this information (JIT of VS005 is optimized, while JIT of VS2010 performs inline optimization thoroughly ).
Appendix A. The IL code when converting to an interface
Convert a derived class to an interface --
IL_001d: ldloc.0
IL_001e: stloc. s V_4
Convert the sealing class to the interface --
IL_0020: ldloc.1
IL_0021: stloc. s V_5
Convert struct to interface --
IL_0023: ldloc.2
IL_0024: box TryPointerCall. SPointerCallAdd
IL_0029: stloc. s V_6
It can be seen that when the struct is converted into an interface, additional packing operations are performed, affecting the performance.
Appendix B. IL code of generic structure calls
Interface generic call struct --
IL_0391: ldloc.2
IL_0392: ldloc. s V_7
IL_0394: call uint8 * TryPointerCall. PointerCallTool: CallPtr <valuetype TryPointerCall. SPointerCallAdd> (!! 0, uint8 *)
Interface generic call struct reference --
IL_03dd: ldloca. s V_2
IL_03df: ldloc. s V_7
IL_03e1: call uint8 * TryPointerCall. PointerCallTool: CallRefPtr <valuetype TryPointerCall. SPointerCallAdd> (!! 0 &, uint8 *)
It can be seen that the IL code of a generic call is not complex. It is basically the same as that of a common call. The parameter is first put into the stack and then called. For reference parameters, replace the "ldloc. *" command with the "ldloca. s" command.
Column of zyl910