Skip to content

Commit 4bde723

Browse files
authored
RUBY-3314 Implement variable iterations for benchmarks (#2771)
1 parent bed8c09 commit 4bde723

File tree

4 files changed

+79
-105
lines changed

4 files changed

+79
-105
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ gemfiles/*.gemfile.lock
2626
.env.private*
2727
.env
2828
build
29+
profile/benchmarking/data

Diff for: profile/benchmarking.rb

+24-73
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# frozen_string_literal: true
2-
# rubocop:todo all
32

43
# Copyright (C) 2015-2020 MongoDB Inc.
54
#
@@ -23,107 +22,59 @@
2322
require_relative 'benchmarking/parallel'
2423

2524
module Mongo
26-
2725
# Module with all functionality for running driver benchmark tests.
2826
#
2927
# @since 2.2.3
3028
module Benchmarking
31-
3229
extend self
3330

34-
# The current path.
35-
#
36-
# @return [ String ] The current path.
37-
#
38-
# @since 2.2.3
39-
CURRENT_PATH = File.expand_path(File.dirname(__FILE__)).freeze
40-
41-
# The path to data files used in Benchmarking tests.
42-
#
4331
# @return [ String ] Path to Benchmarking test files.
44-
#
45-
# @since 2.2.3
46-
DATA_PATH = [CURRENT_PATH, 'benchmarking', 'data'].join('/').freeze
32+
DATA_PATH = [ __dir__, 'benchmarking', 'data' ].join('/').freeze
4733

48-
# The file containing the single tweet document.
49-
#
50-
# @return [ String ] The file containing the tweet document.
51-
#
52-
# @since 2.2.3
53-
TWEET_DOCUMENT_FILE = [DATA_PATH, 'TWEET.json'].join('/').freeze
34+
# @return [ String ] The file containing the single tweet document.
35+
TWEET_DOCUMENT_FILE = [ DATA_PATH, 'TWEET.json' ].join('/').freeze
5436

55-
# The file containing the single small document.
56-
#
57-
# @return [ String ] The file containing the small document.
58-
#
59-
# @since 2.2.3
60-
SMALL_DOCUMENT_FILE = [DATA_PATH, 'SMALL_DOC.json'].join('/').freeze
37+
# @return [ String ] The file containing the single small document.
38+
SMALL_DOCUMENT_FILE = [ DATA_PATH, 'SMALL_DOC.json' ].join('/').freeze
6139

62-
# The file containing the single large document.
63-
#
64-
# @return [ String ] The file containing the large document.
65-
#
66-
# @since 2.2.3
67-
LARGE_DOCUMENT_FILE = [DATA_PATH, 'LARGE_DOC.json'].join('/').freeze
40+
# @return [ String ] The file containing the single large document.
41+
LARGE_DOCUMENT_FILE = [ DATA_PATH, 'LARGE_DOC.json' ].join('/').freeze
6842

69-
# The file to upload when testing GridFS.
70-
#
71-
# @return [ String ] The file containing the GridFS test data.
72-
#
73-
# @since 2.2.3
74-
GRIDFS_FILE = [DATA_PATH, 'GRIDFS_LARGE'].join('/').freeze
43+
# @return [ String ] The file to upload when testing GridFS.
44+
GRIDFS_FILE = [ DATA_PATH, 'GRIDFS_LARGE' ].join('/').freeze
7545

76-
# The file path and base name for the LDJSON files.
77-
#
7846
# @return [ String ] The file path and base name for the LDJSON files.
79-
#
80-
# @since 2.2.3
81-
LDJSON_FILE_BASE = [DATA_PATH, 'LDJSON_MULTI', 'LDJSON'].join('/').freeze
47+
LDJSON_FILE_BASE = [ DATA_PATH, 'LDJSON_MULTI', 'LDJSON' ].join('/').freeze
8248

83-
# The file path and base name for the outputted LDJSON files.
84-
#
85-
# @return [ String ] The file path and base name for the outputted LDJSON files.
86-
#
87-
# @since 2.2.3
88-
LDJSON_FILE_OUTPUT_BASE = [DATA_PATH, 'LDJSON_MULTI', 'output', 'LDJSON'].join('/').freeze
49+
# @return [ String ] The file path and base name for the emitted LDJSON files.
50+
LDJSON_FILE_OUTPUT_BASE = [ DATA_PATH, 'LDJSON_MULTI', 'output', 'LDJSON' ].join('/').freeze
8951

90-
# The file path and base name for the GRIDFS files to upload.
91-
#
9252
# @return [ String ] The file path and base name for the GRIDFS files to upload.
93-
#
94-
# @since 2.2.3
95-
GRIDFS_MULTI_BASE = [DATA_PATH, 'GRIDFS_MULTI', 'file'].join('/').freeze
53+
GRIDFS_MULTI_BASE = [ DATA_PATH, 'GRIDFS_MULTI', 'file' ].join('/').freeze
9654

97-
# The file path and base name for the outputted GRIDFS downloaded files.
98-
#
99-
# @return [ String ] The file path and base name for the outputted GRIDFS downloaded files.
100-
#
101-
# @since 2.2.3
102-
GRIDFS_MULTI_OUTPUT_BASE = [DATA_PATH, 'GRIDFS_MULTI', 'output', 'file-output'].join('/').freeze
55+
# @return [ String ] The file path and base name for the emitted GRIDFS downloaded files.
56+
GRIDFS_MULTI_OUTPUT_BASE = [ DATA_PATH, 'GRIDFS_MULTI', 'output', 'file-output' ].join('/').freeze
10357

104-
# The default number of test repetitions.
105-
#
10658
# @return [ Integer ] The number of test repetitions.
107-
#
108-
# @since 2.2.3
109-
TEST_REPETITIONS = 100.freeze
59+
TEST_REPETITIONS = 100
11060

111-
# The number of default warmup repetitions of the test to do before
112-
# recording times.
113-
#
114-
# @return [ Integer ] The default number of warmup repetitions.
61+
# Convenience helper for loading the single tweet document.
11562
#
116-
# @since 2.2.3
117-
WARMUP_REPETITIONS = 10.freeze
118-
63+
# @return [ Hash ] a single parsed JSON document
11964
def tweet_document
12065
Benchmarking.load_file(TWEET_DOCUMENT_FILE).first
12166
end
12267

68+
# Convenience helper for loading the single small document.
69+
#
70+
# @return [ Hash ] a single parsed JSON document
12371
def small_document
12472
Benchmarking.load_file(SMALL_DOCUMENT_FILE).first
12573
end
12674

75+
# Convenience helper for loading the single large document.
76+
#
77+
# @return [ Hash ] a single parsed JSON document
12778
def large_document
12879
Benchmarking.load_file(LARGE_DOCUMENT_FILE).first
12980
end

Diff for: profile/benchmarking/helper.rb

+44-7
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
# frozen_string_literal: true
2-
# rubocop:todo all
32

43
module Mongo
5-
64
# Helper functions used by benchmarking tasks
75
module Benchmarking
8-
96
extend self
107

118
# Load a json file and represent each document as a Hash.
@@ -19,7 +16,7 @@ module Benchmarking
1916
#
2017
# @since 2.2.3
2118
def load_file(file_name)
22-
File.open(file_name, "r") do |f|
19+
File.open(file_name, 'r') do |f|
2320
f.each_line.collect do |line|
2421
parse_json(line)
2522
end
@@ -39,8 +36,47 @@ def load_file(file_name)
3936
# @since 2.2.3
4037
def parse_json(document)
4138
JSON.parse(document).tap do |doc|
42-
if doc['_id'] && doc['_id']['$oid']
43-
doc['_id'] = BSON::ObjectId.from_string(doc['_id']['$oid'])
39+
doc['_id'] = BSON::ObjectId.from_string(doc['_id']['$oid']) if doc['_id'] && doc['_id']['$oid']
40+
end
41+
end
42+
43+
# The spec requires that most benchmarks use a variable number of
44+
# iterations, defined as follows:
45+
#
46+
# * iterations should loop for at least 1 minute cumulative execution
47+
# time
48+
# * iterations should stop after 100 iterations or 5 minutes cumulative
49+
# execution time, whichever is shorter
50+
#
51+
# This method will yield once for each iteration.
52+
#
53+
# @param [ Integer ] max_iterations the maximum number of iterations to
54+
# attempt (default: 100)
55+
# @param [ Integer ] min_time the minimum number of seconds to spend
56+
# iterating
57+
# @param [ Integer ] max_time the maximum number of seconds to spend
58+
# iterating.
59+
#
60+
# @return [ Array<Float> ] the timings for each iteration
61+
def benchmark(max_iterations: Benchmarking::TEST_REPETITIONS, min_time: 60, max_time: 5 * 60, &block)
62+
[].tap do |results|
63+
iteration_count = 0
64+
cumulative_time = 0
65+
66+
loop do
67+
timing = Benchmark.realtime(&block)
68+
69+
iteration_count += 1
70+
cumulative_time += timing
71+
results.push timing
72+
73+
# always stop after the maximum time has elapsed, regardless of
74+
# iteration count.
75+
break if cumulative_time > max_time
76+
77+
# otherwise, break if the minimum time has elapsed, and the maximum
78+
# number of iterations have been reached.
79+
break if cumulative_time >= min_time && iteration_count >= max_iterations
4480
end
4581
end
4682
end
@@ -56,7 +92,8 @@ def parse_json(document)
5692
#
5793
# @since 2.2.3
5894
def median(values)
59-
values.sort![values.size/2-1]
95+
i = (values.size / 2) - 1
96+
values.sort[i]
6097
end
6198
end
6299
end

Diff for: profile/benchmarking/micro.rb

+10-25
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
# frozen_string_literal: true
2-
# rubocop:todo all
32

43
# Copyright (C) 2015-2020 MongoDB Inc.
54
#
@@ -17,13 +16,11 @@
1716

1817
module Mongo
1918
module Benchmarking
20-
2119
# These tests focus on BSON encoding and decoding; they are client-side only and
2220
# do not involve any transmission of data to or from the server.
2321
#
2422
# @since 2.2.3
2523
module Micro
26-
2724
extend self
2825

2926
# Run a micro benchmark test.
@@ -38,10 +35,11 @@ module Micro
3835
#
3936
# @since 2.2.3
4037
def run(type, action, repetitions = Benchmarking::TEST_REPETITIONS)
41-
file_name = type.to_s << "_bson.json"
38+
file_name = type.to_s << '_bson.json'
4239
GC.disable
43-
file_path = [Benchmarking::DATA_PATH, file_name].join('/')
40+
file_path = [ Benchmarking::DATA_PATH, file_name ].join('/')
4441
puts "#{action} : #{send(action, file_path, repetitions)}"
42+
GC.enable
4543
end
4644

4745
# Run an encoding micro benchmark test.
@@ -59,16 +57,8 @@ def encode(file_name, repetitions)
5957
data = Benchmarking.load_file(file_name)
6058
document = BSON::Document.new(data.first)
6159

62-
# WARMUP_REPETITIONS.times do
63-
# doc.to_bson
64-
# end
65-
66-
results = repetitions.times.collect do
67-
Benchmark.realtime do
68-
10_000.times do
69-
document.to_bson
70-
end
71-
end
60+
results = Benchmarking.benchmark(max_iterations: repetitions) do
61+
10_000.times { document.to_bson }
7262
end
7363
Benchmarking.median(results)
7464
end
@@ -88,18 +78,13 @@ def decode(file_name, repetitions)
8878
data = Benchmarking.load_file(file_name)
8979
buffer = BSON::Document.new(data.first).to_bson
9080

91-
# WARMUP_REPETITIONS.times do
92-
# BSON::Document.from_bson(buffers.shift)
93-
# end
94-
95-
results = repetitions.times.collect do
96-
Benchmark.realtime do
97-
10_000.times do
98-
BSON::Document.from_bson(buffer)
99-
buffer.rewind!
100-
end
81+
results = Benchmarking.benchmark(max_iterations: repetitions) do
82+
10_000.times do
83+
BSON::Document.from_bson(buffer)
84+
buffer.rewind!
10185
end
10286
end
87+
10388
Benchmarking.median(results)
10489
end
10590
end

0 commit comments

Comments
 (0)