Skip to content

Commit 70d26e2

Browse files
adriandoleeramongodbkkloberdanz
authored
CXX-2710 Run microbenchmarks in Evergreen (#1063)
--------- Co-authored-by: Ezra Chung <[email protected]> Co-authored-by: Kyle Kloberdanz <[email protected]>
1 parent 86d4a2b commit 70d26e2

13 files changed

+187
-81
lines changed

.mci.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,16 @@ functions:
360360
CXX: ${cxx_compiler}
361361
script: .evergreen/compile.sh
362362

363+
"compile_benchmarks":
364+
- command: shell.exec
365+
type: setup
366+
params:
367+
shell: bash
368+
working_dir: "mongo-cxx-driver"
369+
script: |
370+
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH="$(pwd)/../mongoc" -DCMAKE_CXX_STANDARD=20
371+
cmake --build build --target microbenchmarks --parallel 64
372+
363373
"test":
364374
- command: shell.exec
365375
params:
@@ -509,6 +519,24 @@ functions:
509519
make -C extras/docker/bookworm nocachebuild test
510520
echo "Building Red Hat UBI Docker image"
511521
make -C extras/docker/redhat-ubi-9.3 nocachebuild test
522+
523+
"run benchmarks":
524+
- command: shell.exec
525+
type: setup
526+
params:
527+
shell: bash
528+
working_dir: "mongo-cxx-driver"
529+
script: ./etc/microbenchmark-test-data.sh
530+
- command: shell.exec
531+
type: test
532+
params:
533+
shell: bash
534+
working_dir: "mongo-cxx-driver"
535+
script: ./build/benchmark/microbenchmarks all
536+
- command: perf.send
537+
params:
538+
name: perf
539+
file: mongo-cxx-driver/results.json
512540

513541
#######################################
514542
# Post Task #
@@ -620,6 +648,14 @@ tasks:
620648
vars:
621649
ENABLE_TESTS: OFF
622650

651+
- name: compile_and_run_benchmarks
652+
commands:
653+
- func: "setup"
654+
- func: "start_mongod"
655+
- func: "fetch_c_driver_source"
656+
- func: "compile_benchmarks"
657+
- func: "run benchmarks"
658+
623659
- name: compile_macro_guard_tests
624660
commands:
625661
- func: "setup"
@@ -1303,6 +1339,14 @@ buildvariants:
13031339
- name: compile_and_test_with_shared_libs_sharded_cluster_with_libmongocrypt
13041340
- name: build_example_with_add_subdirectory
13051341

1342+
- name: benchmarks-rhel9
1343+
display_name: "Benchmarks (RHEL 9.2)"
1344+
expansions:
1345+
mongodb_version: "v6.0-perf"
1346+
run_on: rhel90-dbx-perf-large
1347+
tasks:
1348+
- name: compile_and_run_benchmarks
1349+
13061350
- name: arm-rhel9-release-latest
13071351
display_name: "arm64 RHEL 9 Release (MongoDB Latest)"
13081352
expansions:

benchmark/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ file (GLOB benchmark_DIST_hpps RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.hpp)
5050

5151
set_dist_list (benchmark_DIST
5252
CMakeLists.txt
53-
README.txt
53+
README.md
5454
${BENCHMARK_LIBRARY}
5555
${benchmark_DIST_hpps}
5656
)

benchmark/README.txt renamed to benchmark/README.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
11
This suite implements the benchmarks described in this spec: https://docs.google.com/document/d/1x7dCHHx_owOFYIPZ6VQwPerSWRhdkMSiepyFrNSFlLw/edit#
22

3-
In order to run the microbenchmarks, first run etc/microbenchmark-test-data.sh to download the data.
3+
# Compiling
4+
Configure the C++ driver to build with C++17 or newer and build the `microbenchmarks` target.
45

5-
In order to run specific tests, just specify their names as arguments. If run with no arguments,
6-
all benchmarks will be run.
7-
e.g. build/benchmark/microbenchmarks BSONBench MultiBench
6+
# Running
7+
In order to run the microbenchmarks, first run `etc/microbenchmark-test-data.sh` to download the test data.
8+
9+
In order to run specific tests, just specify their names as arguments:
10+
`build/benchmark/microbenchmarks BSONBench MultiBench`
11+
12+
To run all tests, specify `all` as an argument:
13+
`build/benchmark/microbenchmarks all`
814

915
Full list of options:
1016
BSONBench
@@ -14,14 +20,9 @@ ReadBench
1420
WriteBench
1521
RunCommandBench
1622

17-
Note: make sure you run both the download script and the microbenchmarks binary from the project root.
18-
19-
See the spec for details on these benchmarks.
20-
21-
In case there is trouble running the benchmarks, they were written and will run with:
22-
-libbson at commit 1c5b90022bbbdf0fa8095b337a25b218a2651241
23-
-mongoc driver at commit a3c8a760ce90f144fd65d8a4a6e3606cbeea2f6b
23+
Note: run both the download script and the microbenchmarks binary from the project root.
2424

25+
# Notes
2526
Note that in order to compare against the other drivers, an inMemory mongod instance should be
2627
used.
2728

benchmark/benchmark_runner.cpp

Lines changed: 64 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@
1414

1515
#include "benchmark_runner.hpp"
1616

17+
#include <chrono>
18+
#include <cstdint>
19+
#include <fstream>
20+
#include <iomanip>
21+
#include <iostream>
22+
#include <memory>
23+
#include <sstream>
24+
1725
#include "bson/bson_encoding.hpp"
1826
#include "multi_doc/bulk_insert.hpp"
1927
#include "multi_doc/find_many.hpp"
@@ -26,7 +34,13 @@
2634
#include "single_doc/find_one_by_id.hpp"
2735
#include "single_doc/insert_one.hpp"
2836
#include "single_doc/run_command.hpp"
37+
#include <bsoncxx/builder/basic/array.hpp>
38+
#include <bsoncxx/builder/basic/document.hpp>
39+
#include <bsoncxx/builder/basic/kvp.hpp>
40+
#include <bsoncxx/builder/basic/sub_document.hpp>
41+
#include <bsoncxx/json.hpp>
2942
#include <bsoncxx/stdx/make_unique.hpp>
43+
#include <bsoncxx/types.hpp>
3044

3145
namespace benchmark {
3246

@@ -47,26 +61,28 @@ benchmark_runner::benchmark_runner(std::set<benchmark_type> types) : _types{type
4761
_microbenches.push_back(make_unique<run_command>());
4862
_microbenches.push_back(make_unique<find_one_by_id>("single_and_multi_document/tweet.json"));
4963
_microbenches.push_back(make_unique<insert_one>(
50-
"TestSmallDocInsertOne", 2.75, 10000, "single_and_multi_document/small_doc.json"));
64+
"TestSmallDocInsertOne", 2.75, iterations, "single_and_multi_document/small_doc.json"));
5165
_microbenches.push_back(make_unique<insert_one>(
5266
"TestLargeDocInsertOne", 27.31, 10, "single_and_multi_document/large_doc.json"));
5367

5468
// Multi doc microbenchmarks
5569
_microbenches.push_back(make_unique<find_many>("single_and_multi_document/tweet.json"));
5670
_microbenches.push_back(make_unique<bulk_insert>(
57-
"TestSmallDocBulkInsert", 2.75, 10000, "single_and_multi_document/small_doc.json"));
71+
"TestSmallDocBulkInsert", 2.75, iterations, "single_and_multi_document/small_doc.json"));
5872
_microbenches.push_back(make_unique<bulk_insert>(
5973
"TestLargeDocBulkInsert", 27.31, 10, "single_and_multi_document/large_doc.json"));
60-
_microbenches.push_back(
61-
make_unique<gridfs_upload>("single_and_multi_document/gridfs_large.bin"));
62-
_microbenches.push_back(
63-
make_unique<gridfs_download>("single_and_multi_document/gridfs_large.bin"));
74+
// CXX-2794: Disable GridFS benchmarks due to long runtime
75+
// _microbenches.push_back(
76+
// make_unique<gridfs_upload>("single_and_multi_document/gridfs_large.bin"));
77+
// _microbenches.push_back(
78+
// make_unique<gridfs_download>("single_and_multi_document/gridfs_large.bin"));
6479

6580
// Parallel microbenchmarks
6681
_microbenches.push_back(make_unique<json_multi_import>("parallel/ldjson_multi"));
6782
_microbenches.push_back(make_unique<json_multi_export>("parallel/ldjson_multi"));
68-
_microbenches.push_back(make_unique<gridfs_multi_import>("parallel/gridfs_multi"));
69-
_microbenches.push_back(make_unique<gridfs_multi_export>("parallel/gridfs_multi"));
83+
// CXX-2794: Disable GridFS benchmarks due to long runtime
84+
// _microbenches.push_back(make_unique<gridfs_multi_import>("parallel/gridfs_multi"));
85+
// _microbenches.push_back(make_unique<gridfs_multi_export>("parallel/gridfs_multi"));
7086

7187
// Need to remove some
7288
if (!_types.empty()) {
@@ -89,8 +105,7 @@ benchmark_runner::benchmark_runner(std::set<benchmark_type> types) : _types{type
89105
}
90106

91107
void benchmark_runner::run_microbenches() {
92-
mongocxx::instance instance{};
93-
108+
_start_time = std::chrono::system_clock::now();
94109
for (std::unique_ptr<microbench>& bench : _microbenches) {
95110
std::cout << "Starting " << bench->get_name() << "..." << std::endl;
96111

@@ -103,6 +118,7 @@ void benchmark_runner::run_microbenches() {
103118
<< " second(s) | " << score.get_score() << " MB/s" << std::endl
104119
<< std::endl;
105120
}
121+
_end_time = std::chrono::system_clock::now();
106122
}
107123

108124
double benchmark_runner::calculate_average(benchmark_type tag) {
@@ -145,20 +161,48 @@ double benchmark_runner::calculate_driver_bench_score() {
145161
return (calculate_read_bench_score() + calculate_write_bench_score()) / 2.0;
146162
}
147163

148-
void benchmark_runner::print_scores() {
164+
void benchmark_runner::write_scores() {
149165
double read = -1;
150166
double write = -1;
151167

168+
using namespace bsoncxx;
169+
using builder::basic::sub_document;
170+
171+
auto doc = builder::basic::document{};
172+
doc.append(kvp("info", [](sub_document subdoc) {
173+
subdoc.append(kvp("test_name", "C++ Driver microbenchmarks"));
174+
}));
175+
176+
auto write_time =
177+
[](const std::chrono::time_point<std::chrono::system_clock> t) -> std::string {
178+
std::time_t t1 = std::chrono::system_clock::to_time_t(t);
179+
std::ostringstream oss;
180+
oss << std::put_time(std::gmtime(&t1), "%Y-%m-%dT%H:%M:%S") << "+00:00";
181+
return oss.str();
182+
};
183+
doc.append(kvp("created_at", write_time(_start_time)));
184+
doc.append(kvp("completed_at", write_time(_end_time)));
185+
doc.append(kvp("artifacts", builder::basic::make_array()));
186+
187+
auto metrics_array = builder::basic::array{};
188+
std::cout << std::endl << "Composite benchmarks:" << std::endl << "===========" << std::endl;
189+
152190
std::cout << "Individual microbenchmark scores:" << std::endl << "===========" << std::endl;
153191
for (auto&& bench : _microbenches) {
154192
auto& score = bench->get_results();
193+
const auto bench_time = static_cast<double>(score.get_percentile(50).count()) / 1000.0;
155194

156-
std::cout << bench->get_name() << ": "
157-
<< static_cast<double>(score.get_percentile(50).count()) / 1000.0
158-
<< " second(s) | " << score.get_score() << " MB/s" << std::endl;
159-
}
195+
std::cout << bench->get_name() << ": " << bench_time << " seconds | " << score.get_score()
196+
<< " MB/s" << std::endl;
160197

161-
std::cout << std::endl << "Composite benchmarks:" << std::endl << "===========" << std::endl;
198+
auto metric_doc = builder::basic::document{};
199+
metric_doc.append(kvp("name", bench->get_name()));
200+
metric_doc.append(kvp("type", "THROUGHPUT"));
201+
metric_doc.append(kvp("value", score.get_score()));
202+
metrics_array.append(metric_doc);
203+
}
204+
doc.append(kvp("metrics", metrics_array));
205+
doc.append(kvp("sub_tests", builder::basic::make_array()));
162206

163207
auto print_comp = [this, &read, &write](benchmark_type type) {
164208
double avg = calculate_average(type);
@@ -169,7 +213,7 @@ void benchmark_runner::print_scores() {
169213
write = avg;
170214
}
171215

172-
std::cout << type_names[type] << " " << avg << " MB/s" << std::endl;
216+
std::cout << type_names.at(type) << " " << avg << " MB/s" << std::endl;
173217
};
174218

175219
if (!_types.empty()) {
@@ -185,5 +229,8 @@ void benchmark_runner::print_scores() {
185229
if (read > 0 && write > 0) {
186230
std::cout << "DriverBench: " << (read + write) / 2.0 << " MB/s" << std::endl;
187231
}
232+
233+
std::ofstream os{"results.json"};
234+
os << '[' << bsoncxx::to_json(doc.view()) << ']';
188235
}
189236
} // namespace benchmark

benchmark/benchmark_runner.hpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414

1515
#pragma once
1616

17-
#include <bsoncxx/stdx/optional.hpp>
18-
#include <mongocxx/instance.hpp>
17+
#include <chrono>
1918

2019
#include "microbench.hpp"
20+
#include <bsoncxx/stdx/optional.hpp>
21+
#include <mongocxx/instance.hpp>
2122

2223
namespace benchmark {
2324

@@ -27,7 +28,7 @@ class benchmark_runner {
2728

2829
void run_microbenches();
2930

30-
void print_scores();
31+
void write_scores();
3132

3233
double calculate_bson_bench_score();
3334

@@ -46,6 +47,8 @@ class benchmark_runner {
4647
private:
4748
double calculate_average(benchmark_type);
4849

50+
std::chrono::time_point<std::chrono::system_clock> _start_time;
51+
std::chrono::time_point<std::chrono::system_clock> _end_time;
4952
std::vector<std::unique_ptr<microbench>> _microbenches;
5053
std::set<benchmark_type> _types;
5154
};

benchmark/bson/bson_decoding.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ void bson_decoding::setup() {
4242
}
4343

4444
void bson_decoding::task() {
45-
for (std::uint32_t i = 0; i < 10000; i++) {
45+
for (std::uint32_t i = 0; i < iterations; i++) {
4646
// TODO CXX-1241: call bson_as_extended json on _json.
4747
}
4848
}

benchmark/bson/bson_encoding.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,10 @@
1616

1717
#include <iostream>
1818

19+
#include "../microbench.hpp"
1920
#include <bsoncxx/json.hpp>
2021
#include <bsoncxx/types.hpp>
2122

22-
#include "../microbench.hpp"
23-
2423
namespace benchmark {
2524

2625
class bson_encoding : public microbench {
@@ -58,7 +57,7 @@ void visit_document(bsoncxx::document::view doc) {
5857

5958
// Mirroring mongo-c-driver's interpretation of the spec.
6059
void bson_encoding::task() {
61-
for (std::uint32_t i = 0; i < 10000; i++) {
60+
for (std::uint32_t i = 0; i < iterations; i++) {
6261
visit_document(_doc->view());
6362
}
6463
}

benchmark/main.cpp

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,34 +12,46 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
static_assert(__cplusplus >= 201703L, "requires C++17 or higher");
16+
17+
#include <chrono>
1518
#include <iostream>
19+
#include <stdexcept>
1620

1721
#include "benchmark_runner.hpp"
22+
#include <bsoncxx/stdx/string_view.hpp>
23+
#include <mongocxx/instance.hpp>
1824

1925
using namespace benchmark;
2026

2127
int main(int argc, char* argv[]) {
28+
mongocxx::instance instance;
2229
std::set<benchmark_type> types;
2330

2431
if (argc > 1) {
25-
for (int x = 1; x < argc; ++x) {
26-
std::string type{argv[x]};
27-
auto it = names_types.find(type);
28-
29-
if (it != names_types.end()) {
32+
if (bsoncxx::stdx::string_view(argv[1]) == "all") {
33+
for (const auto& [name, type] : names_types) {
34+
types.insert(type);
35+
}
36+
} else {
37+
for (int x = 1; x < argc; ++x) {
38+
std::string type{argv[x]};
39+
auto it = names_types.find(type);
40+
41+
if (it == names_types.end()) {
42+
throw std::runtime_error("Invalid benchmark: " + type);
43+
}
3044
types.insert(it->second);
31-
} else {
32-
std::cerr << "Invalid benchmark: " << type << std::endl;
3345
}
3446
}
3547

3648
if (types.empty()) {
37-
std::cerr << "No valid benchmarks specified. Exiting." << std::endl;
38-
return 1;
49+
throw std::runtime_error("No valid benchmarks specified");
3950
}
4051
}
4152

4253
benchmark_runner runner{types};
54+
4355
runner.run_microbenches();
44-
runner.print_scores();
56+
runner.write_scores();
4557
}

0 commit comments

Comments
 (0)