1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/js/src/v8/splay.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,394 @@ 1.4 +// Copyright 2009 the V8 project authors. All rights reserved. 1.5 +// Redistribution and use in source and binary forms, with or without 1.6 +// modification, are permitted provided that the following conditions are 1.7 +// met: 1.8 +// 1.9 +// * Redistributions of source code must retain the above copyright 1.10 +// notice, this list of conditions and the following disclaimer. 1.11 +// * Redistributions in binary form must reproduce the above 1.12 +// copyright notice, this list of conditions and the following 1.13 +// disclaimer in the documentation and/or other materials provided 1.14 +// with the distribution. 1.15 +// * Neither the name of Google Inc. nor the names of its 1.16 +// contributors may be used to endorse or promote products derived 1.17 +// from this software without specific prior written permission. 1.18 +// 1.19 +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1.20 +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1.21 +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1.22 +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1.23 +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1.24 +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1.25 +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1.26 +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 1.27 +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 1.28 +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 1.29 +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 1.30 + 1.31 +// This benchmark is based on a JavaScript log processing module used 1.32 +// by the V8 profiler to generate execution time profiles for runs of 1.33 +// JavaScript applications, and it effectively measures how fast the 1.34 +// JavaScript engine is at allocating nodes and reclaiming the memory 1.35 +// used for old nodes. Because of the way splay trees work, the engine 1.36 +// also has to deal with a lot of changes to the large tree object 1.37 +// graph. 1.38 + 1.39 +var Splay = new BenchmarkSuite('Splay', 81491, [ 1.40 + new Benchmark("Splay", SplayRun, SplaySetup, SplayTearDown) 1.41 +]); 1.42 + 1.43 + 1.44 +// Configuration. 1.45 +var kSplayTreeSize = 8000; 1.46 +var kSplayTreeModifications = 80; 1.47 +var kSplayTreePayloadDepth = 5; 1.48 + 1.49 +var splayTree = null; 1.50 + 1.51 + 1.52 +function GeneratePayloadTree(depth, tag) { 1.53 + if (depth == 0) { 1.54 + return { 1.55 + array : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ], 1.56 + string : 'String for key ' + tag + ' in leaf node' 1.57 + }; 1.58 + } else { 1.59 + return { 1.60 + left: GeneratePayloadTree(depth - 1, tag), 1.61 + right: GeneratePayloadTree(depth - 1, tag) 1.62 + }; 1.63 + } 1.64 +} 1.65 + 1.66 + 1.67 +function GenerateKey() { 1.68 + // The benchmark framework guarantees that Math.random is 1.69 + // deterministic; see base.js. 1.70 + return Math.random(); 1.71 +} 1.72 + 1.73 + 1.74 +function InsertNewNode() { 1.75 + // Insert new node with a unique key. 1.76 + var key; 1.77 + do { 1.78 + key = GenerateKey(); 1.79 + } while (splayTree.find(key) != null); 1.80 + var payload = GeneratePayloadTree(kSplayTreePayloadDepth, String(key)); 1.81 + splayTree.insert(key, payload); 1.82 + return key; 1.83 +} 1.84 + 1.85 + 1.86 + 1.87 +function SplaySetup() { 1.88 + splayTree = new SplayTree(); 1.89 + for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode(); 1.90 +} 1.91 + 1.92 + 1.93 +function SplayTearDown() { 1.94 + // Allow the garbage collector to reclaim the memory 1.95 + // used by the splay tree no matter how we exit the 1.96 + // tear down function. 1.97 + var keys = splayTree.exportKeys(); 1.98 + splayTree = null; 1.99 + 1.100 + // Verify that the splay tree has the right size. 1.101 + var length = keys.length; 1.102 + if (length != kSplayTreeSize) { 1.103 + throw new Error("Splay tree has wrong size"); 1.104 + } 1.105 + 1.106 + // Verify that the splay tree has sorted, unique keys. 1.107 + for (var i = 0; i < length - 1; i++) { 1.108 + if (keys[i] >= keys[i + 1]) { 1.109 + throw new Error("Splay tree not sorted"); 1.110 + } 1.111 + } 1.112 +} 1.113 + 1.114 + 1.115 +function SplayRun() { 1.116 + // Replace a few nodes in the splay tree. 1.117 + for (var i = 0; i < kSplayTreeModifications; i++) { 1.118 + var key = InsertNewNode(); 1.119 + var greatest = splayTree.findGreatestLessThan(key); 1.120 + if (greatest == null) splayTree.remove(key); 1.121 + else splayTree.remove(greatest.key); 1.122 + } 1.123 +} 1.124 + 1.125 + 1.126 +/** 1.127 + * Constructs a Splay tree. A splay tree is a self-balancing binary 1.128 + * search tree with the additional property that recently accessed 1.129 + * elements are quick to access again. It performs basic operations 1.130 + * such as insertion, look-up and removal in O(log(n)) amortized time. 1.131 + * 1.132 + * @constructor 1.133 + */ 1.134 +function SplayTree() { 1.135 +}; 1.136 + 1.137 + 1.138 +/** 1.139 + * Pointer to the root node of the tree. 1.140 + * 1.141 + * @type {SplayTree.Node} 1.142 + * @private 1.143 + */ 1.144 +SplayTree.prototype.root_ = null; 1.145 + 1.146 + 1.147 +/** 1.148 + * @return {boolean} Whether the tree is empty. 1.149 + */ 1.150 +SplayTree.prototype.isEmpty = function() { 1.151 + return !this.root_; 1.152 +}; 1.153 + 1.154 + 1.155 +/** 1.156 + * Inserts a node into the tree with the specified key and value if 1.157 + * the tree does not already contain a node with the specified key. If 1.158 + * the value is inserted, it becomes the root of the tree. 1.159 + * 1.160 + * @param {number} key Key to insert into the tree. 1.161 + * @param {*} value Value to insert into the tree. 1.162 + */ 1.163 +SplayTree.prototype.insert = function(key, value) { 1.164 + if (this.isEmpty()) { 1.165 + this.root_ = new SplayTree.Node(key, value); 1.166 + return; 1.167 + } 1.168 + // Splay on the key to move the last node on the search path for 1.169 + // the key to the root of the tree. 1.170 + this.splay_(key); 1.171 + if (this.root_.key == key) { 1.172 + return; 1.173 + } 1.174 + var node = new SplayTree.Node(key, value); 1.175 + if (key > this.root_.key) { 1.176 + node.left = this.root_; 1.177 + node.right = this.root_.right; 1.178 + this.root_.right = null; 1.179 + } else { 1.180 + node.right = this.root_; 1.181 + node.left = this.root_.left; 1.182 + this.root_.left = null; 1.183 + } 1.184 + this.root_ = node; 1.185 +}; 1.186 + 1.187 + 1.188 +/** 1.189 + * Removes a node with the specified key from the tree if the tree 1.190 + * contains a node with this key. The removed node is returned. If the 1.191 + * key is not found, an exception is thrown. 1.192 + * 1.193 + * @param {number} key Key to find and remove from the tree. 1.194 + * @return {SplayTree.Node} The removed node. 1.195 + */ 1.196 +SplayTree.prototype.remove = function(key) { 1.197 + if (this.isEmpty()) { 1.198 + throw Error('Key not found: ' + key); 1.199 + } 1.200 + this.splay_(key); 1.201 + if (this.root_.key != key) { 1.202 + throw Error('Key not found: ' + key); 1.203 + } 1.204 + var removed = this.root_; 1.205 + if (!this.root_.left) { 1.206 + this.root_ = this.root_.right; 1.207 + } else { 1.208 + var right = this.root_.right; 1.209 + this.root_ = this.root_.left; 1.210 + // Splay to make sure that the new root has an empty right child. 1.211 + this.splay_(key); 1.212 + // Insert the original right child as the right child of the new 1.213 + // root. 1.214 + this.root_.right = right; 1.215 + } 1.216 + return removed; 1.217 +}; 1.218 + 1.219 + 1.220 +/** 1.221 + * Returns the node having the specified key or null if the tree doesn't contain 1.222 + * a node with the specified key. 1.223 + * 1.224 + * @param {number} key Key to find in the tree. 1.225 + * @return {SplayTree.Node} Node having the specified key. 1.226 + */ 1.227 +SplayTree.prototype.find = function(key) { 1.228 + if (this.isEmpty()) { 1.229 + return null; 1.230 + } 1.231 + this.splay_(key); 1.232 + return this.root_.key == key ? this.root_ : null; 1.233 +}; 1.234 + 1.235 + 1.236 +/** 1.237 + * @return {SplayTree.Node} Node having the maximum key value. 1.238 + */ 1.239 +SplayTree.prototype.findMax = function(opt_startNode) { 1.240 + if (this.isEmpty()) { 1.241 + return null; 1.242 + } 1.243 + var current = opt_startNode || this.root_; 1.244 + while (current.right) { 1.245 + current = current.right; 1.246 + } 1.247 + return current; 1.248 +}; 1.249 + 1.250 + 1.251 +/** 1.252 + * @return {SplayTree.Node} Node having the maximum key value that 1.253 + * is less than the specified key value. 1.254 + */ 1.255 +SplayTree.prototype.findGreatestLessThan = function(key) { 1.256 + if (this.isEmpty()) { 1.257 + return null; 1.258 + } 1.259 + // Splay on the key to move the node with the given key or the last 1.260 + // node on the search path to the top of the tree. 1.261 + this.splay_(key); 1.262 + // Now the result is either the root node or the greatest node in 1.263 + // the left subtree. 1.264 + if (this.root_.key < key) { 1.265 + return this.root_; 1.266 + } else if (this.root_.left) { 1.267 + return this.findMax(this.root_.left); 1.268 + } else { 1.269 + return null; 1.270 + } 1.271 +}; 1.272 + 1.273 + 1.274 +/** 1.275 + * @return {Array<*>} An array containing all the keys of tree's nodes. 1.276 + */ 1.277 +SplayTree.prototype.exportKeys = function() { 1.278 + var result = []; 1.279 + if (!this.isEmpty()) { 1.280 + this.root_.traverse_(function(node) { result.push(node.key); }); 1.281 + } 1.282 + return result; 1.283 +}; 1.284 + 1.285 + 1.286 +/** 1.287 + * Perform the splay operation for the given key. Moves the node with 1.288 + * the given key to the top of the tree. If no node has the given 1.289 + * key, the last node on the search path is moved to the top of the 1.290 + * tree. This is the simplified top-down splaying algorithm from: 1.291 + * "Self-adjusting Binary Search Trees" by Sleator and Tarjan 1.292 + * 1.293 + * @param {number} key Key to splay the tree on. 1.294 + * @private 1.295 + */ 1.296 +SplayTree.prototype.splay_ = function(key) { 1.297 + if (this.isEmpty()) { 1.298 + return; 1.299 + } 1.300 + // Create a dummy node. The use of the dummy node is a bit 1.301 + // counter-intuitive: The right child of the dummy node will hold 1.302 + // the L tree of the algorithm. The left child of the dummy node 1.303 + // will hold the R tree of the algorithm. Using a dummy node, left 1.304 + // and right will always be nodes and we avoid special cases. 1.305 + var dummy, left, right; 1.306 + dummy = left = right = new SplayTree.Node(null, null); 1.307 + var current = this.root_; 1.308 + while (true) { 1.309 + if (key < current.key) { 1.310 + if (!current.left) { 1.311 + break; 1.312 + } 1.313 + if (key < current.left.key) { 1.314 + // Rotate right. 1.315 + var tmp = current.left; 1.316 + current.left = tmp.right; 1.317 + tmp.right = current; 1.318 + current = tmp; 1.319 + if (!current.left) { 1.320 + break; 1.321 + } 1.322 + } 1.323 + // Link right. 1.324 + right.left = current; 1.325 + right = current; 1.326 + current = current.left; 1.327 + } else if (key > current.key) { 1.328 + if (!current.right) { 1.329 + break; 1.330 + } 1.331 + if (key > current.right.key) { 1.332 + // Rotate left. 1.333 + var tmp = current.right; 1.334 + current.right = tmp.left; 1.335 + tmp.left = current; 1.336 + current = tmp; 1.337 + if (!current.right) { 1.338 + break; 1.339 + } 1.340 + } 1.341 + // Link left. 1.342 + left.right = current; 1.343 + left = current; 1.344 + current = current.right; 1.345 + } else { 1.346 + break; 1.347 + } 1.348 + } 1.349 + // Assemble. 1.350 + left.right = current.left; 1.351 + right.left = current.right; 1.352 + current.left = dummy.right; 1.353 + current.right = dummy.left; 1.354 + this.root_ = current; 1.355 +}; 1.356 + 1.357 + 1.358 +/** 1.359 + * Constructs a Splay tree node. 1.360 + * 1.361 + * @param {number} key Key. 1.362 + * @param {*} value Value. 1.363 + */ 1.364 +SplayTree.Node = function(key, value) { 1.365 + this.key = key; 1.366 + this.value = value; 1.367 +}; 1.368 + 1.369 + 1.370 +/** 1.371 + * @type {SplayTree.Node} 1.372 + */ 1.373 +SplayTree.Node.prototype.left = null; 1.374 + 1.375 + 1.376 +/** 1.377 + * @type {SplayTree.Node} 1.378 + */ 1.379 +SplayTree.Node.prototype.right = null; 1.380 + 1.381 + 1.382 +/** 1.383 + * Performs an ordered traversal of the subtree starting at 1.384 + * this SplayTree.Node. 1.385 + * 1.386 + * @param {function(SplayTree.Node)} f Visitor function. 1.387 + * @private 1.388 + */ 1.389 +SplayTree.Node.prototype.traverse_ = function(f) { 1.390 + var current = this; 1.391 + while (current) { 1.392 + var left = current.left; 1.393 + if (left) left.traverse_(f); 1.394 + f(current); 1.395 + current = current.right; 1.396 + } 1.397 +};