Efficient GOTO

JavaScript performance comparison

Revision 2 of this test case created

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";
      for(;;) {
        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
            return exit(null, []);         //0018    RET0     0   1
        }
      }
    }
   
    // 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…
For / Switch Blocks + Return
proto4(_G, [], onExit);
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