5. Be wary of implicit BOX and UNBOX operations on program performance
Chen Ming Microsoft .NET MVP
"Happy Dictionary, Anxious Q & A" - indicates all Box and UNBOX operations included in the following block:
Public interface imovable {
Void Move (Int newx, int newy);
}
Public struct point: imovable {
Public Int x, y;
Public void move (int newx, int newy) {
X = newx; y = newy;
}
}
...
Point PT1;
Pt1.x = 1; pt1.y = 1;
ArrayList Al = New ArrayList (); // 1
Al.Add (pt1);
Point PT2 = (POINT) Al [0];
TYPE T = pt2.gettype ();
Point Temp = (POINT) Al [0]; // 2
Temp.move (5, 5);
(IMOVABLE) Al [0]). Move (4, 4);
DateTime DT = DateTime.now; // 3
String str2 = dt.toString ();
String str1 = pt1.toString ();
INT m = 1; // 4
String str3 = m "," DT;
...
In terms X we have introduced the Box operation contains memory allocation, and the object copy and other "heavy" work, so correctly identify
The implicit BOX / UNBOX operation existing in the program is critical to the performance of the .NET program.
In terms of concept, Box and UNBOX operations are very easy to understand: When the program looks forward to an object of a reference type, it is actually obtained.
When it is a value type object, it must be converted into a reference type via the Box operation; it is reversible, if the program is looking forward to a simple
The value type object, but it is wrapped by Box, then it must be converted back by Unbox operation.
However, when actual use - especially with interface inheritance, polymorphism, etc., the object-oriented characteristics are used, correctly judge
The presence of Box and UNBOX operation is not so easy. Therefore, let us analyze the procedures mentioned above, see if you are
I fell into several traps. J
ArrayList Al = New ArrayList ();
Al.Add (pt1);
Point PT2 = (POINT) Al [0];
TYPE T = pt2.gettype ();
This is the simplest form of Box and UNBOX operations: because the ARRAYLIST's add method requires an Object type parameter,
The parameter PT1 provided by the program is a value type object, so the compiler must generate the corresponding BOX instruction, call the Adder.
A reference type object corresponding to the PT1 is generated before the method, and the parameter of the ADD method is used in this object.
Since the .NET uses some special techniques in an array implementation, various operations of the value type array do not require BOX and UNBOX
(See Terms 2).
In the next statement, Al [0] returns an object reference for an Object type, and the program needs to force it to Point.
Type, so the compiler must also generate corresponding UNBOX instructions to complete this conversion.
Finally, gettype is the member function of the object, and ValueType and Point have not rewritten the function (because gettype is not
It is a virtual function, and it is necessary to use the new keyword to write GetType), so it actually calls the GetType member function inherited from Object. Obviously, this function always assumes that it works on an object of a reference type, so the compiler must call
The PT2 is performed before the GetType function.
In contrast, the next code is slightly more complex, we need to call the Point class Move method, then we have two options:
First, let Al [0] into the Point object, then call its method:
Point Temp = (POINT) Al [0];
Temp.move (5, 5);
We have already analyzed in front of us, which actually contains UNBOX operations during the process of transition here. Since Move is a member function of Point,
So you can directly call the MOVE method of the TEMP object without the need for BOX operation.
It should be noted that due to the implementation of the C # compiler, TEMP is actually a new Point object established on the stack after UNBOX operation.
So call the MOVE function of the TEMP object does not affect the value of Al [0] in ArrayList. Even if you merge these two statements, it doesn't matter:
(POINT) Al [0]). Move (5, 5); // Do not change Al [0]
If you need to actually modify Al [0], you must write Temp back to ArrayList:
Point Temp = (POINT) Al [0];
Temp.move (5, 5);
Al [0] = TEMP;
Another method is to convert it into an IMOVABLE interface, then call MOVE method:
(IMOVABLE) Al [0]). Move (4, 4);
Maybe some are unexpected, using this method does not need any Box / UNBOX operation. Al [0] Returns a reference to an Object object,
Convert such a reference to a IMOVABLE interface. Obviously does not require a BOX operation, and call an interface function also requires BOX or UNBOX.
Another advantage to use this method is to avoid copying work mentioned earlier. Call the interface function, we "on site" modified
The value of the Point object in ArrayList, which reduces the amount of code, and also improves the performance of the program.
Next is the call to the value type object TOSTRING method:
DateTime DT = datetime.now;
String str2 = dt.toString ();
String str1 = pt1.toString ();
We have analyzed the case of calling the GetType method in front, and the toptype here is the member function of Object,
So call TOSTRING does not clearly need BOX operation? Don't worry, let us analyze it one by one:
DateTime: Let's of course hope that the TOSTRING method of DateTime can get the characters of the time it contain,
Therefore, the DateTime structure has rewritten the toString method from Object inheriting. Among the Tostring method of the datetime structure,
Compiler and Runtime confident that the current operation must be an instance of the DateTime object, not the type from DateTime inherited,
Because .NET does not allow further inheritance from value type. Since compilers and runtime can properly handle this value type object,
Then you pack it with Box operations. So, the TString method calling the datetime does not need
Box operation.
Point: Point's situation is different, because we forget to rewrite the Tostring method when writing the Point structure.
Point.toString will directly call the version inherited from Object, return to its own type. Obviously, Object's ToString Party
The method is to prepare the reference type, that is, the compiler and Runtime expects an object reference during this function call.
The current value type object must be converted via the Box operation to get a valid object reference.
There is still the last statement left:
INT m = 1;
String str3 = m "," DT;
What is the truly difficulty of this object is how this object is completed? To simplify the splicing of objects and strings, C # compiler
You can directly accept the above statement and convert it into a call to the Concat method for the String class:
Public static string concat (params object [];
Public Static String Concat (params string ";
The above statement is actually converted to:
String str3 = string.concat (m, ",", dt);
Obviously the Concat method of the String class can only accept reference type objects such as Object and String, and the value type object such as M and DT
It must be converted to the parameters of the string.concat function through the Box operation.
Through the simple rewriting of the above statement, we can actually avoid this two BOX operations:
String str3 = m.toString () "," DT.toString ();
Since System.int32 and DateTime have rewritten Object.toString method, call the toString method no longer need additional
Box is operated. Considering that avoiding Box operations means reducing memory allocation and copying work, understanding potential box / unbox operations in the program and
Avoid as much benefit to their performance of procedures.
Finally, let us summarize the common implicit Box / UNBOX operations and methods that may be avoided:
Function parameters: When the function parameter is declared as an Object type, the actual passage parameter is a value type object. This
Class functions should provide overloaded forms as possible when designing, such as Console.Write, in addition to providing Object parameter adjustment
In addition to the form, the value of the built-in value is also provided in the form of overloaded calls.
Using a container object: In addition to array, other .NET containers (such as arraylist) are direct accommodation reference types
Icon. Use them to accommodate value type objects require additional box / unbox operations. So if the program performance is critical, try to use
Value type arrays replace other container objects. In addition, unnecessary BOX / UNBOX operations can also be reduced by defining the Interface.
Call Object class method: The method of inheriting from the Object class object is definitely inherited by the object class. because
This is best to provide the realization of the virtual function defined in the value type object, so that except for less potential box / unbox operations,
It is more reasonable for semantics such as the toString method.
Call Interface Function: If a value type implements an interface, convert the value type object into an interface requires BOX operation.
If you can correctly identify the Hidden Box / UNBOX operation hidden in the program, some simple optimization methods are equally applicable here. For example, the following code:
INT K = 5;
For (int i = 0; i <10000; i) {
// Two Box Operations
Console.writeline (k "," i);
We can refer the BOX operation of the implicitly completed variable K to the outside of the cyclic body, thereby reducing the number of program Box operations, which enhances performance:
INT K = 5;
Object OK = (Object) K; // manully Box IT
For (int i = 0; i <10000; i)
{
// only one box operation
Console.WriteLine (OK "," i);
}