js/src/v8/splay.js

changeset 0
6474c204b198
     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 +};

mercurial