JScript.net的運行效率測試(兼多種語言效率對比)
昨天的文章《用JScript.net寫.net應用程序》一文寫了之后,對于其運行效率問題有了一點疑問,所以需要進行以下測試,前人當然做過很多種測試,不過bigtall的測試方法有些不同。這里我采用了斐波納契的兩個算法,一個是遞歸實現,一個是迭代實現。采用斐波納契的理由如下:
- 它是一個復雜度為O(2n)的算法,計算量足夠大
- 相同的計算,遞歸和迭代的主要區別是堆棧的處理,我們也可以同時比較一下不同語言在調用函數之間的效率差別。
- 代碼簡單,而且算法容易理解。不同測試代碼之間的差別也小,不容易起爭議。
測試運行時候考慮到如下的情況:
- 第一次系統裝入是從磁盤裝入,而后幾次則是直接從磁盤緩存裝入,所每個測試連續運行4遍,第一遍時間不計入。
- 因為IO庫實現效率不同,所以算法代碼中不存在任何IO調用,純計算代碼。
參與比較的語言包括c,c#,標準的javascript,JScript.net,后來覺得不過癮,把java6也加上了。加上bigtall自己寫的一個計算時間的小程序和批處理,一共12段代碼,表示如下:
| fibc.c | fib2c.c |
| long Fib(long n) void main() | long Fib(long n) void main() |
| fibcs.cs | fib2cs.cs |
| public class A public static void Main() } | public class A public static void Main() } |
| fibjava.java | fib2java.java |
| public class fibjava public static void main(String[] args) } | public class fib2java public static void main(String[] args) } |
| fibjs1.js | fib2js1.js |
| function Fib(n) for(var i:int = 0; i < 10; i++) | function Fib(n) for(var i:int = 0; i < 26925370; i++) |
| fibjs2.js | fib2js2.js |
| function Fib(n:int):int for(var i:int = 0; i < 10; i++) | function Fib(n:int):int for(var i:int = 0; i < 26925370; i++) |
| ptime.cs | cp.bat |
| public class A } | @echo off csc /o+ /debug- fibcs.cs jsc /fast- /debug- fibjs1.js jsc /fast+ /debug- fibjs2.js "%JAVA_HOME%\bin\javac" -g:none fibjava.java echo 編譯完成 call:run fibc goto end :end |
運行測試的結果如下表格所示,表格內部藍色的4組數據分別為1,2,3,4測試數據,黑色數據為后三組測試結果的平均數,綠色數據為相對C語言運行耗時的比例,最后一行紅色縱比數字為相同語言【單次】遞歸和迭代算法的耗時比例。時間單位為百分之一秒:
| C | C# | java | js | JScript.net | |
| 遞歸 (10次) | 119 | 486 | 2258 | 75397 | 1571 |
| 48.67 | 447 | 483.67 | 75326.33 | 1500.67 | |
| 1 | 9.18 | 9.94 | 1547.70 | 30.83 | |
| 迭代 (26,925,370次) | 120 | 5261 | 7880 | 125786 | 9196 |
| 47.33 | 5040 | 7765.67 | 127310.33 | 9102.67 | |
| 1 | 106.48 | 164.06 | 2689.65 | 192.31 | |
| 縱比 | 0.97 | 11.28 | 16.06 | 1.69 | 6.07 |
由此,我們看出,如果橫向比較,以C語言運行速度為標準,遞歸運算的時候,C#和java的速度都慢了將近10倍,JScript.net慢了將近31倍,js因為使用了運行時綁定,速度慢了1500倍之多;而一旦消除了函數調用,使用純計算代碼的迭代算法的運行時間上,各種語言相差更大,而且明顯C#代碼比java快(這里沒有考慮基礎類庫裝入的差別,因為M$對.net有預裝入),最差的依然是javascript,不過看起來不帶調用的后期綁定似乎更快一些。不過令人驚訝的是JScript.net的編譯優化做的不錯,速度算是很快了。
縱向比較之前,我們需要對算法進行一下分析,通過簡單的代碼,我們得知fib(30)的遞歸調用次數為2692537次,10次重復就是26925370次。這個就是遞歸和迭代算法的區別所在,但是我們把迭代的次數也設定為26925370,以消除函數調用的差別,突出代碼的線性運行差別。通過對代碼的分析,我們得出代碼特征的統計表格:
| 遞歸 | 迭代 | |
| 賦值語句 | 3 | 120 |
| 變量分配 | 2 | 4 |
| 函數調用 | 2 | 0 |
| 返回 | 2 | 2 |
| 條件判斷 | 1 | 30 |
| 跳轉 | 1 | 30 |
| 累計 | 11 | 186 |
迭代算法和遞歸算法相比,明顯代碼量較大,其代碼規模大約是遞歸的186/11=16.9倍。但是運行時間中除了java體現出了這一比例之外,其他都比這個比例要小。C語言甚至時間更短,如果不考慮測試誤差,唯一合理的解釋應該是代碼優化問題,因為編譯器和CPU都有優化代碼的能力,但是顯然無論是哪種優化,都無法跨越函數調用進行優化;C#比java要快,是不是說明C#的優化器比java要好一些呢?但是JS代碼的兩個比例值有點讓我難住了,但是也并非不可解釋,因為js代碼中間可以優化的地方實在是太多太多了。
結論:
- C作為一種老牌的中高級語言,優勢沒得說。
- 遞歸少用,尤其是JavaScript,連函數調用都盡可能壓縮一些。
- 本文的最主要目的,如果用JScript.net做一般應用程序,效率應該屬于可以接受的范圍,但是千萬不要進行數值計算。
- 以前看過什么java或者C#運行效率可以達到C語言的70%之類的文章,現在看來是有水分的,如果單純比較編譯器的效率,我看差距還是明顯的。看來槍手文章還是要警惕啊!
另外給各位看官提一個小小的請求,如果哪位對python,ruby,perl等熟悉的,用相同算法做一個測試如何?
本文章算法參考了淺議Fibonacci(斐波納契)數列求解。
========================================
2007-11-16 17:30修改迭代部分的循環次數為26925370次,重新更正相關測試的時間和部分結論。非常感謝裝配腦袋的提醒。謝謝!另外對之前給大家的誤導表示歉意!

公眾號:老翅寒暑
浙公網安備 33010602011771號