C # Summary of Performance Optimization

Source: Internet
Author: User
Tags throw exception server memory

 

Original article address

1Garbage 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

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 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.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.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.2

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.3

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 #

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.

2 stringOperation

2.1

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. 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.

2.2

String

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 ).

2.3

Comparing the Length attribute of a string object with 0 is the fastest way: if (Str. length = 0) followed by string. empty constant or empty string comparison: 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.

3

3.1

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.

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 serialmanagergetinstance ()

{

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.

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.

3.1.4 set Synchronization

C #

// Creates andinitializes a new arraylist

Arraylist myal = new arraylist ();

Myal. Add ("");

Myal. Add ("quick ");

Myal. Add ("brown ");

Myal. Add ("Fox ");

// Creates asynchronized 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.

3.2

Access 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 ();

}

 

3.3

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 single-instance object cannot be damaged or for any other reason, you should consider using the doublecheck technology.

4

4.1

CLR

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!

4.2 valuetype and referencetype

4.2.1 pass value type parameters 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.

4.2.2 provide the equals Method for valuetype

. Net

Public struct rectangle

{

Public double length;

Public double breadth;

Public override bool equals (objectob)

{

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;

}

}

4.2.3 avoid packing and unpacking

C #

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

5

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 ).

5.1

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.

5.2

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.

5.3

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.

5.4

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.

6

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.

6.1

Typecomparison: 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.

Memberenumeration: 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 is moderate.

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

6.2

C #

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.

6.3

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!

6.4

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)

7

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.

7.1

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.

7.2

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.

7.3

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;

7.4

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 ();

}

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 in the specified bucket for search.

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. In this way, the gethashcode Implementation of 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 it can at least solve the memory leakage problem. 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.