C # performance optimization Summary

Source: Internet
Author: User
Tags server memory

1. C # Language

1.1 garbage collection
Garbage collection frees you from manual Object Management and improves program robustness. However, the side effect is that program code may become random for object creation.
1.1.1 avoid unnecessary object Creation
Because the garbage collection cost is high, a basic principle for C # program development is to avoid unnecessary object creation. The following lists some common cases.
1.1.1.1 avoid creating objects cyclically★
If the state of an object does not change with each loop, repeatedly creating an object in the loop will result in performance loss. An efficient way is to create an object out of a loop.
1.1.1.2 create an object in the required logical Branch
If an object is used only in some logical branches, you should create an object only in this logical branch.
1.1.1.3 use constants to avoid object Creation
Code such as new decimal (0) should not appear in the program, which will lead to frequent creation and collection of small objects. The correct method is to use the decimal. Zero constant. When designing our own classes, we can also learn this design method and apply it to similar scenarios.

1.1.1.4 use stringbuilder for string connection

1.1.2 do not use empty destructor★
If the class contains destructor, the object reference will be added to the finalize queue when the object is created to ensure that the Finalize method can still be called when the object cannot be available. During running, the garbage collector starts a low-priority thread to process the queue. In contrast, objects without destructor do not consume these resources. If the Destructor is empty, this consumption is meaningless and will only result in lower performance! Therefore, do not use empty destructor.

In actual situations, many destructor contain processing code, but are commented out or deleted for various reasons, leaving only one shell, note that the Destructor itself should be commented out or deleted.
1.1.3 implement the idisposable Interface
In fact, garbage collection only supports hosted internal garbage collection. For other unmanaged resources, such as window GDI handles or database connections, releasing these resources in the Destructor is a big problem. The reason is that garbage collection depends on internal shortage. Although the database connection may be exhausted, garbage collection will not run if the memory is sufficient.

C #'s idisposable interface is a mechanism for explicitly releasing resources. The Using statement is provided to simplify the usage (the compiler automatically generates the try... Finally block and calls the dispose method in the Finally block ). For applying for an unmanaged resource object, the idisposable interface should be implemented to ensure that the resource is released as soon as it exceeds the using statement range. This makes sense for building robust and well-performing programs!

To prevent the dispose method of an object from being called, an destructor is also provided. Both call a public method to process resource release. At the same time, the dispose method should call system. gc. suppressfinalize (this) to tell the Garbage Collector that the Finalize method does not need to be processed.

1.2 string operation
1.2.1 use stringbuilder for string connection

String is a constant class. Using the + operation to connect strings will lead to the creation of a new string. If the number of string connections is not fixed, for example, in a loop, use the stringbuilder class for string connection. Because stringbuilder has a stringbuffer, the connection operation does not allocate new string space each time. A new buffer space will be applied only when the connected string exceeds the buffer size. The typical code is as follows:


Stringbuilder sb = new stringbuilder (256 );

For (INT I = 0; I <results. Count; I ++)

{

SB. append (results [I]);
}

If the number of connections is fixed and only a few times, you should use the + sign to connect directly to keep the program simple and easy to read. In fact, the compiler has been optimized to call the string. Concat method with different parameter numbers based on the number of plus signs. For example:

String STR = str1 + str2 + str3 + str4;
Will be compiled into string. Concat (str1, str2, str3, str4 ). In this method, the total string length is calculated and allocated only once. It is not allocated three times as expected. As an experience value, stringbuilder should be used when the number of string connection operations reaches 10 or more times.

Note: The default value of buffer in stringbuilder is 16, which is too small. According to the use scenario of stringbuilder, the buffer must be re-allocated. The empirical value is generally 256 as the initial value of the buffer. Of course, if the final length of the generated string can be calculated, the initial value of the buffer should be set based on this value. Use new stringbuilder (256) to set the initial length of the buffer to 256.

1.2.2 avoid unnecessary calls to the toupper or tolower Method
String is a constant class. Calling the toupper or tolower method will create a new string. If it is frequently called, it will lead to frequent creation of string objects. This violates the basic principle of "avoiding frequent object creation" mentioned above.

For example, the bool. parse method is case-insensitive. Do not call the tolower method when calling it.
Another common scenario is string comparison. An efficient method is to use the compare method. This method can be case-insensitive and does not create new strings.
Another case is that when hashtable is used, sometimes it cannot be guaranteed whether the case of the passed key is as expected, and the key is forcibly converted to either upper or lower case. In fact, hashtable has different constructor forms. It fully supports the case-insensitive key: New hashtable (stringcomparer. ordinalignorecase ).

1.2.3 fastest comparison of empty strings
Comparing the Length attribute of a string object with 0 is the fastest way: if (Str. Length = 0)
Second, compare it with a string. Empty constant or empty string: If (STR = string. Empty) or if (STR = "")
Note: C # will put all the string constants declared in the Assembly into the retention pool (intern pool) during compilation, and the same constants will not be allocated repeatedly.
1.3 Multithreading
1.3.1 Thread Synchronization

Thread Synchronization is the first consideration to Write multi-threaded programs. C # provides the following basic synchronization mechanisms for synchronization: Monitor, mutex, autoresetevent, and manualresetevent objects to encapsulate the Win32 critical section, mutex object, and event object. C # also provides a lock statement for ease of use. The Compiler automatically generates the appropriate monitor. Enter and monitor. Exit calls.

1.3.1.1 synchronization Granularity
The synchronization granularity can be the entire method or a code segment in the method. Specifying the methodimploptions. Synchronized attribute for the method will mark the entire method synchronization. For example:


[Methodimpl (methodimploptions. Synchronized)]

Public static serialmanager getinstance ()

{

If (instance = NULL)

{

Instance = new serialmanager ();

}

Return instance;
}

Generally, the synchronization range should be reduced to improve the system performance. It is not a good idea to simply mark the entire method as synchronization unless you can determine that every code in the method needs to be protected by synchronization.
1.3.1.2 synchronization Policy
Use lock for synchronization. You can select type, this, or a member variable specially constructed for the synchronization object.
Avoid locking type★
Locking a type object affects all instances of this type of appdomain in the same process. This may not only cause serious performance problems, but also lead to unexpected behavior. This is a bad habit. Even for a type that only contains the static method, an additional static member variable should be constructed so that the member variable can be used as the Lock Object.

Avoid locking this
Locking this affects all methods of the instance. Assume that the object OBJ has two methods: A and B. The method A uses lock (this) to set synchronization protection for a code segment in the method. Now, for some reason, Method B also uses lock (this) to set synchronization protection for completely different purposes. In this way, method A is disturbed and its behavior may be unpredictable. Therefore, as a good habit, we recommend that you avoid using the lock (this) method.

Use member variables specially constructed for synchronization purposes
This is recommended. Method is to create an object, which is only used for synchronization purposes.
If multiple methods need to be synchronized for different purposes, you can create several synchronization member variables for them.
1.3.1.4 set Synchronization
C # provides two convenient synchronization mechanisms for various Collection types: Synchronized wrappers and syncroot attributes.


// Creates and initializes a new arraylist

Arraylist myal = new arraylist ();
Myal. Add (
"");
Myal. Add (
"Quick ");
Myal. Add (
"Brown ");
Myal. Add (
"Fox ");



// Creates a synchronized wrapper around the arraylist

Arraylist mysyncdal = arraylist. synchronized (myal );

Calling the synchronized method will return an identical set object that ensures that all operations are thread-safe. Consider the mysyncdal [0] = mysyncdal [0] + "test" statement. Two locks are used for reading and writing. Generally, the efficiency is not high. We recommend that you use the syncroot attribute for more detailed control.

1.3.2 use threadstatic instead of namedataslot★
Stores the thread of namedataslot. getdata and thread. the setdata method requires thread synchronization and involves two locks: localdatastore. the setdata method requires a lock at the appdomain level, and the other is threadnative. the getdomainlocalstore method must be locked at the process level. If namedataslot is used for some underlying basic services, the system may suffer severe scaling problems.

To avoid this problem, use the threadstatic variable. Example:


Public sealed class invokecontext

{

[Threadstatic]

Private Static invokecontext current;

Private hashtable maps = new hashtable ();
}

1.3.3 multithreading programming skills
1.3.3.1 use double check technology to create objects


Internal idictionary keytable

{

Get

{

If (this. _ keytable = NULL)

{

Lock (base. _ Lock)

{

If (this. _ keytable = NULL)

{

This. _ keytable = new hashtable ();

}

}

}

Return this. _ keytable;

}
}

Creating a singleton object is a common programming scenario. Generally, an object will be created directly after the lock statement, but this is not safe enough. Before locking an object, multiple threads may have entered the first if statement. If the second if statement is not added, the singleton object is created again, and the new instance replaces the old instance. If the existing data in the singleton object cannot be damaged or for any other reason, you should consider using the double check technology.

1.4 Type System
1.4.1 avoid meaningless variable Initialization

CLR ensures that all objects have been initialized before access, so that the allocated memory is cleared. Therefore, you do not need to reinitialize the variable to 0, false, or null.
Note that the local variables in the method are not allocated from the stack but from the stack. Therefore, C # does not perform clearing. If an unspecified local variable is used, an alarm is triggered during compilation. Do not assign values to the member variables of all classes because of this impression. The mechanism of the two is completely different!

1.4.2 valuetype and referencetype
1.4.2.1 pass the value type parameter in Reference Mode

The value type is allocated from the call stack, and the reference type is allocated from the managed stack. When the value type is used as a method parameter, the parameter value is copied by default, which offset the advantage of the Value Type allocation efficiency. As a basic technique, passing value type parameters in reference mode can improve performance.

1.4.2.2 provide the equals Method for valuetype
The valuetype. Equals method implemented by. Net by default uses the reflection technology to obtain all member variable values for comparison, which is extremely inefficient. If the equals method of the value object we write is used (for example, placing the value object in hashtable), The equals method should be overloaded.


Public struct rectangle

{

Public double length;

Public double breadth;

Public override bool equals (Object ob)

{

If (OB is rectangle)

Return equels (rectangle) ob ))

Else

Return false;

}

Private bool equals (rectangle rect)

{

Return this. Length = rect. Length & this. breadth = rect. breach;

}
}

1.4.2.3 avoid packing and unpacking
C # It can be automatically converted between the value type and the reference type by packing and unpacking. Objects need to be allocated from the heap and the value must be copied for packing, which consumes a certain amount of performance. If this process occurs in a loop or is frequently called as the underlying method, you should be cautious about the cumulative effect.

A common situation occurs when the set type is used. For example:


Arraylist Al = new arraylist ();

For (INT I = 0; I <1000; I ++)

{

Al. Add (I); // implicitly boxed because add () takes an object

}

Int F = (INT) al [0]; // The element is unboxed

1.5 Exception Handling
Exceptions are also typical features of modern languages. Compared with traditional methods of checking error codes, exceptions are mandatory (not dependent on whether you forget to write the code for checking error codes), strong types, and rich exception information (such as call stacks ).
1.5.1 do not eat exceptions★
The most important principle about exception handling is: do not eat the exception. This issue has nothing to do with performance, but is very important for compiling robust and error-prone programs. In other words, this principle is not to capture exceptions that you cannot handle.
Eating exceptions is a bad habit, because you have eliminated the clues to solve the problem. Once an error occurs, it is very difficult to locate the problem. In addition to this method of completely consuming exceptions, it is also inappropriate to write the exception information to the log file but not to perform more processing.

1.5.2 do not eat exception information★
Some code throws an exception, but eats the exception information.
It is the responsibility of programmers to disclose detailed information for exceptions. If you cannot add richer and more humane content while retaining the meaning of the original exception information, the direct display of the original exception information is much stronger. Never eat exceptions.

1.5.3 avoid unnecessary exceptions
Throwing exceptions and capturing exceptions consume a lot. If possible, you should improve the program logic to avoid unnecessary exceptions. A tendency related to this is to use exceptions to control the processing logic. Although in rare cases, this may lead to more elegant solutions, it should be avoided in general.

1.5.4 avoid unnecessary re-throwing exceptions
If it is for the purpose of packaging exceptions (that is, adding more information to package new exceptions), it is reasonable. However, there is a lot of code, and the capture exception is thrown again without any processing. This will increase the consumption of One capture exception and one throw exception, compromising the performance.

1.6 reflection
Reflection is a basic technology that converts static bindings during compilation to dynamic bindings delayed to runtime. In many scenarios (especially the design of the class framework), you can obtain a flexible and scalable architecture. However, the problem is that dynamic binding will cause great performance damage compared with static binding.

1.6.1 reflection Classification
Type comparison: type determination, mainly including the IS and typeof operators and GetType calls on object instances. This is the most lightweight consumption, and you do not need to consider optimization issues. Note that the typeof operator is faster than the GetType method on the object instance. If possible, the typeof operator is preferred.

Member enumeration: Member enumeration, used to access reflection-related metadata, such as assembly. getmodule, module. call isinterface, ispublic, getmethod, getmethods, getproperty, getproperties, and getconstructor on GetType and type objects. Although metadata is cached by CLR, calling of some methods consumes a lot. However, the call frequency of such methods is not very high, so the overall performance loss process is moderate.

Member invocation: Member calls, including dynamic object creation and dynamic object calling methods, including activator. createinstance and type. invokemember.

1.6.2 dynamically create an object
C # mainly supports five ways to dynamically create objects:
1. type. invokemember
2. contructorinfo. Invoke
3. activator. createinstance (type)
4. activator. createinstance (assemblyname, typename)
5. Assembly. createinstance (typename)
The fastest is method 3, which is about seven times slower than direct create within an order of magnitude. For other methods, the speed is at least 40 times, the slowest is Mode 4, and the speed is three orders of magnitude.

1.6.3 dynamic method call
Method calls are divided into two types: Early binding during the compilation period and dynamic binding during the runtime, namely early-bound invocation and late-bound invocation. Early-bound invocation can be subdivided into direct-call, interface-call, and delegate-call. Late-bound invocation mainly includes type. invokemember and methodbase. Invoke. You can also use the lightweight Code Generation Technology to generate il code for dynamic calling.

From the test results, the type. invokemember is nearly three orders of magnitude slower than direct call. Although methodbase. Invoke is three times faster than type. invokemember, it is about 270 times slower than direct call. It can be seen that the performance of dynamic method calls is very low. Our suggestion is: Do not use it unless you want to meet specific requirements!

1.6.4 recommended usage principles
Mode
1. If possible, avoid using reflection and dynamic binding
2. Use the interface call method to convert dynamic binding to early binding
3. Use activator. createinstance (type) to dynamically create an object
4. Use the typeof operator instead of GetType to call
Reverse Mode
1. If type is obtained, use assembly. createinstance (type. fullname)
1.7 basic code skills
This section describes the basic code skills that can improve performance in some application scenarios. It makes sense to perform such optimization on code in a critical path. Common Code can be left empty, but it makes sense to develop a good habit.
1.7.1 circular writing
You can record the cyclic judgment conditions with local variables. Local variables are often optimized by the compiler to directly use registers, which is faster than the variables allocated from normal stacks or stacks. If complex computing attributes are accessed, the improvement will be more obvious. For (INT I = 0, j = collection. getindexof (item); I <j; I ++)

It should be noted that this writing method has no significance for the Count attribute of the CLR collection class, because the compiler has made special Optimizations in this way.
1.7.2 assemble strings
It is inefficient to compile the statement and then delete it. In most cases, the loop length of some methods is 1, which is more efficient:


Public static string tostring (metadatakey entitykey)

{

String STR = "";

Object [] Vals = entitykey. values;

For (INT I = 0; I <Vals. length; I ++)

{

STR + = "," + Vals [I]. tostring ();

}

Return STR = ""? "": Str. Remove (0, 1 );
}

The following statement is recommended:


If (Str. Length = 0)

STR = Vals [I]. tostring ();

Else

STR + = "," + Vals [I]. tostring ();

In fact, this writing method is very natural and efficient, and does not need to be bypassed by a remove method.
1.7.3 avoid retrieving set elements twice
When getting a set element, you sometimes need to check whether the element exists. The common practice is to call the containskey (or contains) method first, and then obtain the collection element. This writing method is very logical.
However, if efficiency is taken into consideration, you can directly obtain the object and then determine whether the object is null to determine whether the element exists. For hashtable, this saves one gethashcode call and comparison with N equals.

Example:


Public idata getitembyid (guid ID)

{

Idata data1 = NULL;

If (this. idtable. containskey (Id. tostring ())

{

Data1 = This. idtable [ID. tostring ()] as idata;

}

Return data1;
}

In fact, a single line of code is fully available: return this. idtable [ID] As idata;
1.7.4 avoid two type conversions
Consider the following example, which contains two types of conversions:


If (obj is sometype)

{

Sometype ST = (sometype) OBJ;

St. sometypemethod ();
}

A more efficient approach is as follows:


Sometype ST = OBJ as sometype;

If (st! = NULL)

{

St. sometypemethod ();
}

1.8 hashtable
Hashtable is a basic set type that is frequently used. There are two factors that affect the efficiency of hashtable: one is the gethashcode method and the other is the equals method ). Hashtable first uses the hash code of the key to distribute objects to different buckets, and then uses the equals method of the key to search for objects in the specified bucket.

A good hash code is the first factor. Ideally, different keys have different hash codes. The equals method is also very important, because hash only needs to be performed once, And the bucket search key may need to be performed multiple times. From the actual experience, the equals method consumes more than half of hashtable.

The system. object class provides the default gethashcode implementation, using the address of the object in the memory as the hash code. We have encountered an example of using hashtable to cache objects. Each time an expressionlist object is constructed based on the passed oql expression, the querycompiler method is called to compile the compiledquery object. Store the expressionlist object and compiledquery object as key-value pairs in hashtable. The expressionlist object does not overload the gethashcode implementation, and its super class arraylist does not exist. In this way, the system. Object Class is used.
. Because the expressionlist object is constructed every time, its hashcode is different each time, so this compiledquerycache does not play the expected role. This small omission brings a major performance problem. Due to frequent parsing of oql expressions, compiledquerycache continues to grow, resulting in Server Memory leakage! The simplest way to solve this problem is to provide a constant implementation. For example, set the hash code to constant 0. Although this will cause all objects to converge into the same bucket, the efficiency is not high, but at least the memory leakage problem can be solved. Of course, an efficient gethashcode method will be implemented in the end.

The hashtable mechanism described above is intended for everyone to understand: If hashtable is used, you should check whether the object provides the appropriate gethashcode and equals methods. Otherwise, there may be situations where the efficiency is not high or is inconsistent with the expected behavior.

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.