Skip to content

Commit e2ea7c6

Browse files
committed
Improve performance of dependency management - avoid linear search.
1 parent fb31045 commit e2ea7c6

File tree

2 files changed

+117
-69
lines changed

2 files changed

+117
-69
lines changed

lib/protocol/http2/dependency.rb

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ def <=> other
5858
@weight <=> other.weight
5959
end
6060

61+
def == other
62+
@id == other.id
63+
end
64+
6165
# The connection this stream belongs to.
6266
attr :connection
6367

@@ -101,13 +105,30 @@ def add_child(dependency)
101105

102106
dependency.parent = self
103107

104-
self.clear_cache!
108+
if @ordered_children
109+
# Binary search for insertion point:
110+
index = @ordered_children.bsearch_index do |child|
111+
child.weight >= dependency.weight
112+
end
113+
114+
if index
115+
@ordered_children.insert(index, dependency)
116+
else
117+
@ordered_children.push(dependency)
118+
end
119+
120+
@total_weight += dependency.weight
121+
end
105122
end
106123

107124
def remove_child(dependency)
108125
@children&.delete(dependency.id)
109126

110-
self.clear_cache!
127+
if @ordered_children
128+
# Don't do a linear search here, it can be slow. Instead, the child's parent will be set to `nil`, and we check this in {#consume_window} using `delete_if`.
129+
# @ordered_children.delete(dependency)
130+
@total_weight -= dependency.weight
131+
end
111132
end
112133

113134
# An exclusive flag allows for the insertion of a new level of dependencies. The exclusive flag causes the stream to become the sole dependency of its parent stream, causing other dependencies to become dependent on the exclusive stream.
@@ -195,11 +216,17 @@ def consume_window(size)
195216
end
196217

197218
# Otherwise, allow the dependent children to use up the available window:
198-
self.ordered_children&.each do |child|
199-
# Compute the proportional allocation:
200-
allocated = (child.weight * size) / @total_weight
201-
202-
child.consume_window(allocated) if allocated > 0
219+
self.ordered_children&.delete_if do |child|
220+
if child.parent
221+
# Compute the proportional allocation:
222+
allocated = (child.weight * size) / @total_weight
223+
224+
child.consume_window(allocated) if allocated > 0
225+
226+
false
227+
else
228+
true
229+
end
203230
end
204231
end
205232

test/protocol/http2/dependency.rb

Lines changed: 83 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,14 @@ def before
4848
# a
4949
# /
5050
# b
51-
begin
52-
a.priority = a.priority
53-
server.read_frame
54-
55-
priority = b.priority
56-
priority.stream_dependency = a.id
57-
b.priority = priority
58-
59-
server.read_frame
60-
end
51+
a.priority = a.priority
52+
server.read_frame
53+
54+
priority = b.priority
55+
priority.stream_dependency = a.id
56+
b.priority = priority
57+
58+
server.read_frame
6159

6260
expect(a.dependency.children).to be == {b.id => b.dependency}
6361

@@ -68,13 +66,11 @@ def before
6866
# a
6967
# / \
7068
# b c
71-
begin
72-
priority = c.priority
73-
priority.stream_dependency = a.id
74-
c.priority = priority
75-
76-
server.read_frame
77-
end
69+
priority = c.priority
70+
priority.stream_dependency = a.id
71+
c.priority = priority
72+
73+
server.read_frame
7874

7975
expect(a.dependency.children).to be == {b.id => b.dependency, c.id => c.dependency}
8076
expect(server.dependencies[a.id].children).to be == {b.id => server.dependencies[b.id], c.id => server.dependencies[c.id]}
@@ -84,14 +80,12 @@ def before
8480
# d
8581
# / \
8682
# b c
87-
begin
88-
priority = d.priority
89-
priority.stream_dependency = a.id
90-
priority.exclusive = true
91-
d.priority = priority
92-
93-
server.read_frame
94-
end
83+
priority = d.priority
84+
priority.stream_dependency = a.id
85+
priority.exclusive = true
86+
d.priority = priority
87+
88+
server.read_frame
9589

9690
expect(a.dependency.children).to be == {d.id => d.dependency}
9791
expect(d.dependency.children).to be == {b.id => b.dependency, c.id => c.dependency}
@@ -163,36 +157,32 @@ def before
163157
# b c
164158
# |
165159
# d
166-
begin
167-
a.priority = a.priority
168-
server.read_frame
169-
170-
priority = b.priority
171-
priority.stream_dependency = a.id
172-
b.priority = priority
173-
server.read_frame
174-
175-
priority = c.priority
176-
priority.stream_dependency = a.id
177-
c.priority = priority
178-
server.read_frame
179-
180-
priority = d.priority
181-
priority.stream_dependency = c.id
182-
d.priority = priority
183-
server.read_frame
184-
end
160+
a.priority = a.priority
161+
server.read_frame
162+
163+
priority = b.priority
164+
priority.stream_dependency = a.id
165+
b.priority = priority
166+
server.read_frame
167+
168+
priority = c.priority
169+
priority.stream_dependency = a.id
170+
c.priority = priority
171+
server.read_frame
172+
173+
priority = d.priority
174+
priority.stream_dependency = c.id
175+
d.priority = priority
176+
server.read_frame
185177

186178
expect(server.dependencies[a.id].children).to be == {b.id => server.dependencies[b.id], c.id => server.dependencies[c.id]}
187179
expect(server.dependencies[c.id].children).to be == {d.id => server.dependencies[d.id]}
188180

189181
# a
190182
# / \
191183
# b d
192-
begin
193-
c.send_reset_stream
194-
server.read_frame
195-
end
184+
c.send_reset_stream
185+
server.read_frame
196186

197187
expect(server.dependencies[a.id].children).to be == {b.id => server.dependencies[b.id], d.id => server.dependencies[d.id]}
198188
expect(server.dependencies[c.id]).to be == nil
@@ -205,20 +195,18 @@ def before
205195
# a
206196
# / \
207197
# b c
208-
begin
209-
a.priority = a.priority
210-
server.read_frame
211-
212-
priority = b.priority
213-
priority.stream_dependency = a.id
214-
b.priority = priority
215-
server.read_frame
216-
217-
priority = c.priority
218-
priority.stream_dependency = a.id
219-
c.priority = priority
220-
server.read_frame
221-
end
198+
a.priority = a.priority
199+
server.read_frame
200+
201+
priority = b.priority
202+
priority.stream_dependency = a.id
203+
b.priority = priority
204+
server.read_frame
205+
206+
priority = c.priority
207+
priority.stream_dependency = a.id
208+
c.priority = priority
209+
server.read_frame
222210

223211
a.dependency.print_hierarchy(buffer)
224212

@@ -228,4 +216,37 @@ def before
228216
#<Protocol::HTTP2::Dependency id=5 parent id=1 weight=16 0 children>
229217
END
230218
end
219+
220+
it "can insert several children" do
221+
a, b, c, d = 4.times.collect {client.create_stream}
222+
223+
b.dependency.weight = 10
224+
c.dependency.weight = 20
225+
d.dependency.weight = 30
226+
227+
# a __
228+
# / \ \
229+
# b c d
230+
231+
a.priority = a.priority
232+
server.read_frame
233+
234+
priority = d.priority
235+
priority.stream_dependency = a.id
236+
d.priority = priority
237+
server.read_frame
238+
239+
priority = b.priority
240+
priority.stream_dependency = a.id
241+
b.priority = priority
242+
server.read_frame
243+
244+
priority = c.priority
245+
priority.stream_dependency = a.id
246+
c.priority = priority
247+
server.read_frame
248+
249+
ordered_children = server.dependencies[a.id].ordered_children
250+
expect(ordered_children).to be == [b.dependency, c.dependency, d.dependency]
251+
end
231252
end

0 commit comments

Comments
 (0)