Skip to content

Commit 275bc63

Browse files
rmacnak-googlecommit-bot@chromium.org
authored andcommitted
[vm, gc] Add Splay as a test of various GC modes and verification tools.
Bug: #40780 Change-Id: I2b760c4e2884f400e1dcfbd77f21fa9c4d96f25c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/137645 Reviewed-by: Alexander Markov <[email protected]> Commit-Queue: Ryan Macnak <[email protected]>
1 parent 501af5a commit 275bc63

File tree

1 file changed

+319
-0
lines changed

1 file changed

+319
-0
lines changed

runtime/tests/vm/dart/splay_test.dart

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// This benchmark is based on a JavaScript log processing module used
6+
// by the V8 profiler to generate execution time profiles for runs of
7+
// JavaScript applications, and it effectively measures how fast the
8+
// JavaScript engine is at allocating nodes and reclaiming the memory
9+
// used for old nodes. Because of the way splay trees work, the engine
10+
// also has to deal with a lot of changes to the large tree object
11+
// graph.
12+
13+
// VMOptions=
14+
// VMOptions=--no_concurrent_mark --no_concurrent_sweep
15+
// VMOptions=--no_concurrent_mark --concurrent_sweep
16+
// VMOptions=--no_concurrent_mark --use_compactor
17+
// VMOptions=--no_concurrent_mark --use_compactor --force_evacuation
18+
// VMOptions=--concurrent_mark --no_concurrent_sweep
19+
// VMOptions=--concurrent_mark --concurrent_sweep
20+
// VMOptions=--concurrent_mark --use_compactor
21+
// VMOptions=--concurrent_mark --use_compactor --force_evacuation
22+
// VMOptions=--verify_before_gc
23+
// VMOptions=--verify_after_gc
24+
// VMOptions=--verify_before_gc --verify_after_gc
25+
// VMOptions=--verify_store_buffer
26+
27+
import "dart:math";
28+
import 'package:benchmark_harness/benchmark_harness.dart';
29+
30+
void main() {
31+
Splay.main();
32+
}
33+
34+
class Splay extends BenchmarkBase {
35+
const Splay() : super("Splay");
36+
37+
// Configuration.
38+
static final int kTreeSize = 8000;
39+
static final int kTreeModifications = 80;
40+
static final int kTreePayloadDepth = 5;
41+
42+
static SplayTree tree;
43+
44+
static Random rnd = new Random(12345);
45+
46+
// Insert new node with a unique key.
47+
static num insertNewNode() {
48+
num key;
49+
do {
50+
key = rnd.nextDouble();
51+
} while (tree.find(key) != null);
52+
Payload payload = Payload.generate(kTreePayloadDepth, key.toString());
53+
tree.insert(key, payload);
54+
return key;
55+
}
56+
57+
static void mysetup() {
58+
tree = new SplayTree();
59+
for (int i = 0; i < kTreeSize; i++) insertNewNode();
60+
}
61+
62+
static void tearDown() {
63+
// Allow the garbage collector to reclaim the memory
64+
// used by the splay tree no matter how we exit the
65+
// tear down function.
66+
List<num> keys = tree.exportKeys();
67+
tree = null;
68+
69+
// Verify that the splay tree has the right size.
70+
int length = keys.length;
71+
if (length != kTreeSize) throw new Error("Splay tree has wrong size");
72+
73+
// Verify that the splay tree has sorted, unique keys.
74+
for (int i = 0; i < length - 1; i++) {
75+
if (keys[i] >= keys[i + 1]) throw new Error("Splay tree not sorted");
76+
}
77+
}
78+
79+
void warmup() {
80+
exercise();
81+
}
82+
83+
void exercise() {
84+
// Replace a few nodes in the splay tree.
85+
for (int i = 0; i < kTreeModifications; i++) {
86+
num key = insertNewNode();
87+
Node greatest = tree.findGreatestLessThan(key);
88+
if (greatest == null) tree.remove(key);
89+
else tree.remove(greatest.key);
90+
}
91+
}
92+
93+
static void main() {
94+
mysetup();
95+
new Splay().report();
96+
tearDown();
97+
}
98+
}
99+
100+
101+
class Leaf {
102+
Leaf(String tag) {
103+
string = "String for key $tag in leaf node";
104+
array = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
105+
}
106+
String string;
107+
List<num> array;
108+
}
109+
110+
111+
class Payload {
112+
Payload(this.left, this.right);
113+
var left, right;
114+
115+
static generate(depth, tag) {
116+
if (depth == 0) return new Leaf(tag);
117+
return new Payload(generate(depth - 1, tag),
118+
generate(depth - 1, tag));
119+
}
120+
}
121+
122+
class Error implements Exception {
123+
const Error(this.message);
124+
final String message;
125+
}
126+
127+
128+
/**
129+
* A splay tree is a self-balancing binary search tree with the additional
130+
* property that recently accessed elements are quick to access again.
131+
* It performs basic operations such as insertion, look-up and removal
132+
* in O(log(n)) amortized time.
133+
*/
134+
class SplayTree {
135+
SplayTree();
136+
137+
/**
138+
* Inserts a node into the tree with the specified [key] and value if
139+
* the tree does not already contain a node with the specified key. If
140+
* the value is inserted, it becomes the root of the tree.
141+
*/
142+
void insert(num key, value) {
143+
if (isEmpty) {
144+
root = new Node(key, value);
145+
return;
146+
}
147+
// Splay on the key to move the last node on the search path for
148+
// the key to the root of the tree.
149+
splay(key);
150+
if (root.key == key) return;
151+
Node node = new Node(key, value);
152+
if (key > root.key) {
153+
node.left = root;
154+
node.right = root.right;
155+
root.right = null;
156+
} else {
157+
node.right = root;
158+
node.left = root.left;
159+
root.left = null;
160+
}
161+
root = node;
162+
}
163+
164+
/**
165+
* Removes a node with the specified key from the tree if the tree
166+
* contains a node with this key. The removed node is returned. If
167+
* [key] is not found, an exception is thrown.
168+
*/
169+
Node remove(num key) {
170+
if (isEmpty) throw new Error('Key not found: $key');
171+
splay(key);
172+
if (root.key != key) throw new Error('Key not found: $key');
173+
Node removed = root;
174+
if (root.left == null) {
175+
root = root.right;
176+
} else {
177+
Node right = root.right;
178+
root = root.left;
179+
// Splay to make sure that the new root has an empty right child.
180+
splay(key);
181+
// Insert the original right child as the right child of the new
182+
// root.
183+
root.right = right;
184+
}
185+
return removed;
186+
}
187+
188+
/**
189+
* Returns the node having the specified [key] or null if the tree doesn't
190+
* contain a node with the specified [key].
191+
*/
192+
Node find(num key) {
193+
if (isEmpty) return null;
194+
splay(key);
195+
return root.key == key ? root : null;
196+
}
197+
198+
/**
199+
* Returns the Node having the maximum key value.
200+
*/
201+
Node findMax([Node start]) {
202+
if (isEmpty) return null;
203+
Node current = null == start ? root : start;
204+
while (current.right != null) current = current.right;
205+
return current;
206+
}
207+
208+
/**
209+
* Returns the Node having the maximum key value that
210+
* is less than the specified [key].
211+
*/
212+
Node findGreatestLessThan(num key) {
213+
if (isEmpty) return null;
214+
// Splay on the key to move the node with the given key or the last
215+
// node on the search path to the top of the tree.
216+
splay(key);
217+
// Now the result is either the root node or the greatest node in
218+
// the left subtree.
219+
if (root.key < key) return root;
220+
if (root.left != null) return findMax(root.left);
221+
return null;
222+
}
223+
224+
/**
225+
* Perform the splay operation for the given key. Moves the node with
226+
* the given key to the top of the tree. If no node has the given
227+
* key, the last node on the search path is moved to the top of the
228+
* tree. This is the simplified top-down splaying algorithm from:
229+
* "Self-adjusting Binary Search Trees" by Sleator and Tarjan
230+
*/
231+
void splay(num key) {
232+
if (isEmpty) return;
233+
// Create a dummy node. The use of the dummy node is a bit
234+
// counter-intuitive: The right child of the dummy node will hold
235+
// the L tree of the algorithm. The left child of the dummy node
236+
// will hold the R tree of the algorithm. Using a dummy node, left
237+
// and right will always be nodes and we avoid special cases.
238+
final Node dummy = new Node(null, null);
239+
Node left = dummy;
240+
Node right = dummy;
241+
Node current = root;
242+
while (true) {
243+
if (key < current.key) {
244+
if (current.left == null) break;
245+
if (key < current.left.key) {
246+
// Rotate right.
247+
Node tmp = current.left;
248+
current.left = tmp.right;
249+
tmp.right = current;
250+
current = tmp;
251+
if (current.left == null) break;
252+
}
253+
// Link right.
254+
right.left = current;
255+
right = current;
256+
current = current.left;
257+
} else if (key > current.key) {
258+
if (current.right == null) break;
259+
if (key > current.right.key) {
260+
// Rotate left.
261+
Node tmp = current.right;
262+
current.right = tmp.left;
263+
tmp.left = current;
264+
current = tmp;
265+
if (current.right == null) break;
266+
}
267+
// Link left.
268+
left.right = current;
269+
left = current;
270+
current = current.right;
271+
} else {
272+
break;
273+
}
274+
}
275+
// Assemble.
276+
left.right = current.left;
277+
right.left = current.right;
278+
current.left = dummy.right;
279+
current.right = dummy.left;
280+
root = current;
281+
}
282+
283+
/**
284+
* Returns a list with all the keys of the tree.
285+
*/
286+
List<num> exportKeys() {
287+
List<num> result = [];
288+
if (!isEmpty) root.traverse((Node node) => result.add(node.key));
289+
return result;
290+
}
291+
292+
// Tells whether the tree is empty.
293+
bool get isEmpty => null == root;
294+
295+
// Pointer to the root node of the tree.
296+
Node root;
297+
}
298+
299+
300+
class Node {
301+
Node(this.key, this.value);
302+
final num key;
303+
final Object value;
304+
305+
Node left, right;
306+
307+
/**
308+
* Performs an ordered traversal of the subtree starting here.
309+
*/
310+
void traverse(void f(Node n)) {
311+
Node current = this;
312+
while (current != null) {
313+
Node left = current.left;
314+
if (left != null) left.traverse(f);
315+
f(current);
316+
current = current.right;
317+
}
318+
}
319+
}

0 commit comments

Comments
 (0)