文章目錄
自動化測試另外一個比較重要的內容點是效能測試,很多問題可能有不止一種解決方案,很多時候並不知道哪個是最好的解決方案。例如有很多建立javascript對象的方法,使用javascript構造器、使用函數的方法或者使用閉包。我們可能會從可測試化、靈活性和效能的角度去考慮使用哪種方式。足見效能是相當重要的一點。
1.基準和相對效能
當一個問題我們有兩個以上的解決方案的時候,判斷哪個解決方案更好的原則很簡單,就是哪個的效能更好。判斷的原則也很簡單:1.建立new Date()作為開始時間;2.執行要衡量的代碼;3.代碼執行完畢,建立new Date()作為結束時間,減去開始時間算出總時間長度;4.替換執行代碼,重複上述步驟;5.比較各種方案的執行時間長度。
每個要比較的代碼我們需要執行很多次,通常會把他們放在一個迴圈中。因為windows xp和vista作業系統的瀏覽器的timer的間隔時間是15ms,讓這個問題變的更加複雜,測試會變的相當不準確,所以我們需要保證測試代碼運行時間長度在500ms以上。
下面是我們用來做效能測試的代碼,檔案為benchmark.js:
var ol;
function runBenchmark(name, test) { if (!ol) { ol = document.createElement("ol"); document.body.appendChild(ol); } setTimeout(function () { var start = new Date().getTime(); test(); var total = new Date().getTime() - start; var li = document.createElement("li"); li.innerHTML = name + ": " + total + "ms"; ol.appendChild(li); }, 15);}
下面的代碼使用runBenchmark()用來做測試,檔案名稱為loops.js:
var loopLength = 500000;// 填充迴圈數組var array = [];for (var i = 0; i < loopLength; i++) { array[i] = "item" + i;}function forLoop() { for (var i = 0, item; i < array.length; i++) { item = array[i]; }}function forLoopCachedLength() { for (var i = 0, l = array.length, item; i < l; i++) { item = array[i]; }}function forLoopDirectAccess() { for (var i = 0, item; (item = array[i]); i++) { }}function whileLoop() { var i = 0, item; while (i < array.length) { item = array[i]; i++; }}function whileLoopCachedLength() { var i = 0, l = array.length, item; while (i < l) { item = array[i]; i++; }}function reversedWhileLoop() { var l = array.length, item; while (l--) { item = array[l]; }}function doubleReversedWhileLoop() { var l = array.length, i = l, item; while (i--) { item = array[l - i - 1]; }}// Run testsrunBenchmark("for-loop", forLoop);runBenchmark("for-loop, cached length", forLoopCachedLength);runBenchmark("for-loop, direct array access", forLoopDirectAccess);runBenchmark("while-loop", whileLoop);runBenchmark("while-loop, cached length property", whileLoopCachedLength);runBenchmark("reversed while-loop", reversedWhileLoop);runBenchmark("double reversed while-loop", doubleReversedWhileLoop);
使用setTimeout方法的目的是,在測試中避免瀏覽器的堵塞。因為瀏覽器只有一個單線程,他用這一個線程完成javascript的運行、事件的觸發和頁面的渲染工作。timer則為瀏覽器提供了一個任務隊列,可以用他來處理長時間啟動並執行工作,這樣就可以避免在運行測試代碼時彈出逾時警告。
下面我們看看loops.html頁面的內容:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"><html> <head> <title>Relative performance of loops</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> </head><body> <h1>Relative performance of loops</h1> <script type="text/javascript" src="benchmark.js"></script> <script type="text/javascript" src="loops.js"></script></body></html>
所有的測試做的事情都差不多,遍曆數組,並訪問每個成員。通過測試,我們可以看到哪種方式效率最高。
我們可以把runBenchmark方法做一下重構:
var benchmark = (function () { function init(name) { var heading = document.createElement("h2"); heading.innerHTML = name; document.body.appendChild(heading); var ol = document.createElement("ol"); document.body.appendChild(ol); return ol; }
function runTests(tests, view, iterations) { for (var label in tests) { if (!tests.hasOwnProperty(label) || typeof tests[label] != "function") { continue; } (function (name, test) { setTimeout(function () { var start = new Date().getTime(); var l = iterations; while (l--) { test(); } var total = new Date().getTime() - start; var li = document.createElement("li"); li.innerHTML = name + ": " + total + "ms (total), " + (total / iterations) + "ms (avg)"; view.appendChild(li); }, 15); }(label, tests[label])); } }
function benchmark(name, tests, iterations) { iterations = iterations || 1000; var view = init(name); runTests(tests, view, iterations); }
return benchmark;}());
使用benchmark也很簡單,如下:
var loopLength = 100000;var array = [];for (var i = 0; i < loopLength; i++) { array[i] = "item" + i;}benchmark("Loop performance", { "for-loop": function () { for (var i = 0, item; i < array.length; i++) { item = array[i]; } }, "for-loop, cached length": function () { for (var i = 0, l = array.length, item; i < l; i++) { item = array[i]; } }, // ... "double reversed while-loop": function () { var l = array.length, i = l, item; while (i--) { item = array[l - i - 1]; } }}, 1000);
我們也可以為benchmark擴充更多的功能,例如把最快和最慢的測試用高亮的方式顯示:
// 記錄次數var times;function runTests (tests, view, iterations) { // ... (function (name, test) { // ... var total = new Date().getTime() - start; times[name] = total; // ... }(label, tests[label])); // ...}
function highlightExtremes(view) { // The timeout is queued after all other timers, ensuring // that all tests are finished running and the times // object is populated setTimeout(function () { var min = new Date().getTime(); var max = 0; var fastest, slowest; for (var label in times) { if (!times.hasOwnProperty(label)) { continue; } if (times[label] < min) { min = times[label]; fastest = label; } if (times[label] > max) { max = times[label]; slowest = label; } } var lis = view.getElementsByTagName("li"); var fastRegexp = new RegExp("^" + fastest + ":"); var slowRegexp = new RegExp("^" + slowest + ":"); for (var i = 0, l = lis.length; i < l; i++) { if (slowRegexp.test(lis[i].innerHTML)) { lis[i].style.color = "#c00"; } if (fastRegexp.test(lis[i].innerHTML)) { lis[i].style.color = "#0c0"; } } }, 15);}// Updated benchmark functionfunction benchmark (name, tests, iterations) { iterations = iterations || 1000; times = {}; var view = init(name); runTests(tests, view, iterations); highlightExtremes(view);}
2.分析和定位瓶頸
很多瀏覽器提供了調試工具,例如firefox的firebug。這些工具可以為我們提供很多資訊,協助我們跟蹤代碼,判斷代碼中的問題。例如就是使用firebug的執行個體。
代碼下載: