Efficient GOTO

JavaScript performance comparison

Test case created by Tim Caswell

Preparation code


      
      <script>
Benchmark.prototype.setup = function() {
  function proto1(env, args, exit) {
    // Allocate slots for frames
    var f0, f1, f2, f3;
    // Split the code into blocks to enable jumps
    function _0001() {
      f0 = [ null, 'This', 'is', 'a', 'stream?' ]; //0001    TDUP     0   0
      f1 = 1;                 //0002    KSHORT   1   1
      f2 = env.print;         //0003    GGET     2   1      ; "print"
      f3 = "before";          //0004    KSTR     3   2      ; "before"
      f2(f3);                 //0005    CALL     2   1   2
      return _0006;
    }
    function _0006() {
      f2 = f0[f1];            //0006 => TGETV    2   0   1
      if (!f2)                //0007    ISF          2
        return _0015;         //0008    JMP      3 => 0015
                              //0009    LOOP     2 => 0015
      f2 = env.print;         //0010    GGET     2   1      ; "print"
      f3 = f0[f1];            //0011    TGETV    3   0   1
      f2(f3);                 //0012    CALL     2   1   2
      f1 = f1 + 1;            //0013    ADDVN    1   1   0  ; 1
      return _0006;           //0014    JMP      2 => 0006
    }
    function _0015() {
      f2 = env.print;         //0015 => GGET     2   1      ; "print"
      f3 = "after";           //0016    KSTR     3   3      ; "after"
      f2(f3);                 //0017    CALL     2   1   2
      exit(null, []);         //0018    RET0     0   1
    }
  
    // Run a little trampoline to enable the gotos
    var next = _0001;
    while (next = next());
  }
  
  function proto2(env, args, exit) {
    // Allocate slots for frames
    var f0, f1, f2, f3;
    // Split the code into blocks to enable jumps
    var blocks = {
      _0001: function () {
        f0 = [ null, 'This', 'is', 'a', 'stream?' ]; //0001    TDUP     0   0
        f1 = 1;                 //0002    KSHORT   1   1
        f2 = env.print;         //0003    GGET     2   1      ; "print"
        f3 = "before";          //0004    KSTR     3   2      ; "before"
        f2(f3);                 //0005    CALL     2   1   2
        return "_0006";
      },
      _0006: function () {
        f2 = f0[f1];            //0006 => TGETV    2   0   1
        if (!f2)                //0007    ISF          2
          return "_0015";       //0008    JMP      3 => 0015
                                //0009    LOOP     2 => 0015
        f2 = env.print;         //0010    GGET     2   1      ; "print"
        f3 = f0[f1];            //0011    TGETV    3   0   1
        f2(f3);                 //0012    CALL     2   1   2
        f1 = f1 + 1;            //0013    ADDVN    1   1   0  ; 1
        return "_0006";         //0014    JMP      2 => 0006
      },
      _0015: function () {
        f2 = env.print;         //0015 => GGET     2   1      ; "print"
        f3 = "after";           //0016    KSTR     3   3      ; "after"
        f2(f3);                 //0017    CALL     2   1   2
        exit(null, []);         //0018    RET0     0   1
      }
    };
  
    // Run a little trampoline to enable the gotos
    var next = "_0001";
    while (next = blocks[next]());
  }
  
  function proto3(env, args, exit) {
    // Allocate slots for frames
    var f0, f1, f2, f3;
    var state = "0001";
    main: while(true) {
      switch(state) {
        case "0001":
          f0 = [ null, 'This', 'is', 'a', 'stream?' ]; //0001    TDUP     0   0
          f1 = 1;                 //0002    KSHORT   1   1
          f2 = env.print;         //0003    GGET     2   1      ; "print"
          f3 = "before";          //0004    KSTR     3   2      ; "before"
          f2(f3);                 //0005    CALL     2   1   2
          state = "0006";
          break;
        case "0006":
          f2 = f0[f1];            //0006 => TGETV    2   0   1
          if (!f2) {              //0007    ISF          2
            state = "0015";       //0008    JMP      3 => 0015
            break;
          }
          // no-op tracing hook   //0009    LOOP     2 => 0015
          f2 = env.print;         //0010    GGET     2   1      ; "print"
          f3 = f0[f1];            //0011    TGETV    3   0   1
          f2(f3);                 //0012    CALL     2   1   2
          f1 = f1 + 1;            //0013    ADDVN    1   1   0  ; 1
          state = "0006";         //0014    JMP      2 => 0006
          break;
        case "0015":
          f2 = env.print;         //0015 => GGET     2   1      ; "print"
          f3 = "after";           //0016    KSTR     3   3      ; "after"
          f2(f3);                 //0017    CALL     2   1   2
          exit(null, []);         //0018    RET0     0   1
          break main;
      }
    }
  }
  
  function proto4(env, args, exit) {
    // Allocate slots for frames
    var f0, f1, f2, f3;
    var state = "0001";
    main: while(true) {
      switch(state) {
        case "0001":
          f0 = [ null, 'This', 'is', 'a', 'stream?' ]; //0001    TDUP     0   0
          f1 = 1;                 //0002    KSHORT   1   1
          f2 = env.print;         //0003    GGET     2   1      ; "print"
          f3 = "before";          //0004    KSTR     3   2      ; "before"
          f2(f3);                 //0005    CALL     2   1   2
        case "0006":
          f2 = f0[f1];            //0006 => TGETV    2   0   1
          if (!f2) {              //0007    ISF          2
            state = "0015";       //0008    JMP      3 => 0015
            break;
          }
          // no-op tracing hook   //0009    LOOP     2 => 0015
          f2 = env.print;         //0010    GGET     2   1      ; "print"
          f3 = f0[f1];            //0011    TGETV    3   0   1
          f2(f3);                 //0012    CALL     2   1   2
          f1 = f1 + 1;            //0013    ADDVN    1   1   0  ; 1
          state = "0006";         //0014    JMP      2 => 0006
          break;
        case "0015":
          f2 = env.print;         //0015 => GGET     2   1      ; "print"
          f3 = "after";           //0016    KSTR     3   3      ; "after"
          f2(f3);                 //0017    CALL     2   1   2
          exit(null, []);         //0018    RET0     0   1
          break main;
      }
    }
  }
  
  function proto5(env, args, exit) {
    // Allocate slots for frames
    var f0, f1, f2, f3;
    var state = "0001";
    main: while(true) {
      switch(state) {
        case "0001":
          f0 = [ null, 'This', 'is', 'a', 'stream?' ]; //0001    TDUP     0   0
        case "0002":
          f1 = 1;                 //0002    KSHORT   1   1
        case "0003":
          f2 = env.print;         //0003    GGET     2   1      ; "print"
        case "0004":
          f3 = "before";          //0004    KSTR     3   2      ; "before"
        case "0005":
          f2(f3);                 //0005    CALL     2   1   2
        case "0006":
          f2 = f0[f1];            //0006 => TGETV    2   0   1
        case "0007":
          if (!f2) {              //0007    ISF          2
            state = "0015";       //0008    JMP      3 => 0015
            break;
          }
        case "0009":
          // no-op tracing hook   //0009    LOOP     2 => 0015
        case "0010":
          f2 = env.print;         //0010    GGET     2   1      ; "print"
        case "0011":
          f3 = f0[f1];            //0011    TGETV    3   0   1
        case "0012":
          f2(f3);                 //0012    CALL     2   1   2
        case "0013":
          f1 = f1 + 1;            //0013    ADDVN    1   1   0  ; 1
        case "0014":
          state = "0006";         //0014    JMP      2 => 0006
          break;
        case "0015":
          f2 = env.print;         //0015 => GGET     2   1      ; "print"
        case "0016":
          f3 = "after";           //0016    KSTR     3   3      ; "after"
        case "0017":
          f2(f3);                 //0017    CALL     2   1   2
        case "0018":
          exit(null, []);         //0018    RET0     0   1
          break main;
      }
    }
  }
  
  function proto6(env, args, exit) {
    // Allocate slots for frames
    var f0, f1, f2, f3;
    var state = "0001";
    main: while(true) {
      loop: switch(state) {
        case "0001":
          f0 = [ null, 'This', 'is', 'a', 'stream?' ]; //0001    TDUP     0   0
          f1 = 1;                 //0002    KSHORT   1   1
          f2 = env.print;         //0003    GGET     2   1      ; "print"
          f3 = "before";          //0004    KSTR     3   2      ; "before"
          f2(f3);                 //0005    CALL     2   1   2
        case "0006":
        while(true) {
          f2 = f0[f1];            //0006 => TGETV    2   0   1
          if (!f2) {              //0007    ISF          2
            state = "0015";       //0008    JMP      3 => 0015
            break loop;
          }
          // no-op tracing hook   //0009    LOOP     2 => 0015
          f2 = env.print;         //0010    GGET     2   1      ; "print"
          f3 = f0[f1];            //0011    TGETV    3   0   1
          f2(f3);                 //0012    CALL     2   1   2
          f1 = f1 + 1;            //0013    ADDVN    1   1   0  ; 1
        }                         //0014    JMP      2 => 0006
        case "0015":
          f2 = env.print;         //0015 => GGET     2   1      ; "print"
          f3 = "after";           //0016    KSTR     3   3      ; "after"
          f2(f3);                 //0017    CALL     2   1   2
          exit(null, []);         //0018    RET0     0   1
          break main;
      }
    }
  }
  
  function fn0() {
    var $, $$, $0, $1, $2, $3;
    var state = "0";
    for(;;) {
      loop: switch(state) {
      case "0":
        $0 = [null,"This","is","a","stream?"];
        $1 = 0x1;
        $2 = this["print"];
        $3 = "before";
        $2($3);
      case "5":
        $2 = $0[$1];
        if (!$2) { state = "e"; break loop; }
        // LOOP
        $2 = this["print"];
        $3 = $0[$1];
        $2($3);
        $1 = $1 + 1;
        state = "5"; break loop;
      case "e":
        $2 = this["print"];
        $3 = "after";
        $2($3);
        return [];
      }
    }
  }
  
  // Set up some global functions
  var _G = {
    print: function () {
      //console.log(Array.prototype.join.call(arguments, " "));
      return [];
    }
  };
  function onExit(err, results) {
    if (err) throw err;
  }
  

};
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
Named Function Blocks
proto1(_G, [], onExit);
pending…
Object Function Blocks
proto2(_G, [], onExit);
pending…
Switch Blocks
proto3(_G, [], onExit);
pending…
Switch Blocks with fall-through
proto4(_G, [], onExit);
pending…
Lots of switch cases
proto5(_G, [], onExit);
pending…
Fall through switch with loop detection
proto6(_G, [], onExit);
pending…
Actual Generated code
fn0.apply(_G, []);
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL.

0 Comments