A few days ago, N Articles reported that C #/. NET was too slow and required to delete some features of C #/. NET.
C #/. NET Slow seems to be recognized by the industry, no matter how you prove C #/. NET is actually not much slower than C ++, but the application-level performance is still so slow.
So where is C #/. NET slow?
Unfortunately, most c # programs are slowed down by most programmers. This conclusion may not be easily accepted, but is widely used.
String operation
Almost all programs have String operations. at least 90% of the programs need to ignore case-sensitivity comparisons and check the Code. At least half of the applications have code similar to this:
If (str1.ToUpper () = str2.ToUpper ())
Or ToLower version, or even I have seen a Web HttpModule written in:
For (int I = 0; I <strs. Count; I ++)
If (value. ToUpper () = strs [I]. ToUpper ())
//...
Think about it, every page request will execute such a piece of code, a large part of the string instance to create, more exaggerated is that some people say this is to use space for time.
Performance Testing
This method is slow. Some people may not admit it. They think this is the best method, so here we will use a specific test to present a fact.
First, prepare a method to test the performance:
PRivate static TResult MeasurePerformance <TArg, TResult> (Func <TArg, TResult> func, TArg arg, int loop)
{
GC. Collect ();
Int gc0 = GC. CollectionCount (0 );
Int gc1 = GC. CollectionCount (1 );
Int gc2 = GC. CollectionCount (2 );
TResult result = default (TResult );
Stopwatch sw = Stopwatch. StartNew ();
For (int I = 0; I <loop; I ++)
{
Result = func (arg );
}
Console. WriteLine (sw. ElapsedMilliseconds. ToString () + "ms ");
Console. WriteLine ("GC 0:" + (GC. CollectionCount (0)-gc0). ToString ());
Console. WriteLine ("GC 1:" + (GC. CollectionCount (1)-gc1). ToString ());
Console. WriteLine ("GC 2:" + (GC. CollectionCount (2)-gc2). ToString ());
Return result;
}
Then prepare a heap string:
Private static List <string> CreateStrings ()
{
List <string> strs = new List <string> (10000 );
Char [] chs = new char [3];
For (int I = 0; I <10000; I ++)
{
Int j = I;
For (int k = 0; k <chs. Length; k ++)
{
Chs [k] = (char) ('A' + j % 26 );
J = j/26;
}
Strs. Add (new string (chs ));
}
Return strs;
}
Then let's take a look at ToUpper's implementation:
Private static bool ImplementByToUpper (List <string> strs, string value)
{
For (int I = 0; I <strs. Count; I ++)
If (value. ToUpper () = strs [I]. ToUpper ())
Return true;
Return false;
}
Finally, prepare the main method:
List <string> strs = CreateStrings ();
Bool result;
Console. WriteLine ("Use ImplementByToUpper ");
Result = MeasurePerformance (s => ImplementByToUpper (strs, s), "yZh", 1000 );
Console. WriteLine ("result is" + result. ToString ());
Console. ReadLine ();
Let's take a look at the execution results:
Use ImplementByToUpper
2192 ms
GC 0: 247
GC 1:0
GC 2: 0
Result is True
For a comparative test, use string. Equals to test:
Private static bool ImplementByStringEquals (List <string> strs, string value)
{
For (int I = 0; I <strs. Count; I ++)
If (string. Equals (value, strs [I], StringComparison. CurrentCultureIgnoreCase ))
Return true;
Return false;
}
Let's take a look at the execution results:
Use ImplementByStringEquals
1117 ms
GC 0: 0
GC 1:0
GC 2: 0
Result is True
Compared with ToUpper, ToUpper is twice as slow as ToUpper, and there are a large number of 0-generation junk objects. Those who claim to be using space for time can reflect on what space? Negative time?
Usage of dictionary classes
In the string scenario, some people may think of using Hash tables and other similar structures to accelerate. This is a good idea. However, the Hash table is not always the best solution. What do you not believe? Let's do a test:
Private static bool ImplementByHashSet (List <string> strs, string value)
{
HashSet <string> set = new HashSet <string> (strs, StringComparer. CurrentCultureIgnoreCase );
Return set. Contains (value );
}
Check the execution result:
Use ImplementByHashSet
5114 ms
GC 0: 38
GC :38
GC 2: 38
Result is True
Surprised, the speed is more than doubled than ToUpper, and the second generation of garbage is 38 times recycled (when the second generation of garbage is recycled, the first and 0th generation of garbage collection will be enforced ).
However, using Hash tables and other similar methods to accelerate this idea is a very correct idea, but the premise is that the Hash table itself can be cached, for example:
Private static Func <string, bool> ImplementByHashSet2 (List <string> strs)
{
HashSet <string> set = new HashSet <string> (strs, StringComparer. CurrentCultureIgnoreCase );
Return set. Contains;
}
Then, modify the main method:
Console. WriteLine ("Use ImplementByHashSet2 ");
Result = MeasurePerformance (s =>
{
Var f = ImplementByHashSet2 (strs );
Bool ret = false;
For (int I = 0; I <1000; I ++)
{
Ret = f (s );
}
Return ret;
}, "YZh", 1 );
Console. WriteLine ("result is" + result. ToString ());
Console. ReadLine ();
Let's look at the results:
Use ImplementByHashSet2
6 ms
GC 0: 0
GC 1:0
GC 2: 0
Result is True
The performance has experienced rapid growth.
More
What slowed down C #/. NET? To put it simply, you can create unnecessary objects, synchronize unnecessary objects, and execute inefficient methods cyclically (for example, reflection by firelong, however, ms does not allow you to use Invoke in the loop), and uses inefficient data structures and algorithms (you can see the amazing performance of the Hash table's similar structure in the cache, so you will know the difference ).
C #/. NET's low threshold is indeed conducive to pulling more programmers into C #/. NET, but the entire C #/. the code level of the NET program has been reduced a lot, which is indeed very worrying.