Add new Splay benchmark to the V8 benchmark suite and remove
the unused parts (most) of the Prototype library from raytrace.js. Review URL: http://codereview.chromium.org/115227 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1910 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
bde19c7365
commit
1a54dd0197
@ -40,3 +40,15 @@ pages where it occurs and the number of times it is executed while
|
||||
loading each page. Finally the literal letters in the data are
|
||||
encoded using ROT13 in a way that does not affect how the regexps
|
||||
match their input.
|
||||
|
||||
|
||||
Changes from Version 3 to Version 4
|
||||
===================================
|
||||
|
||||
The Splay benchmark is a newcomer in version 4. It manipulates a
|
||||
splay tree by adding and removing data nodes, thus exercising the
|
||||
memory management subsystem of the JavaScript engine.
|
||||
|
||||
Furthermore, all the unused parts of the Prototype library were
|
||||
removed from the RayTrace benchmark. This does not affect the running
|
||||
of the benchmark.
|
||||
|
@ -31,10 +31,15 @@
|
||||
|
||||
|
||||
// A benchmark has a name (string) and a function that will be run to
|
||||
// do the performance measurement.
|
||||
function Benchmark(name, run) {
|
||||
// do the performance measurement. The optional setup and tearDown
|
||||
// arguments are functions that will be invoked before and after
|
||||
// running the benchmark, but the running time of these functions will
|
||||
// not be accounted for in the benchmark score.
|
||||
function Benchmark(name, run, setup, tearDown) {
|
||||
this.name = name;
|
||||
this.run = run;
|
||||
this.Setup = setup ? setup : function() { };
|
||||
this.TearDown = tearDown ? tearDown : function() { };
|
||||
}
|
||||
|
||||
|
||||
@ -73,7 +78,7 @@ BenchmarkSuite.suites = [];
|
||||
// Scores are not comparable across versions. Bump the version if
|
||||
// you're making changes that will affect that scores, e.g. if you add
|
||||
// a new benchmark or change an existing one.
|
||||
BenchmarkSuite.version = '3';
|
||||
BenchmarkSuite.version = '4';
|
||||
|
||||
|
||||
// To make the benchmark results predictable, we replace Math.random
|
||||
@ -114,7 +119,7 @@ BenchmarkSuite.RunSuites = function(runner) {
|
||||
continuation = suite.RunStep(runner);
|
||||
}
|
||||
if (continuation && typeof window != 'undefined' && window.setTimeout) {
|
||||
window.setTimeout(RunStep, 100);
|
||||
window.setTimeout(RunStep, 25);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -194,7 +199,7 @@ BenchmarkSuite.prototype.NotifyError = function(error) {
|
||||
|
||||
// Runs a single benchmark for at least a second and computes the
|
||||
// average time it takes to run a single iteration.
|
||||
BenchmarkSuite.prototype.RunSingle = function(benchmark) {
|
||||
BenchmarkSuite.prototype.RunSingleBenchmark = function(benchmark) {
|
||||
var elapsed = 0;
|
||||
var start = new Date();
|
||||
for (var n = 0; elapsed < 1000; n++) {
|
||||
@ -216,18 +221,45 @@ BenchmarkSuite.prototype.RunStep = function(runner) {
|
||||
var length = this.benchmarks.length;
|
||||
var index = 0;
|
||||
var suite = this;
|
||||
function RunNext() {
|
||||
|
||||
// Run the setup, the actual benchmark, and the tear down in three
|
||||
// separate steps to allow the framework to yield between any of the
|
||||
// steps.
|
||||
|
||||
function RunNextSetup() {
|
||||
if (index < length) {
|
||||
try {
|
||||
suite.RunSingle(suite.benchmarks[index++]);
|
||||
suite.benchmarks[index].Setup();
|
||||
} catch (e) {
|
||||
suite.NotifyError(e);
|
||||
return null;
|
||||
}
|
||||
return RunNext;
|
||||
return RunNextBenchmark;
|
||||
}
|
||||
suite.NotifyResult();
|
||||
return null;
|
||||
}
|
||||
return RunNext();
|
||||
|
||||
function RunNextBenchmark() {
|
||||
try {
|
||||
suite.RunSingleBenchmark(suite.benchmarks[index]);
|
||||
} catch (e) {
|
||||
suite.NotifyError(e);
|
||||
return null;
|
||||
}
|
||||
return RunNextTearDown;
|
||||
}
|
||||
|
||||
function RunNextTearDown() {
|
||||
try {
|
||||
suite.benchmarks[index++].TearDown();
|
||||
} catch (e) {
|
||||
suite.NotifyError(e);
|
||||
return null;
|
||||
}
|
||||
return RunNextSetup;
|
||||
}
|
||||
|
||||
// Start out running the setup.
|
||||
return RunNextSetup();
|
||||
}
|
||||
|
@ -4682,4 +4682,3 @@ function RunBenchmark(name, count, run, warn) {
|
||||
}
|
||||
|
||||
var BgL_runzd2benchmarkzd2 = RunBenchmark;
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,20 @@ the benchmark suite.
|
||||
|
||||
</p>
|
||||
|
||||
<div class="subtitle"><h3>Version 4 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v4/run.html">link</a>)</h3></div>
|
||||
|
||||
<p>The <i>Splay</i> benchmark is a newcomer in version 4. It
|
||||
manipulates a splay tree by adding and removing data nodes, thus
|
||||
exercising the memory management subsystem of the JavaScript engine.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Furthermore, all the unused parts of the Prototype library were
|
||||
removed from the RayTrace benchmark. This does not affect the running
|
||||
of the benchmark.
|
||||
</p>
|
||||
|
||||
|
||||
<div class="subtitle"><h3>Version 3 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v3/run.html">link</a>)</h3></div>
|
||||
|
||||
<p>Version 3 adds a new benchmark, <i>RegExp</i>. The RegExp
|
||||
@ -32,9 +46,10 @@ encoded using ROT13 in a way that does not affect how the regexps
|
||||
match their input.
|
||||
</p>
|
||||
|
||||
|
||||
<div class="subtitle"><h3>Version 2 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v2/run.html">link</a>)</h3></div>
|
||||
|
||||
<p>For version 2 the crypto benchmark was fixed. Previously, the
|
||||
<p>For version 2 the Crypto benchmark was fixed. Previously, the
|
||||
decryption stage was given plaintext as input, which resulted in an
|
||||
error. Now, the decryption stage is given the output of the
|
||||
encryption stage as input. The result is checked against the original
|
||||
@ -49,6 +64,7 @@ results of their calculations. This is to avoid accidentally
|
||||
obtaining scores that are the result of an incorrect JavaScript engine
|
||||
optimization.</p>
|
||||
|
||||
|
||||
<div class="subtitle"><h3>Version 1 (<a href="http://v8.googlecode.com/svn/data/benchmarks/v1/run.html">link</a>)</h3></div>
|
||||
|
||||
<p>Initial release.</p>
|
||||
|
@ -8,6 +8,7 @@
|
||||
<script type="text/javascript" src="raytrace.js"></script>
|
||||
<script type="text/javascript" src="earley-boyer.js"></script>
|
||||
<script type="text/javascript" src="regexp.js"></script>
|
||||
<script type="text/javascript" src="splay.js"></script>
|
||||
<link type="text/css" rel="stylesheet" href="style.css"></link>
|
||||
<script type="text/javascript">
|
||||
var completed = 0;
|
||||
@ -72,12 +73,13 @@ higher scores means better performance: <em>Bigger is better!</em>
|
||||
<ul>
|
||||
<li><b>Richards</b><br/>OS kernel simulation benchmark, originally written in BCPL by Martin Richards (<i>539 lines</i>).</li>
|
||||
<li><b>DeltaBlue</b><br/>One-way constraint solver, originally written in Smalltalk by John Maloney and Mario Wolczko (<i>880 lines</i>).</li>
|
||||
<li><b>Crypto</b><br/>Encryption and decryption benchmark based on code by Tom Wu (<i>1689 lines</i>).</li>
|
||||
<li><b>RayTrace</b><br/>Ray tracer benchmark based on code by <a href="http://flog.co.nz/">Adam Burmister</a> (<i>3418 lines</i>).</li>
|
||||
<li><b>EarleyBoyer</b><br/>Classic Scheme benchmarks, translated to JavaScript by Florian Loitsch's Scheme2Js compiler (<i>4682 lines</i>).</li>
|
||||
<li><b>Crypto</b><br/>Encryption and decryption benchmark based on code by Tom Wu (<i>1698 lines</i>).</li>
|
||||
<li><b>RayTrace</b><br/>Ray tracer benchmark based on code by <a href="http://flog.co.nz/">Adam Burmister</a> (<i>935 lines</i>).</li>
|
||||
<li><b>EarleyBoyer</b><br/>Classic Scheme benchmarks, translated to JavaScript by Florian Loitsch's Scheme2Js compiler (<i>4685 lines</i>).</li>
|
||||
<li><b>RegExp</b><br/>Regular expression benchmark generated by extracting regular expression operations from 50 of the most popular web pages
|
||||
(<i>4758 lines</i>).
|
||||
(<i>1614 lines</i>).
|
||||
</li>
|
||||
<li><b>Splay</b><br/>Data manipulation benchmark that deals with splay trees and exercises the automatic memory management subsystem (<i>378 lines</i>).</li>
|
||||
</ul>
|
||||
|
||||
<p>
|
||||
@ -90,7 +92,7 @@ the <a href="http://v8.googlecode.com/svn/data/benchmarks/current/revisions.html
|
||||
|
||||
</td><td style="text-align: center">
|
||||
<div class="run">
|
||||
<div id="status" style="text-align: center; margin-top: 60px; font-size: 120%; font-weight: bold;">Starting...</div>
|
||||
<div id="status" style="text-align: center; margin-top: 50px; font-size: 120%; font-weight: bold;">Starting...</div>
|
||||
<div style="text-align: left; margin: 30px 0 0 90px;" id="results">
|
||||
<div>
|
||||
</div>
|
||||
|
@ -33,6 +33,7 @@ load('crypto.js');
|
||||
load('raytrace.js');
|
||||
load('earley-boyer.js');
|
||||
load('regexp.js');
|
||||
load('splay.js');
|
||||
|
||||
var success = true;
|
||||
|
||||
|
378
benchmarks/splay.js
Normal file
378
benchmarks/splay.js
Normal file
@ -0,0 +1,378 @@
|
||||
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// This benchmark is based on a JavaScript log processing module used
|
||||
// by the V8 profiler to generate execution time profiles for runs of
|
||||
// JavaScript applications, and it effectively measures how fast the
|
||||
// JavaScript engine is at allocating nodes and reclaiming the memory
|
||||
// used for old nodes. Because of the way splay trees work, the engine
|
||||
// also has to deal with a lot of changes to the large tree object
|
||||
// graph.
|
||||
|
||||
var Splay = new BenchmarkSuite('Splay', 126125, [
|
||||
new Benchmark("Splay", SplayRun, SplaySetup, SplayTearDown)
|
||||
]);
|
||||
|
||||
|
||||
// Configuration.
|
||||
var kSplayTreeSize = 8000;
|
||||
var kSplayTreeModifications = 80;
|
||||
var kSplayTreePayloadDepth = 5;
|
||||
|
||||
var splayTree = null;
|
||||
|
||||
|
||||
function GeneratePayloadTree(depth, key) {
|
||||
if (depth == 0) {
|
||||
return {
|
||||
array : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
|
||||
string : 'String for key ' + key + ' in leaf node'
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
left: GeneratePayloadTree(depth - 1, key),
|
||||
right: GeneratePayloadTree(depth - 1, key)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function GenerateKey() {
|
||||
// The benchmark framework guarantees that Math.random is
|
||||
// deterministic; see base.js.
|
||||
return Math.random();
|
||||
}
|
||||
|
||||
|
||||
function InsertNewNode() {
|
||||
// Insert new node with a unique key.
|
||||
var key;
|
||||
do {
|
||||
key = GenerateKey();
|
||||
} while (splayTree.find(key) != null);
|
||||
splayTree.insert(key, GeneratePayloadTree(kSplayTreePayloadDepth, key));
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function SplaySetup() {
|
||||
splayTree = new SplayTree();
|
||||
for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode();
|
||||
}
|
||||
|
||||
|
||||
function SplayTearDown() {
|
||||
// Allow the garbage collector to reclaim the memory
|
||||
// used by the splay tree no matter how we exit the
|
||||
// tear down function.
|
||||
var keys = splayTree.exportKeys();
|
||||
splayTree = null;
|
||||
|
||||
// Verify that the splay tree has the right size.
|
||||
var length = keys.length;
|
||||
if (length != kSplayTreeSize) {
|
||||
throw new Error("Splay tree has wrong size");
|
||||
}
|
||||
|
||||
// Verify that the splay tree has sorted, unique keys.
|
||||
for (var i = 0; i < length - 1; i++) {
|
||||
if (keys[i] >= keys[i + 1]) {
|
||||
throw new Error("Splay tree not sorted");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function SplayRun() {
|
||||
// Replace a few nodes in the splay tree.
|
||||
for (var i = 0; i < kSplayTreeModifications; i++) {
|
||||
var key = InsertNewNode();
|
||||
var greatest = splayTree.findGreatestLessThan(key);
|
||||
if (greatest == null) splayTree.remove(key);
|
||||
else splayTree.remove(greatest.key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a Splay tree. A splay tree is a self-balancing binary
|
||||
* search tree with the additional property that recently accessed
|
||||
* elements are quick to access again. It performs basic operations
|
||||
* such as insertion, look-up and removal in O(log(n)) amortized time.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function SplayTree() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Pointer to the root node of the tree.
|
||||
*
|
||||
* @type {SplayTree.Node}
|
||||
* @private
|
||||
*/
|
||||
SplayTree.prototype.root_ = null;
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether the tree is empty.
|
||||
*/
|
||||
SplayTree.prototype.isEmpty = function() {
|
||||
return !this.root_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Inserts a node into the tree with the specified key and value if
|
||||
* the tree does not already contain a node with the specified key. If
|
||||
* the value is inserted, it becomes the root of the tree.
|
||||
*
|
||||
* @param {number} key Key to insert into the tree.
|
||||
* @param {*} value Value to insert into the tree.
|
||||
*/
|
||||
SplayTree.prototype.insert = function(key, value) {
|
||||
if (this.isEmpty()) {
|
||||
this.root_ = new SplayTree.Node(key, value);
|
||||
return;
|
||||
}
|
||||
// Splay on the key to move the last node on the search path for
|
||||
// the key to the root of the tree.
|
||||
this.splay_(key);
|
||||
if (this.root_.key == key) {
|
||||
return;
|
||||
}
|
||||
var node = new SplayTree.Node(key, value);
|
||||
if (key > this.root_.key) {
|
||||
node.left = this.root_;
|
||||
node.right = this.root_.right;
|
||||
this.root_.right = null;
|
||||
} else {
|
||||
node.right = this.root_;
|
||||
node.left = this.root_.left;
|
||||
this.root_.left = null;
|
||||
}
|
||||
this.root_ = node;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes a node with the specified key from the tree if the tree
|
||||
* contains a node with this key. The removed node is returned. If the
|
||||
* key is not found, an exception is thrown.
|
||||
*
|
||||
* @param {number} key Key to find and remove from the tree.
|
||||
* @return {SplayTree.Node} The removed node.
|
||||
*/
|
||||
SplayTree.prototype.remove = function(key) {
|
||||
if (this.isEmpty()) {
|
||||
throw Error('Key not found: ' + key);
|
||||
}
|
||||
this.splay_(key);
|
||||
if (this.root_.key != key) {
|
||||
throw Error('Key not found: ' + key);
|
||||
}
|
||||
var removed = this.root_;
|
||||
if (!this.root_.left) {
|
||||
this.root_ = this.root_.right;
|
||||
} else {
|
||||
var right = this.root_.right;
|
||||
this.root_ = this.root_.left;
|
||||
// Splay to make sure that the new root has an empty right child.
|
||||
this.splay_(key);
|
||||
// Insert the original right child as the right child of the new
|
||||
// root.
|
||||
this.root_.right = right;
|
||||
}
|
||||
return removed;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the node having the specified key or null if the tree doesn't contain
|
||||
* a node with the specified key.
|
||||
*
|
||||
* @param {number} key Key to find in the tree.
|
||||
* @return {SplayTree.Node} Node having the specified key.
|
||||
*/
|
||||
SplayTree.prototype.find = function(key) {
|
||||
if (this.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
this.splay_(key);
|
||||
return this.root_.key == key ? this.root_ : null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {SplayTree.Node} Node having the maximum key value that
|
||||
* is less or equal to the specified key value.
|
||||
*/
|
||||
SplayTree.prototype.findGreatestLessThan = function(key) {
|
||||
if (this.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
// Splay on the key to move the node with the given key or the last
|
||||
// node on the search path to the top of the tree.
|
||||
this.splay_(key);
|
||||
// Now the result is either the root node or the greatest node in
|
||||
// the left subtree.
|
||||
if (this.root_.key <= key) {
|
||||
return this.root_;
|
||||
} else if (this.root_.left) {
|
||||
return this.findMax(this.root_.left);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {Array<*>} An array containing all the keys of tree's nodes.
|
||||
*/
|
||||
SplayTree.prototype.exportKeys = function() {
|
||||
var result = [];
|
||||
if (!this.isEmpty()) {
|
||||
this.root_.traverse_(function(node) { result.push(node.key); });
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Perform the splay operation for the given key. Moves the node with
|
||||
* the given key to the top of the tree. If no node has the given
|
||||
* key, the last node on the search path is moved to the top of the
|
||||
* tree. This is the simplified top-down splaying algorithm from:
|
||||
* "Self-adjusting Binary Search Trees" by Sleator and Tarjan
|
||||
*
|
||||
* @param {number} key Key to splay the tree on.
|
||||
* @private
|
||||
*/
|
||||
SplayTree.prototype.splay_ = function(key) {
|
||||
if (this.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
// Create a dummy node. The use of the dummy node is a bit
|
||||
// counter-intuitive: The right child of the dummy node will hold
|
||||
// the L tree of the algorithm. The left child of the dummy node
|
||||
// will hold the R tree of the algorithm. Using a dummy node, left
|
||||
// and right will always be nodes and we avoid special cases.
|
||||
var dummy, left, right;
|
||||
dummy = left = right = new SplayTree.Node(null, null);
|
||||
var current = this.root_;
|
||||
while (true) {
|
||||
if (key < current.key) {
|
||||
if (!current.left) {
|
||||
break;
|
||||
}
|
||||
if (key < current.left.key) {
|
||||
// Rotate right.
|
||||
var tmp = current.left;
|
||||
current.left = tmp.right;
|
||||
tmp.right = current;
|
||||
current = tmp;
|
||||
if (!current.left) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Link right.
|
||||
right.left = current;
|
||||
right = current;
|
||||
current = current.left;
|
||||
} else if (key > current.key) {
|
||||
if (!current.right) {
|
||||
break;
|
||||
}
|
||||
if (key > current.right.key) {
|
||||
// Rotate left.
|
||||
var tmp = current.right;
|
||||
current.right = tmp.left;
|
||||
tmp.left = current;
|
||||
current = tmp;
|
||||
if (!current.right) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Link left.
|
||||
left.right = current;
|
||||
left = current;
|
||||
current = current.right;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Assemble.
|
||||
left.right = current.left;
|
||||
right.left = current.right;
|
||||
current.left = dummy.right;
|
||||
current.right = dummy.left;
|
||||
this.root_ = current;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a Splay tree node.
|
||||
*
|
||||
* @param {number} key Key.
|
||||
* @param {*} value Value.
|
||||
*/
|
||||
SplayTree.Node = function(key, value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @type {SplayTree.Node}
|
||||
*/
|
||||
SplayTree.Node.prototype.left = null;
|
||||
|
||||
|
||||
/**
|
||||
* @type {SplayTree.Node}
|
||||
*/
|
||||
SplayTree.Node.prototype.right = null;
|
||||
|
||||
|
||||
/**
|
||||
* Performs an ordered traversal of the subtree starting at
|
||||
* this SplayTree.Node.
|
||||
*
|
||||
* @param {function(SplayTree.Node)} f Visitor function.
|
||||
* @private
|
||||
*/
|
||||
SplayTree.Node.prototype.traverse_ = function(f) {
|
||||
var current = this;
|
||||
while (current) {
|
||||
var left = current.left;
|
||||
if (left) left.traverse_(f);
|
||||
f(current);
|
||||
current = current.right;
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue
Block a user