测试驱动javascript开发 -- 5.性能测试

  自动化测试另外一个比较重要的内容点是性能测试,很多问题可能有不止一种解决方案,很多时候并不知道哪个是最好的解决方案。例如有很多创建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 tests
runBenchmark("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 function function benchmark (name, tests, iterations) {   iterations = iterations || 1000;   times = {};   var view = init(name);   runTests(tests, view, iterations);   highlightExtremes(view); }

 

  2.分析和定位瓶颈

  很多浏览器提供了调试工具,例如firefox的firebug。这些工具可以为我们提供很多信息,帮助我们跟踪代码,判断代码中的问题。例如下图就是使用firebug的实例。

 

代码下载:

posted @ 2012-11-07 16:38  下一站永远  阅读(1665)  评论(0编辑  收藏  举报