本文不講C#開發技巧,只測試同樣的代碼在不同CPU的機器上的運行速度。結果包括普通版本和並行版本的對比,方便大家選配電腦,對“性能”有個感性的認識。
一. 測試原理
字符串相關度計算是文本處理和數據挖掘中一個不可或缺的方法,例如論文查重等。Levenshtein Distance可以用來比較兩個字符串的相似度,即兩個字符串之間的“距離”。這個“距離”其實就是從源字符串變換到目標字符串需要進行的刪除、插入和替換的次數。算法簡圖如下:
有不少該算法的改進版本,可大幅度提升效率,不過這不是本文的目的。我們的目的就是看看該算法在不同的機器下到底能跑多快,再看並行優化后又能跑多快。該算法基本只對CPU性能敏感,很適合做測試。
我們隨機生成1000個1000長度的字符串,並比較字符串相關度。示例工程可在文末找到,由於需要並行計算,需要VS2010,.NET4.0支持。
代碼如下:

namespace SpeedTest { using System; using System.Diagnostics; using System.Text; using System.Threading.Tasks; internal class Program { #region Methods private static void Main(string[] args) { var watch =new Stopwatch(); const long count = 1000; const int length = 1000; string comparestring = StringDistance.GenerateRandomString(length); var strlist = new string[count]; var steps = new int[count]; // prepare string[] for comparison Parallel.For(0, count, i => strlist[i] = StringDistance.GenerateRandomString(length)); Console.WriteLine("已經生成了" + count + "個長度為" + length + "的字符串"); watch.Start(); for (int i = 0; i < count; i++) { steps[i] = StringDistance.LevenshteinDistance(comparestring, strlist[i]); } watch.Stop(); Console.WriteLine("完成非並行計算,耗時(ms)"+watch.ElapsedMilliseconds); Console.WriteLine("性能比" + 100000d/watch.ElapsedMilliseconds); watch.Reset(); watch.Start(); Parallel.For( 0, count, delegate(long i) { steps[i] = StringDistance.LevenshteinDistance(comparestring, strlist[i]); }); watch.Stop(); Console.WriteLine("完成並行計算,耗時(ms)" + watch.ElapsedMilliseconds); Console.WriteLine("性能比" + 100000d / watch.ElapsedMilliseconds); Console.ReadKey(); } #endregion } internal class StringDistance { #region Public Methods public static string GenerateRandomString(int length) { var r = new Random((int)DateTime.Now.Ticks); var sb = new StringBuilder(length); for (int i = 0; i < length; i++) { int c = r.Next(97, 123); sb.Append(Char.ConvertFromUtf32(c)); } return sb.ToString(); } public static int LevenshteinDistance(string str1, string str2) { var scratchDistanceMatrix = new int[str1.Length + 1,str2.Length + 1]; // distance matrix contains one extra row and column for the seed values for (int i = 0; i <= str1.Length; i++) { scratchDistanceMatrix[i, 0] = i; } for (int j = 0; j <= str2.Length; j++) { scratchDistanceMatrix[0, j] = j; } for (int i = 1; i <= str1.Length; i++) { int str1Index = i - 1; for (int j = 1; j <= str2.Length; j++) { int str2Index = j - 1; int cost = (str1[str1Index] == str2[str2Index]) ? 0 : 1; int deletion = (i == 0) ? 1 : scratchDistanceMatrix[i - 1, j] + 1; int insertion = (j == 0) ? 1 : scratchDistanceMatrix[i, j - 1] + 1; int substitution = (i == 0 || j == 0) ? cost : scratchDistanceMatrix[i - 1, j - 1] + cost; scratchDistanceMatrix[i, j] = Math.Min(Math.Min(deletion, insertion), substitution); // Check for Transposition if (i > 1 && j > 1 && (str1[str1Index] == str2[str2Index - 1]) && (str1[str1Index - 1] == str2[str2Index])) { scratchDistanceMatrix[i, j] = Math.Min( scratchDistanceMatrix[i, j], scratchDistanceMatrix[i - 2, j - 2] + cost); } } } // Levenshtein distance is the bottom right element return scratchDistanceMatrix[str1.Length, str2.Length]; } #endregion } }
廢話不說,上測試結果。
二.測試結果
1. Debug,Debug直接運行,Release調試,Release直接運行的區別
以上四種環境對應不同開發階段的軟件性能,對開發的性能調優有指導價值。
電腦配置如下:
測試結果如下,數值為執行時間,單位為ms
值得注意的是,在不同代碼下四種環境的結果是很不相同的。一般情況下軟件直接運行要比調試快很多。Release模式比Debug模式快幾倍。但在這個示例下,直接運行和調試卻沒有特別明顯的區別,希望大神能給予解答。
另外,並行算法比普通的串行算法快了四倍。符合四核CPU的實際情況。
2.不同CPU下的性能
接下來,我們來看不同CPU下的性能區別。為簡化測試,以下所有結果都采用Release運行模式。對比不同CPU在串行/並行下的速度區別。
結果都是以執行時間ms表示的,值越小越好。
古老電腦AMD QL-62 雙核心 2.00GHz
難得找到AMD的筆記本電腦,該CPU已經停產很久了,三年前的CPU不負眾望,成績還不錯,當然並行成績比服務器慢了十幾倍。
筆記本T8100,雙核2.4G
I3 2310 筆記本核心,雙核,2.1GHz
雖然比T8100的頻率低,但新一代酷睿I3還是完虐T8100的。
桌面老牌酷睿E7300 雙核心2.67GHz
回想06年底,能買到酷睿E6300是剛上高三的我最大的夢想,現在看來是在不敢恭維,並行速度和I7比差了四倍不止。這款當時很牛的桌面CPU連現在低端的筆記本CPU都不如。
華碩筆記本UX31A, I5 3317U 雙核雙線程, @2.3GHz Auto OC
超級本的性能還是不錯的,也體現出了雙核的優勢。在“節能模式”下,CPU不會自動睿頻,只有1.7GHz。結果慘不忍睹。超級本一般情況下做開發足夠了。
第二代酷睿筆記本I5 2430 @ 2.3GHz
不得不說,標壓筆記本的I5都可以完虐超低壓的I7. 誰讓超級本必須做到那么薄呢? 價格還死貴。如果考慮性能和性價比,還是選標壓CPU吧。
聯想台式機 I3 2130 @3.4GHz
雖然是I3,但在串行下與同頻的I7幾乎無區別,當然由於是雙核的,並行速度正好是串行的一半。
I7 2600 四核八線程 3.4GHz
實在沒有找到2600K的超頻結果,就先用2600默頻湊數吧,單核性能中規中矩。
I5 2500K OC 4.2GHz 四核四線程
寢室台式機的跑分,看來頻率是王道啊,在4.2G輕松秒殺I7 2600。
服務器核心X5670 六核心 @2.93Ghz
服務器六核心CPU看似高端,但在這個程序下,還是無法與2600甚至2130抗衡,並行加速比4.66, 六核心有點小吃虧。
服務器核心E5 2690 8核心,雙CPU共16核心, 2.9GHz *2
這幾乎是我目前看到的最好結果, 該CPU在串行模式下就達到了11.657s的水平,並行加速比達到了可喜的7.32。可是與2500K這樣的CPU不對等的是它的售價,一萬三四夠買10個2500K了。
3. 測試總結和對比
下圖是不同CPU串行速度的對比圖:
我們看到基本上同平台,同架構的CPU間,頻率是王道。而當前I3新酷睿都可以干掉當年桌面的老酷睿經典,不得不說技術發展迅速。而服務器核心雖然核心多,甚至是雙CPU,但頻率一般,因此在這種普通應用下甚至抗不過桌面的I5 2500K。 能多核優化的程序還是占少數,大家能超就超一點吧,超頻帶來的性能提升真心不是一點點。
下圖是並行下速度對比圖:
並行計算下,核的數量是王道!在非服務器核心上,加速比幾乎就是CPU核心數。四核的速度就是比雙核的快。在八核的頂級服務器CPU上,它的速度幾乎是AMD QL-62的13倍!但若算是這台機器有雙CPU16個核心的話,加速比和16還是差很多的。難道.NET並行庫對多CPU支持不好?我怎么記得它可以支持最多64個CPU?
三. 總結和源代碼下載
咱不是專業的硬件測評師,只是好奇之下跑了幾個分數看了他們的性能而已,花了整整一晚上時間,因此若有不足之處還請海涵。
總體來說,單核性能頻率和架構決定一切,多核性能上核心數決定了加速比。 桌面級應用完全沒必要買服務器的多核心(E3 1230這類除外)。大部分程序最多只對四核CPU做過優化。更多核心甚至雙CPU會造成各種不兼容性問題,連游戲都打不了(嘿嘿),編程都卡。
現在覺得,I5 2500K還是要比E3 1230性價比高很多,超頻超出來的性能不是蓋的。別擔心壽命,它只會在需要的時候睿頻到更高的頻率,平時都是乖乖的二點幾吉赫茲。而且非常簡單!Enjoy it!
程序並行的優勢還是非常明顯的,.NET的並行庫可比Intel並行庫方便多了(雖然效率不能比),但它可以很好的幫助你挖掘多核CPU的潛力。大家可以多學習一下.NET並行庫。