Efficient GOTO

JavaScript performance comparison

Test case created by Tim Caswell

Info

This jsperf is to explore options for generating JS out of luajit bytecode. https://gist.github.com/4206005#file_compiled.js

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 unknown unknown
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. Here’s a list of current revisions for this page:

0 comments

Add a comment