Skip to content

Commit 12f789e

Browse files
authored
RUBY-3316 Quality of life updates for BSON benchmarks (#2774)
* RUBY-3314 Implement variable iterations for benchmarks * report percentiles along with the median * rename Benchmarking::Micro to Benchmarking::BSON * refactoring to appease rubocop * RUBY-3315 benchmark scoring * clean up the Rakefile * progress indicator while benchmarks are running * rubocop
1 parent b03052a commit 12f789e

File tree

8 files changed

+276
-191
lines changed

8 files changed

+276
-191
lines changed

Rakefile

Lines changed: 1 addition & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -131,165 +131,4 @@ namespace :docs do
131131
end
132132
end
133133

134-
require_relative "profile/benchmarking"
135-
136-
# Some require data files, available from the drivers team. See the comments above each task for details."
137-
namespace :benchmark do
138-
desc "Run the bson benchmarking tests"
139-
task :bson do
140-
puts "BSON BENCHMARK"
141-
Mongo::Benchmarking.report({
142-
bson: Mongo::Benchmarking::BSON.run_all({
143-
flat: %i[ encode decode ],
144-
deep: %i[ encode decode ],
145-
full: %i[ encode decode ],
146-
})
147-
})
148-
end
149-
150-
namespace :bson do
151-
namespace :flat do
152-
desc "Benchmarking for flat bson documents."
153-
154-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called flat_bson.json.
155-
task :encode do
156-
puts "BSON BENCHMARK :: FLAT :: ENCODE"
157-
Mongo::Benchmarking.report({ bson: { flat: { encode: Mongo::Benchmarking::BSON.run(:flat, :encode) } } })
158-
end
159-
160-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called flat_bson.json.
161-
task :decode do
162-
puts "BSON BENCHMARK :: FLAT :: DECODE"
163-
Mongo::Benchmarking.report({ bson: { flat: { decode: Mongo::Benchmarking::BSON.run(:flat, :decode) } } })
164-
end
165-
end
166-
167-
namespace :deep do
168-
desc "Benchmarking for deep bson documents."
169-
170-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called deep_bson.json.
171-
task :encode do
172-
puts "BSON BENCHMARK :: DEEP :: ENCODE"
173-
Mongo::Benchmarking.report({ bson: { deep: { encode: Mongo::Benchmarking::BSON.run(:deep, :encode) } } })
174-
end
175-
176-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called deep_bson.json.
177-
task :decode do
178-
puts "BSON BENCHMARK :: DEEP :: DECODE"
179-
Mongo::Benchmarking.report({ bson: { deep: { decode: Mongo::Benchmarking::BSON.run(:deep, :decode) } } })
180-
end
181-
end
182-
183-
namespace :full do
184-
desc "Benchmarking for full bson documents."
185-
186-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called full_bson.json.
187-
task :encode do
188-
puts "BSON BENCHMARK :: FULL :: ENCODE"
189-
Mongo::Benchmarking.report({ bson: { full: { encode: Mongo::Benchmarking::BSON.run(:full, :encode) } } })
190-
end
191-
192-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called full_bson.json.
193-
task :decode do
194-
puts "BSON BENCHMARK :: FULL :: DECODE"
195-
Mongo::Benchmarking.report({ bson: { full: { decode: Mongo::Benchmarking::BSON.run(:full, :decode) } } })
196-
end
197-
end
198-
end
199-
200-
namespace :single_doc do
201-
desc "Run the common driver single-document benchmarking tests"
202-
task :command do
203-
puts "SINGLE DOC BENCHMARK:: COMMAND"
204-
Mongo::Benchmarking::SingleDoc.run(:command)
205-
end
206-
207-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called TWEET.json.
208-
task :find_one do
209-
puts "SINGLE DOC BENCHMARK:: FIND ONE BY ID"
210-
Mongo::Benchmarking::SingleDoc.run(:find_one)
211-
end
212-
213-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called SMALL_DOC.json.
214-
task :insert_one_small do
215-
puts "SINGLE DOC BENCHMARK:: INSERT ONE SMALL DOCUMENT"
216-
Mongo::Benchmarking::SingleDoc.run(:insert_one_small)
217-
end
218-
219-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called LARGE_DOC.json.
220-
task :insert_one_large do
221-
puts "SINGLE DOC BENCHMARK:: INSERT ONE LARGE DOCUMENT"
222-
Mongo::Benchmarking::SingleDoc.run(:insert_one_large)
223-
end
224-
end
225-
226-
namespace :multi_doc do
227-
desc "Run the common driver multi-document benchmarking tests"
228-
229-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called TWEET.json.
230-
task :find_many do
231-
puts "MULTI DOCUMENT BENCHMARK:: FIND MANY"
232-
Mongo::Benchmarking::MultiDoc.run(:find_many)
233-
end
234-
235-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called SMALL_DOC.json.
236-
task :bulk_insert_small do
237-
puts "MULTI DOCUMENT BENCHMARK:: BULK INSERT SMALL"
238-
Mongo::Benchmarking::MultiDoc.run(:bulk_insert_small)
239-
end
240-
241-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called LARGE_DOC.json.
242-
task :bulk_insert_large do
243-
puts "MULTI DOCUMENT BENCHMARK:: BULK INSERT LARGE"
244-
Mongo::Benchmarking::MultiDoc.run(:bulk_insert_large)
245-
end
246-
247-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called GRIDFS_LARGE.
248-
task :gridfs_upload do
249-
puts "MULTI DOCUMENT BENCHMARK:: GRIDFS UPLOAD"
250-
Mongo::Benchmarking::MultiDoc.run(:gridfs_upload)
251-
end
252-
253-
# Requirement: A file in Mongo::Benchmarking::DATA_PATH, called GRIDFS_LARGE.
254-
task :gridfs_download do
255-
puts "MULTI DOCUMENT BENCHMARK:: GRIDFS DOWNLOAD"
256-
Mongo::Benchmarking::MultiDoc.run(:gridfs_download)
257-
end
258-
end
259-
260-
namespace :parallel do
261-
desc "Run the common driver paralell ETL benchmarking tests"
262-
263-
# Requirement: A directory in Mongo::Benchmarking::DATA_PATH, called LDJSON_MULTI,
264-
# with the files used in this task.
265-
task :import do
266-
puts "PARALLEL ETL BENCHMARK:: IMPORT"
267-
Mongo::Benchmarking::Parallel.run(:import)
268-
end
269-
270-
# Requirement: A directory in Mongo::Benchmarking::DATA_PATH, called LDJSON_MULTI,
271-
# with the files used in this task.
272-
# Requirement: Another directory in "#{Mongo::Benchmarking::DATA_PATH}/LDJSON_MULTI"
273-
# called 'output'.
274-
task :export do
275-
puts "PARALLEL ETL BENCHMARK:: EXPORT"
276-
Mongo::Benchmarking::Parallel.run(:export)
277-
end
278-
279-
# Requirement: A directory in Mongo::Benchmarking::DATA_PATH, called GRIDFS_MULTI,
280-
# with the files used in this task.
281-
task :gridfs_upload do
282-
puts "PARALLEL ETL BENCHMARK:: GRIDFS UPLOAD"
283-
Mongo::Benchmarking::Parallel.run(:gridfs_upload)
284-
end
285-
286-
# Requirement: A directory in Mongo::Benchmarking::DATA_PATH, called GRIDFS_MULTI,
287-
# with the files used in this task.
288-
# Requirement: Another directory in "#{Mongo::Benchmarking::DATA_PATH}/GRIDFS_MULTI"
289-
# called 'output'.
290-
task :gridfs_download do
291-
puts "PARALLEL ETL BENCHMARK:: GRIDFS DOWNLOAD"
292-
Mongo::Benchmarking::Parallel.run(:gridfs_download)
293-
end
294-
end
295-
end
134+
load 'profile/benchmarking/rake/tasks.rake'

profile/benchmarking/bson.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def score_for(type, percentiles, scale: 10_000)
7171
# @return [ Hash<:timings,:percentiles,:score> ] The test results for
7272
# the requested benchmark.
7373
def run(type, action)
74-
timings = Benchmarking.without_gc { send(action, file_for(type)) }
74+
timings = send(action, file_for(type))
7575
percentiles = Percentiles.new(timings)
7676
score = score_for(type, percentiles)
7777

profile/benchmarking/helper.rb

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,21 @@ def parse_json(document)
5858
# iterating.
5959
#
6060
# @return [ Array<Float> ] the timings for each iteration
61-
def benchmark(max_iterations: Benchmarking::TEST_REPETITIONS, min_time: 60, max_time: 5 * 60, &block)
61+
def benchmark(max_iterations: Benchmarking::TEST_REPETITIONS,
62+
min_time: 60,
63+
max_time: 5 * 60,
64+
progress: default_progress_callback,
65+
&block)
66+
progress ||= ->(state) {} # fallback to a no-op callback
67+
progress[:start]
68+
6269
[].tap do |results|
6370
iteration_count = 0
6471
cumulative_time = 0
6572

6673
loop do
67-
timing = Benchmark.realtime(&block)
74+
timing = without_gc { Benchmark.realtime(&block) }
75+
progress[:step]
6876

6977
iteration_count += 1
7078
cumulative_time += timing
@@ -78,6 +86,8 @@ def benchmark(max_iterations: Benchmarking::TEST_REPETITIONS, min_time: 60, max_
7886
# number of iterations have been reached.
7987
break if cumulative_time >= min_time && iteration_count >= max_iterations
8088
end
89+
90+
progress[:end]
8191
end
8292
end
8393

@@ -98,32 +108,6 @@ def report(results, indent: 0, percentiles: [ 10, 25, 50, 75, 90, 95, 98, 99 ])
98108
end
99109
end
100110

101-
# A utility class for returning the list item at a given percentile
102-
# value.
103-
class Percentiles
104-
# @return [ Array<Number> ] the sorted list of numbers to consider
105-
attr_reader :list
106-
107-
# Create a new Percentiles object that encapsulates the given list of
108-
# numbers.
109-
#
110-
# @param [ Array<Number> ] list the list of numbers to considier
111-
def initialize(list)
112-
@list = list.sort
113-
end
114-
115-
# Finds and returns the element in the list that represents the given
116-
# percentile value.
117-
#
118-
# @param [ Number ] percentile a number in the range [1,100]
119-
#
120-
# @return [ Number ] the element of the list for the given percentile.
121-
def [](percentile)
122-
i = (list.size * percentile / 100.0).ceil - 1
123-
list[i]
124-
end
125-
end
126-
127111
# Get the median of values in a list.
128112
#
129113
# @example Get the median.
@@ -144,5 +128,37 @@ def without_gc
144128
ensure
145129
GC.enable
146130
end
131+
132+
private
133+
134+
# Returns the proc object (or nil) corresponding to the "PROGRESS"
135+
# environment variable.
136+
#
137+
# @return [ Proc | nil ] the callback proc to use (or nil if none should
138+
# be used)
139+
def default_progress_callback
140+
case ENV['PROGRESS']
141+
when '0', 'false', 'none'
142+
nil
143+
when nil, '1', 'true', 'minimal'
144+
method(:minimal_progress_callback).to_proc
145+
else
146+
raise ArgumentError, "unsupported progress callback #{ENV['PROGRESS'].inspect}"
147+
end
148+
end
149+
150+
# A minimal progress callback implementation, printing '|' when a benchmark
151+
# starts and '.' for each iteration.
152+
#
153+
# @param [ :start | :step | :end ] state the current progress state
154+
def minimal_progress_callback(state)
155+
case state
156+
when :start then print '|'
157+
when :step then print '.'
158+
when :end then puts
159+
end
160+
161+
$stdout.flush
162+
end
147163
end
148164
end

0 commit comments

Comments
 (0)