Back home
Back to the tailopt article

This page serves as a test case for the Mozilla bug report concerning tail-call optimization (TCO) in Javascript. It should highlight strange interferences between the proposed TCO and JIT. Turn JIT on or off, and you'll see the results vary.

System: windows 7 professional, dell optiplex 760, intel core quad cpu Q9650 3.00GHz, RAM 8.00 GB, 64 bits

Results: Duration of a single call, in seconds: the lower, the better.


Stack depth: case name      Firefox 3.6 (JIT on)       Firefox 3.6 (JIT off)      Your browser & system

n: treeforeach_rec()        1.834e-4 s (100: base)     1.834e-4 s (100: base)     *
n: treeforeach_tail()       2.220e-4 s (121)           2.192e-4 s (119)           *
1: treeforeach_tailopt()    5.438e-4 s (296)           1.871e-4 s (102)           *

n: DOMtreeforeach_rec()     2.156e-3 s (100: base)     1.905e-3 s (100: base)     *
1: DOMtreeforeach_tail()    1.629e-3 s (76)            1.899e-3 s (100)           *
1: DOMtreeforeach_tailopt() 2.904e-3 s (135)           1.823e-3 s (96)            *

1. Generated code

---

// treeforeach_rec: global.list[9].fun.toString():

function treeforeach_rec(tree, node_id, fun) {

        // Execute fun on all nodes of the subtree tree[node_id]
        
        var a, b, node = tree[node_id];
        
        // Breadth
        fun(tree, node_id);
        
        // Depth
        for (b = 0; b < node.children.length; b++) {
            treeforeach_rec(tree, node.children[b], fun);
        }
    }

---

// treeforeach_tail: global.list[10].fun.toString():

function treeforeach_tail(tree, node_id, fun) {

        // Execute fun on all nodes of the subtree tree[node_id]
        return (function sub(_tree, _node_id, _fun, _from_below) {

            var _node = _tree[_node_id];

            if (! _from_below) { // _fun is called one and only one time per node
                _fun(_tree, _node_id);
                if (_node.firstChild)  return sub(_tree, _node.firstChild, _fun, false);
            }

            if (_node_id === node_id) return; // Done
            if (_node.nextSibling)    return sub(_tree, _node.nextSibling, _fun, false);          
            if (_node.parent)         return sub(_tree, _node.parent,      _fun, true);

            return;

        })(tree, node_id, fun, false);

    }

---

// treeforeach_tailopt: global.list[11].fun.toString():

function treeforeach_tail(tree, node_id, fun){

        
        var _tree = tree, _tree_new, _node_id = node_id, _node_id_new, _fun = fun, _fun_new, _from_below = false, _from_below_new;
L_sub: while (true) {

            var _node = _tree[_node_id];

            if (! _from_below) { 
                _fun(_tree, _node_id);
                if (_node.firstChild)  { _node_id_new = _node.firstChild;
_from_below_new = false;
_node_id = _node_id_new;
_from_below = _from_below_new;
continue L_sub;
}
            }

            if (_node_id === node_id) return; 
            if (_node.nextSibling)    { _node_id_new = _node.nextSibling;
_from_below_new = false;
_node_id = _node_id_new;
_from_below = _from_below_new;
continue L_sub;
}          
            if (_node.parent)         { _node_id_new = _node.parent;
_from_below_new = true;
_node_id = _node_id_new;
_from_below = _from_below_new;
continue L_sub;
}

            return;

        };

    }

---

// DOMtreeforeach_rec: global.list[12].fun.toString():

function DOMtreeforeach_rec(node, fun) {
        fun(node);
        var a, c = node.childNodes;
        for (a = 0; a < c.length; a++) {
            DOMtreeforeach_rec(c[a], fun);
        }
    }

---

// DOMtreeforeach_tail: global.list[13].fun.toString():

function DOMtreeforeach_tail(node, fun) {

        return (function sub(_node, _fun, _from_below) {

            if (! _from_below) { // _fun is called one and only one time per node
                _fun(_node);
                if (_node.firstChild)  return sub(_node.firstChild,  _fun, false);
            }

            if (_node.nextSibling)     return sub(_node.nextSibling, _fun, false);          
            if (_node.parentNode)      return sub(_node.parentNode,  _fun, true);

            return;
            
        })(node, fun, false);

    }

---

// DOMtreeforeach_tailopt: global.list[14].fun.toString():

function DOMtreeforeach_tail(node, fun){

        var _node = node, _node_new, _fun = fun, _fun_new, _from_below = false, _from_below_new;
L_sub: while (true) {

            if (! _from_below) { 
                _fun(_node);
                if (_node.firstChild)  { _node_new = _node.firstChild;
_from_below_new = false;
_node = _node_new;
_from_below = _from_below_new;
continue L_sub;
}
            }

            if (_node.nextSibling)     { _node_new = _node.nextSibling;
_from_below_new = false;
_node = _node_new;
_from_below = _from_below_new;
continue L_sub;
}          
            if (_node.parentNode)      { _node_new = _node.parentNode;
_from_below_new = true;
_node = _node_new;
_from_below = _from_below_new;
continue L_sub;
}

            return;
            
        };

    }

2. Sanity checks (extract from the article)

3. Misc

For the treeforeach_tail we need some text nodes with .js in them.

For the treeforeach_tail we need some text nodes with .js in them.

For the treeforeach_tail we need some text nodes with .js in them.

For the treeforeach_tail we need some text nodes with .js in them.

For the treeforeach_tail we need some text nodes with .js in them.


Produced on 2010-02-04 by mozilla-bug-report.scm - by Guillaume Lathoud (glathoud _at_ yahoo _dot_ fr)Valid HTML 4.01 Strict