【问题标题】:Totally bizarre performance of loops and CPS loops in javascriptjavascript中循环和CPS循环的完全奇怪的性能
【发布时间】:2018-11-07 07:56:20
【问题描述】:

为了查看实现循环的延续传递风格是否会太慢而无法在 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;
  }

这五种方法是使用标准 for 循环、while-true 循环、使用朴素函数调用、使用蹦床和 CPS 以及使用蹦床和 CPS 在堆上预分配局部变量。

我预计 for 循环是最快的,紧随其后的是 while-true 循环,然后蹦床循环比 for 循环长 2-10 倍,朴素函数循环比 for 长 10-100 倍-循环。

现在令人震惊的是,蹦床循环似乎在 Firefox 上执行得最快。最慢的循环似乎是 while-true 循环!即使是简单的函数调用循环也相对较快。当朴素的函数循环与迭代次数成比例地增长堆栈,而其他方法使用恒定的堆栈空间时,这怎么可能呢?

此外,幼稚的蹦床循环在每次执行期间都会多次在堆上分配一个函数。 javascript 引擎在优化函数调用方面是否过于激进?我是否在我的代码中做了一些非常愚蠢的事情?

【问题讨论】:

    标签: javascript performance loops continuations


    【解决方案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 中的时间非常一致,对于其他浏览器,结果各不相同,我使用了我认为最一致的输出。

    【讨论】:

    • hmmm 我认为我的浏览器在 JSPerf 中一定会以某种方式变得奇怪。
    • Edge 和 Firefox 符合我的期望,但 chrome 完全奇怪。为什么 NonTrampLoop 表现如此出色?
    • 时间太短了,所以我认为你不应该对这些 Chrome 值下定论。迭代次数应该更多以获得更好的测试结果,但它会导致堆栈溢出错误超过 14000。
    • 如果去掉 NonTrampLoop,其他方法都不会堆栈溢出。
    • 这样做了,结果已添加到答案中。
    猜你喜欢
    • 1970-01-01
    • 2012-01-01
    • 2016-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-15
    • 2015-08-14
    相关资源
    最近更新 更多