javascript

时间:2018-05-28 17:32:38

标签: javascript performance loops continuations

为了查看实现循环的延续传递样式是否在javascript中使用太慢,我创建了一个JS-Perf来使用以下代码对其进行测试:

 const ITERATIONS = 10000;

  function NormalLoop() {
      for (var i = 0; i < ITERATIONS; i++) {
          console.log("loop iteration");
          if (i % 5) {
              console.log("continuing");
              i += 2;
              continue;
          }
          console.log("normally going out");
      }
      console.log("ended loop");
  }

  function WhileTrueLoop() {
      var i = 0;
      while (true) {
          if (i >= ITERATIONS) {
              break;
          }
          console.log("loop iteration");
          if (i % 5) {
              console.log("continuing");
              i += 2;
              i++;
              continue;
          }
          console.log("normally going out");
          i++;
      }
      console.log("ended loop");
  }

  function NonTrampLoop() {
      var i = 0;
      n1(i);
  }

  function n1(i) {
      if (i >= ITERATIONS) {
          n4(i);
          return;
      }
      console.log("loop iteration");
      if (i % 5) {
          console.log("continuing");
          i += 2;
          n3(i)
          return;
      }

      n2(i)
  }

  function n2(i) {
      console.log("normally going out");
      n3(i);
  }

  function n3(i) {
      i = i + 1;
      n1(i);
  }

  function n4() {
      console.log("ended loop");
  }

  function TrampolineSimplistic() {
      var f = function () { return ts1(0) };
      while (f !== null) { f = f(); }
      console.log("ended loop");
  }

  function ts1(i) {
      if (i >= ITERATIONS) {
          return null;
      }
      console.log("loop iteration");
      if (i % 5) {
          console.log("continuing");
          i += 2;
          return function () { return ts3(i); };
      }

      return function () { return ts2(i); };
  }

  function ts2(i) {
      console.log("normally going out");
      return function () { return ts3(i); }
  }

  function ts3(i) {
      i = i + 1;
      return function () { return ts1(i); }
  }

  function TrampolineStreamlined() {

      var f = { cont: t1, i: 0 };
      while (f.cont !== null) { f.cont(f); }
      console.log("ended loop");
  }

  function t1(th) {
      var i = th.i;
      if (i >= ITERATIONS) {
          th.cont = null;
          return;
      }
      console.log("loop iteration");
      if (i % 5) {
          i = i + 2;
          th.i = i;
          th.cont = t3;
          return;
      }

      th.i = i;
      th.cont = t2;
      return;
  }

  function t2(th) {
      var i = th.i;
      console.log("normally going out");
      th.i = i;
      th.cont = t3;
      return;
  }

  function t3(th) {
      var i = th.i;
      i = i + 1;
      th.i = i;
      th.cont = t1;
      return;
  }

这五种方法是循环标准,一个真正的循环,使用朴素函数调用,使用trampolining和CPS,并使用trampolining和CPS在堆上预分配局部变量。

我期望for循环是最快的,紧接着是while-true循环,然后trampolining循环比for循环长2-10倍,而naive函数循环比for for循环长10-100倍-loop。

现在令人震惊的是,trampolining循环似乎在firefox上表现最快。最慢的循环似乎是真正的循环!即使是天真的函数调用循环也相对较快。当天真函数循环与迭代次数成比例地增加堆栈时,这是怎么回事,而其他方法使用常量堆栈空间。

此外,naive trampoline循环在每次执行期间多次在堆上分配一个函数。 javascript引擎在优化函数调用方面是否过于激进?我的代码中是否有异常愚蠢的事情?

1 个答案:

答案 0 :(得分:1)

不是答案,但我在一些浏览器上运行了一些时间。时间以毫秒为单位,迭代次数取决于浏览器中的粗略最大大小,然后才会出现堆栈大小错误。

Firefox
Iterations      : 30000
NormalLoop      : 0
WhileTrueLoop   : 1
NonTrampLoop    : 22
TrampSimplistic : 2
TrampStreamlined: 1

Chrome
Iterations      : 14000
NormalLoop      : 3
WhileTrueLoop   : 2
NonTrampLoop    : 1
TrampSimplistic : 7
TrampStreamlined: 3

Edge
Iterations      : 5000
NormalLoop      : 0
WhileTrueLoop   : 1
NonTrampLoop    : 3
TrampSimplistic : 11
TrampStreamlined: 3

Chrome
Iterations      : 200000
NormalLoop      : 4
WhileTrueLoop   : 4
TrampSimplistic : 68
TrampStreamlined: 14

Firefox中的时代非常一致,对于其他浏览器,结果各不相同,我使用的输出我认为是最一致的。