Skip to content

Commit 19b9ba2

Browse files
authored
Merge pull request #487 from jhawthorn/splay
Add splay GC benchmark ported from V8/WebKit
2 parents 6e3ec43 + 1a35e47 commit 19b9ba2

File tree

2 files changed

+217
-0
lines changed

2 files changed

+217
-0
lines changed

benchmarks.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,9 @@ rubykon:
128128
sudoku:
129129
desc: sudoku solver
130130
ractor: true
131+
splay:
132+
desc: Splay tree operations (insert, find, remove) to stress GC.
133+
single_file: true
131134
tinygql:
132135
desc: TinyGQL gem parsing a large file in pure Ruby
133136

benchmarks/splay.rb

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# Copyright 2009 the V8 project authors. All rights reserved.
2+
# Copyright (C) 2015 Apple Inc. 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+
#
29+
# Ported to Ruby from the V8/WebKit JavaScript benchmark suite.
30+
# https://browserbench.org/JetStream2.1/Octane/splay.js
31+
32+
require_relative '../harness/loader'
33+
34+
class SplayTree
35+
class Node
36+
attr_accessor :key, :value, :left, :right
37+
38+
def initialize(key, value)
39+
@key = key
40+
@value = value
41+
@left = nil
42+
@right = nil
43+
end
44+
end
45+
46+
def initialize
47+
@root = nil
48+
end
49+
50+
def empty?
51+
@root.nil?
52+
end
53+
54+
def insert(key, value)
55+
if empty?
56+
@root = Node.new(key, value)
57+
return
58+
end
59+
splay!(key)
60+
return if @root.key == key
61+
node = Node.new(key, value)
62+
if key > @root.key
63+
node.left = @root
64+
node.right = @root.right
65+
@root.right = nil
66+
else
67+
node.right = @root
68+
node.left = @root.left
69+
@root.left = nil
70+
end
71+
@root = node
72+
end
73+
74+
def remove(key)
75+
raise "Key not found: #{key}" if empty?
76+
splay!(key)
77+
raise "Key not found: #{key}" if @root.key != key
78+
removed = @root
79+
if @root.left.nil?
80+
@root = @root.right
81+
else
82+
right = @root.right
83+
@root = @root.left
84+
splay!(key)
85+
@root.right = right
86+
end
87+
removed
88+
end
89+
90+
def find(key)
91+
return nil if empty?
92+
splay!(key)
93+
@root.key == key ? @root : nil
94+
end
95+
96+
def find_max(start_node = nil)
97+
return nil if empty?
98+
current = start_node || @root
99+
current = current.right while current.right
100+
current
101+
end
102+
103+
def find_greatest_less_than(key)
104+
return nil if empty?
105+
splay!(key)
106+
if @root.key < key
107+
@root
108+
elsif @root.left
109+
find_max(@root.left)
110+
end
111+
end
112+
113+
private
114+
115+
def splay!(key)
116+
return if empty?
117+
dummy = Node.new(nil, nil)
118+
left = dummy
119+
right = dummy
120+
current = @root
121+
loop do
122+
if key < current.key
123+
break unless current.left
124+
if key < current.left.key
125+
tmp = current.left
126+
current.left = tmp.right
127+
tmp.right = current
128+
current = tmp
129+
break unless current.left
130+
end
131+
right.left = current
132+
right = current
133+
current = current.left
134+
elsif key > current.key
135+
break unless current.right
136+
if key > current.right.key
137+
tmp = current.right
138+
current.right = tmp.left
139+
tmp.left = current
140+
current = tmp
141+
break unless current.right
142+
end
143+
left.right = current
144+
left = current
145+
current = current.right
146+
else
147+
break
148+
end
149+
end
150+
left.right = current.left
151+
right.left = current.right
152+
current.left = dummy.right
153+
current.right = dummy.left
154+
@root = current
155+
end
156+
end
157+
158+
TREE_SIZE = 8000
159+
MODIFICATIONS = 80
160+
PAYLOAD_DEPTH = 5
161+
162+
class PayloadNode
163+
attr_accessor :left, :right
164+
def initialize(left, right)
165+
@left = left
166+
@right = right
167+
end
168+
end
169+
170+
def generate_payload(depth, tag)
171+
if depth == 0
172+
{ array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
173+
string: "String for key #{tag} in leaf node" }
174+
else
175+
PayloadNode.new(
176+
generate_payload(depth - 1, tag),
177+
generate_payload(depth - 1, tag)
178+
)
179+
end
180+
end
181+
182+
def insert_new_node(tree, rng)
183+
loop do
184+
key = rng.rand
185+
next if tree.find(key)
186+
tree.insert(key, generate_payload(PAYLOAD_DEPTH, key.to_s))
187+
return key
188+
end
189+
end
190+
191+
def splay_setup(rng)
192+
tree = SplayTree.new
193+
TREE_SIZE.times { insert_new_node(tree, rng) }
194+
tree
195+
end
196+
197+
def splay_run(tree, rng)
198+
MODIFICATIONS.times do
199+
key = insert_new_node(tree, rng)
200+
greatest = tree.find_greatest_less_than(key)
201+
if greatest
202+
tree.remove(greatest.key)
203+
else
204+
tree.remove(key)
205+
end
206+
end
207+
end
208+
209+
rng = Random.new(42)
210+
tree = splay_setup(rng)
211+
212+
run_benchmark(200) do
213+
50.times { splay_run(tree, rng) }
214+
end

0 commit comments

Comments
 (0)