程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#和.NET不可忍受之慢,誰是罪魁禍首

C#和.NET不可忍受之慢,誰是罪魁禍首

編輯:C#入門知識

 

前些日子,有爆出N篇說C#/.NET太慢的,要求刪除C#/.NET部分特性的文章。

  撇開那些文章不說,C#/.NET慢似乎是業界公認的鐵則,不論大家如何證明C# / .NET其實不比C++慢多少,但是應用程序級別的性能卻依然這麼慢。

 

  那麼C#/.NET慢在哪裡?

 

  很不幸的是大部分c#程序是被大部分程序員拖慢的,也許這個結論不太容易被人接受,卻是一個廣泛存在的。

 

  String的操作

 

  幾乎所有的程序都有String操作,至少90%的程序需要忽略大小寫的比較,檢查一下代碼,至少其中大半的應用程序有類似這樣的代碼:

 

if (str1.ToUpper() == str2.ToUpper())

  或者ToLower版的,甚至我還看到過有個Web的HttpModule裡面寫上了:

 

for (int i = 0; i < strs.Count; i++)

if (value.ToUpper() == strs[i].ToUpper())

//...

  想一下,每個頁面請求過來,都要執行這樣一段代碼,大片大片的創建string實例,更誇張的是還有人說這是用空間換時間。

 

  性能測試

 

  說這個方法慢,也許還有人不承認,認為這個就是最好的方法,所以這裡要用具體測試來擺個事實。

 

  首先准備一個測試性能的方法:

 

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;

}

  然後來准備一個堆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;

}

 

  然後來看看ToUpper的實現:

 

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;

}

  最後准備好main方法:

 

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

  來看看執行結果:

 

Use ImplementByToUpper

2192ms

GC 0:247

GC 1:0

GC 2:0

result is True

  來個對比測試,用string.Equals來測試一下:

 

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;

}

  來看看執行結果:

 

Use ImplementByStringEquals

1117ms

GC 0:0

GC 1:0

GC 2:0

result is True

  對比一下,使用ToUpper的速度要慢一倍,並且有大量的0代垃圾對象。那些號稱是用空間換時間的人可以反思一下了,用空間換來了什麼?負時間嗎?

 

 

 

  字典類的使用

 

  繼續說string的場景,有些人也許會想到用Hash表等類似結構來加速,不錯,這是個好主意,只不過,Hash表不一定總是最佳方案,什麼不相信?還是做個測試吧:

 

private static bool ImplementByHashSet(List<string> strs, string value)

{

HashSet<string> set = new HashSet<string>(strs, StringComparer.CurrentCultureIgnoreCase);

return set.Contains(value);

}

  看看執行結果:

 

Use ImplementByHashSet

5114ms

GC 0:38

GC 1:38

GC 2:38

result is True

  驚訝吧,速度比用ToUpper還慢了1倍多,而且2代垃圾也38次的回收(執行2代垃圾回收時,會強制執行1代和0代垃圾回收)。

 

  不過使用Hash表等類似來加速這個想法本身是一個很正確的想法,不過前提是Hash表本身能夠緩存,例如:

 

private static Func<string, bool> ImplementByHashSet2(List<string> strs)

{

HashSet<string> set = new HashSet<string>(strs, StringComparer.CurrentCultureIgnoreCase);

return set.Contains;

}

  然後把main的方法修改為:

 

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

  再看看結果:

 

Use ImplementByHashSet2

6ms

GC 0:0

GC 1:0

GC 2:0

result is True

  性能出現了飛躍性的增長。

 

  更 多

 

  是什麼拖慢了C#/.NET?簡單的說:不必要的創建對象,不必要的同步,循環執行低效的方法(例如被firelong重點批斗的反射,不過ms並沒讓你在循環裡面使用Invoke),使用低效的數據結構和算法(看看緩存情況下Hash表類似結構的驚人表現,就知道區別了)。

 

  C#/.NET的低門檻確實在一定程度上有利於把更多的程序員拉入C#/.NET,但是也確實把整個C#/.NET程序的代碼水平降低了不少,這一點確實很令人擔憂。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved