/js/src/v8/splay.js

http://github.com/zpao/v8monkey · JavaScript · 394 lines · 205 code · 48 blank · 141 comment · 50 complexity · c41101296f97c2700de629b2eb1d139a MD5 · raw file

  1. // Copyright 2009 the V8 project authors. All rights reserved.
  2. // Redistribution and use in source and binary forms, with or without
  3. // modification, are permitted provided that the following conditions are
  4. // met:
  5. //
  6. // * Redistributions of source code must retain the above copyright
  7. // notice, this list of conditions and the following disclaimer.
  8. // * Redistributions in binary form must reproduce the above
  9. // copyright notice, this list of conditions and the following
  10. // disclaimer in the documentation and/or other materials provided
  11. // with the distribution.
  12. // * Neither the name of Google Inc. nor the names of its
  13. // contributors may be used to endorse or promote products derived
  14. // from this software without specific prior written permission.
  15. //
  16. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  17. // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  18. // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  19. // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  20. // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  21. // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  22. // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23. // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24. // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  26. // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. // This benchmark is based on a JavaScript log processing module used
  28. // by the V8 profiler to generate execution time profiles for runs of
  29. // JavaScript applications, and it effectively measures how fast the
  30. // JavaScript engine is at allocating nodes and reclaiming the memory
  31. // used for old nodes. Because of the way splay trees work, the engine
  32. // also has to deal with a lot of changes to the large tree object
  33. // graph.
  34. var Splay = new BenchmarkSuite('Splay', 81491, [
  35. new Benchmark("Splay", SplayRun, SplaySetup, SplayTearDown)
  36. ]);
  37. // Configuration.
  38. var kSplayTreeSize = 8000;
  39. var kSplayTreeModifications = 80;
  40. var kSplayTreePayloadDepth = 5;
  41. var splayTree = null;
  42. function GeneratePayloadTree(depth, tag) {
  43. if (depth == 0) {
  44. return {
  45. array : [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ],
  46. string : 'String for key ' + tag + ' in leaf node'
  47. };
  48. } else {
  49. return {
  50. left: GeneratePayloadTree(depth - 1, tag),
  51. right: GeneratePayloadTree(depth - 1, tag)
  52. };
  53. }
  54. }
  55. function GenerateKey() {
  56. // The benchmark framework guarantees that Math.random is
  57. // deterministic; see base.js.
  58. return Math.random();
  59. }
  60. function InsertNewNode() {
  61. // Insert new node with a unique key.
  62. var key;
  63. do {
  64. key = GenerateKey();
  65. } while (splayTree.find(key) != null);
  66. var payload = GeneratePayloadTree(kSplayTreePayloadDepth, String(key));
  67. splayTree.insert(key, payload);
  68. return key;
  69. }
  70. function SplaySetup() {
  71. splayTree = new SplayTree();
  72. for (var i = 0; i < kSplayTreeSize; i++) InsertNewNode();
  73. }
  74. function SplayTearDown() {
  75. // Allow the garbage collector to reclaim the memory
  76. // used by the splay tree no matter how we exit the
  77. // tear down function.
  78. var keys = splayTree.exportKeys();
  79. splayTree = null;
  80. // Verify that the splay tree has the right size.
  81. var length = keys.length;
  82. if (length != kSplayTreeSize) {
  83. throw new Error("Splay tree has wrong size");
  84. }
  85. // Verify that the splay tree has sorted, unique keys.
  86. for (var i = 0; i < length - 1; i++) {
  87. if (keys[i] >= keys[i + 1]) {
  88. throw new Error("Splay tree not sorted");
  89. }
  90. }
  91. }
  92. function SplayRun() {
  93. // Replace a few nodes in the splay tree.
  94. for (var i = 0; i < kSplayTreeModifications; i++) {
  95. var key = InsertNewNode();
  96. var greatest = splayTree.findGreatestLessThan(key);
  97. if (greatest == null) splayTree.remove(key);
  98. else splayTree.remove(greatest.key);
  99. }
  100. }
  101. /**
  102. * Constructs a Splay tree. A splay tree is a self-balancing binary
  103. * search tree with the additional property that recently accessed
  104. * elements are quick to access again. It performs basic operations
  105. * such as insertion, look-up and removal in O(log(n)) amortized time.
  106. *
  107. * @constructor
  108. */
  109. function SplayTree() {
  110. };
  111. /**
  112. * Pointer to the root node of the tree.
  113. *
  114. * @type {SplayTree.Node}
  115. * @private
  116. */
  117. SplayTree.prototype.root_ = null;
  118. /**
  119. * @return {boolean} Whether the tree is empty.
  120. */
  121. SplayTree.prototype.isEmpty = function() {
  122. return !this.root_;
  123. };
  124. /**
  125. * Inserts a node into the tree with the specified key and value if
  126. * the tree does not already contain a node with the specified key. If
  127. * the value is inserted, it becomes the root of the tree.
  128. *
  129. * @param {number} key Key to insert into the tree.
  130. * @param {*} value Value to insert into the tree.
  131. */
  132. SplayTree.prototype.insert = function(key, value) {
  133. if (this.isEmpty()) {
  134. this.root_ = new SplayTree.Node(key, value);
  135. return;
  136. }
  137. // Splay on the key to move the last node on the search path for
  138. // the key to the root of the tree.
  139. this.splay_(key);
  140. if (this.root_.key == key) {
  141. return;
  142. }
  143. var node = new SplayTree.Node(key, value);
  144. if (key > this.root_.key) {
  145. node.left = this.root_;
  146. node.right = this.root_.right;
  147. this.root_.right = null;
  148. } else {
  149. node.right = this.root_;
  150. node.left = this.root_.left;
  151. this.root_.left = null;
  152. }
  153. this.root_ = node;
  154. };
  155. /**
  156. * Removes a node with the specified key from the tree if the tree
  157. * contains a node with this key. The removed node is returned. If the
  158. * key is not found, an exception is thrown.
  159. *
  160. * @param {number} key Key to find and remove from the tree.
  161. * @return {SplayTree.Node} The removed node.
  162. */
  163. SplayTree.prototype.remove = function(key) {
  164. if (this.isEmpty()) {
  165. throw Error('Key not found: ' + key);
  166. }
  167. this.splay_(key);
  168. if (this.root_.key != key) {
  169. throw Error('Key not found: ' + key);
  170. }
  171. var removed = this.root_;
  172. if (!this.root_.left) {
  173. this.root_ = this.root_.right;
  174. } else {
  175. var right = this.root_.right;
  176. this.root_ = this.root_.left;
  177. // Splay to make sure that the new root has an empty right child.
  178. this.splay_(key);
  179. // Insert the original right child as the right child of the new
  180. // root.
  181. this.root_.right = right;
  182. }
  183. return removed;
  184. };
  185. /**
  186. * Returns the node having the specified key or null if the tree doesn't contain
  187. * a node with the specified key.
  188. *
  189. * @param {number} key Key to find in the tree.
  190. * @return {SplayTree.Node} Node having the specified key.
  191. */
  192. SplayTree.prototype.find = function(key) {
  193. if (this.isEmpty()) {
  194. return null;
  195. }
  196. this.splay_(key);
  197. return this.root_.key == key ? this.root_ : null;
  198. };
  199. /**
  200. * @return {SplayTree.Node} Node having the maximum key value.
  201. */
  202. SplayTree.prototype.findMax = function(opt_startNode) {
  203. if (this.isEmpty()) {
  204. return null;
  205. }
  206. var current = opt_startNode || this.root_;
  207. while (current.right) {
  208. current = current.right;
  209. }
  210. return current;
  211. };
  212. /**
  213. * @return {SplayTree.Node} Node having the maximum key value that
  214. * is less than the specified key value.
  215. */
  216. SplayTree.prototype.findGreatestLessThan = function(key) {
  217. if (this.isEmpty()) {
  218. return null;
  219. }
  220. // Splay on the key to move the node with the given key or the last
  221. // node on the search path to the top of the tree.
  222. this.splay_(key);
  223. // Now the result is either the root node or the greatest node in
  224. // the left subtree.
  225. if (this.root_.key < key) {
  226. return this.root_;
  227. } else if (this.root_.left) {
  228. return this.findMax(this.root_.left);
  229. } else {
  230. return null;
  231. }
  232. };
  233. /**
  234. * @return {Array<*>} An array containing all the keys of tree's nodes.
  235. */
  236. SplayTree.prototype.exportKeys = function() {
  237. var result = [];
  238. if (!this.isEmpty()) {
  239. this.root_.traverse_(function(node) { result.push(node.key); });
  240. }
  241. return result;
  242. };
  243. /**
  244. * Perform the splay operation for the given key. Moves the node with
  245. * the given key to the top of the tree. If no node has the given
  246. * key, the last node on the search path is moved to the top of the
  247. * tree. This is the simplified top-down splaying algorithm from:
  248. * "Self-adjusting Binary Search Trees" by Sleator and Tarjan
  249. *
  250. * @param {number} key Key to splay the tree on.
  251. * @private
  252. */
  253. SplayTree.prototype.splay_ = function(key) {
  254. if (this.isEmpty()) {
  255. return;
  256. }
  257. // Create a dummy node. The use of the dummy node is a bit
  258. // counter-intuitive: The right child of the dummy node will hold
  259. // the L tree of the algorithm. The left child of the dummy node
  260. // will hold the R tree of the algorithm. Using a dummy node, left
  261. // and right will always be nodes and we avoid special cases.
  262. var dummy, left, right;
  263. dummy = left = right = new SplayTree.Node(null, null);
  264. var current = this.root_;
  265. while (true) {
  266. if (key < current.key) {
  267. if (!current.left) {
  268. break;
  269. }
  270. if (key < current.left.key) {
  271. // Rotate right.
  272. var tmp = current.left;
  273. current.left = tmp.right;
  274. tmp.right = current;
  275. current = tmp;
  276. if (!current.left) {
  277. break;
  278. }
  279. }
  280. // Link right.
  281. right.left = current;
  282. right = current;
  283. current = current.left;
  284. } else if (key > current.key) {
  285. if (!current.right) {
  286. break;
  287. }
  288. if (key > current.right.key) {
  289. // Rotate left.
  290. var tmp = current.right;
  291. current.right = tmp.left;
  292. tmp.left = current;
  293. current = tmp;
  294. if (!current.right) {
  295. break;
  296. }
  297. }
  298. // Link left.
  299. left.right = current;
  300. left = current;
  301. current = current.right;
  302. } else {
  303. break;
  304. }
  305. }
  306. // Assemble.
  307. left.right = current.left;
  308. right.left = current.right;
  309. current.left = dummy.right;
  310. current.right = dummy.left;
  311. this.root_ = current;
  312. };
  313. /**
  314. * Constructs a Splay tree node.
  315. *
  316. * @param {number} key Key.
  317. * @param {*} value Value.
  318. */
  319. SplayTree.Node = function(key, value) {
  320. this.key = key;
  321. this.value = value;
  322. };
  323. /**
  324. * @type {SplayTree.Node}
  325. */
  326. SplayTree.Node.prototype.left = null;
  327. /**
  328. * @type {SplayTree.Node}
  329. */
  330. SplayTree.Node.prototype.right = null;
  331. /**
  332. * Performs an ordered traversal of the subtree starting at
  333. * this SplayTree.Node.
  334. *
  335. * @param {function(SplayTree.Node)} f Visitor function.
  336. * @private
  337. */
  338. SplayTree.Node.prototype.traverse_ = function(f) {
  339. var current = this;
  340. while (current) {
  341. var left = current.left;
  342. if (left) left.traverse_(f);
  343. f(current);
  344. current = current.right;
  345. }
  346. };