PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/hphp/benchmarks/php-octane/splay.php

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