Skip to content
This repository was archived by the owner on Dec 8, 2021. It is now read-only.

Commit a221df7

Browse files
authored
feat: parallelize benchmark smoke tests (#1115)
Run the smoke tests in parallel to keep the CI build times under control.
1 parent 508724a commit a221df7

File tree

1 file changed

+98
-50
lines changed

1 file changed

+98
-50
lines changed

google/cloud/spanner/benchmarks/multiple_rows_cpu_benchmark.cc

Lines changed: 98 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ struct Config {
6565

6666
std::int64_t table_size = 1000 * 1000L;
6767
std::int64_t query_size = 1000;
68+
69+
bool use_only_clients = false;
70+
bool use_only_stubs = false;
6871
};
6972

7073
std::ostream& operator<<(std::ostream& os, Config const& config);
@@ -201,6 +204,8 @@ std::ostream& operator<<(std::ostream& os, Config const& config) {
201204
<< "s"
202205
<< "\n# Table Size: " << config.table_size
203206
<< "\n# Query Size: " << config.query_size
207+
<< "\n# Use Only Stubs: " << config.use_only_stubs
208+
<< "\n# Use Only Clients: " << config.use_only_clients
204209
<< "\n# Compiler: " << cs::internal::CompilerId() << "-"
205210
<< cs::internal::CompilerVersion()
206211
<< "\n# Build Flags: " << cs::internal::BuildFlags() << "\n";
@@ -336,8 +341,8 @@ class ExperimentImpl {
336341
explicit ExperimentImpl(google::cloud::internal::DefaultPRNG const& generator)
337342
: generator_(generator) {}
338343

339-
Status FillTable(Config const& config, cs::Database const& database,
340-
std::string const& table_name) {
344+
Status CreateTable(Config const&, cs::Database const& database,
345+
std::string const& table_name) {
341346
std::string statement = "CREATE TABLE " + table_name;
342347
statement += " (Key INT64 NOT NULL,\n";
343348
for (int i = 0; i != 10; ++i) {
@@ -359,6 +364,13 @@ class ExperimentImpl {
359364
std::cerr << "Error creating table: " << db.status() << "\n";
360365
return std::move(db).status();
361366
}
367+
return {};
368+
}
369+
370+
Status FillTable(Config const& config, cs::Database const& database,
371+
std::string const& table_name) {
372+
auto status = CreateTable(config, database, table_name);
373+
if (!status.ok()) return status;
362374

363375
// We need to populate some data or all the requests to read will fail.
364376
cs::Client client(cs::MakeConnection(database));
@@ -401,6 +413,36 @@ class ExperimentImpl {
401413
cs::MakeKeyBoundClosed(cs::Value(end)));
402414
}
403415

416+
bool UseStub(Config const& config) {
417+
if (config.use_only_clients) {
418+
return false;
419+
}
420+
if (config.use_only_stubs) {
421+
return true;
422+
}
423+
std::lock_guard<std::mutex> lk(mu_);
424+
return std::uniform_int_distribution<int>(0, 1)(generator_) == 1;
425+
}
426+
427+
int ThreadCount(Config const& config) {
428+
std::lock_guard<std::mutex> lk(mu_);
429+
return std::uniform_int_distribution<int>(
430+
config.minimum_threads, config.maximum_threads)(generator_);
431+
}
432+
433+
int ClientCount(Config const& config, int thread_count) {
434+
// TODO(#1000) - avoid deadlocks with more than 100 threads per client
435+
auto const min_clients =
436+
(std::max<int>)(thread_count / 100 + 1, config.minimum_clients);
437+
auto const max_clients = config.maximum_clients;
438+
if (min_clients <= max_clients) {
439+
return min_clients;
440+
}
441+
std::lock_guard<std::mutex> lk(mu_);
442+
return std::uniform_int_distribution<int>(min_clients,
443+
max_clients - 1)(generator_);
444+
}
445+
404446
/// Get a snapshot of the random bit generator
405447
google::cloud::internal::DefaultPRNG Generator() const {
406448
std::lock_guard<std::mutex> lk(mu_);
@@ -532,30 +574,13 @@ class ReadExperiment : public Experiment {
532574
}
533575
std::cout << " DONE\n";
534576

535-
std::uniform_int_distribution<int> use_stubs_gen(0, 1);
536-
std::uniform_int_distribution<int> thread_count_gen(config.minimum_threads,
537-
config.maximum_threads);
538-
539-
// Get a snapshot of the generator, to be used in this thread only.
540-
auto generator = impl_.Generator();
541-
542577
// Capture some overall getrusage() statistics as comments.
543578
SimpleTimer overall;
544579
overall.Start();
545580
for (int i = 0; i != config.samples; ++i) {
546-
auto const use_stubs = use_stubs_gen(generator) == 1;
547-
auto const thread_count = thread_count_gen(generator);
548-
// TODO(#1000) - avoid deadlocks with more than 100 threads per client
549-
auto const min_clients = (std::max<std::size_t>)(thread_count / 100 + 1,
550-
config.minimum_clients);
551-
auto const max_clients = clients.size();
552-
auto const client_count = [min_clients, max_clients, &generator] {
553-
if (min_clients <= max_clients) {
554-
return min_clients;
555-
}
556-
return std::uniform_int_distribution<std::size_t>(
557-
min_clients, max_clients - 1)(generator);
558-
}();
581+
auto const use_stubs = impl_.UseStub(config);
582+
auto const thread_count = impl_.ThreadCount(config);
583+
auto const client_count = impl_.ClientCount(config, thread_count);
559584
if (use_stubs) {
560585
std::vector<std::shared_ptr<cs::internal::SpannerStub>> iteration_stubs(
561586
stubs.begin(), stubs.begin() + client_count);
@@ -773,28 +798,13 @@ class UpdateExperiment : public Experiment {
773798
}
774799
std::cout << " DONE\n";
775800

776-
std::uniform_int_distribution<int> use_stubs_gen(0, 1);
777-
std::uniform_int_distribution<int> thread_count_gen(config.minimum_threads,
778-
config.maximum_threads);
779-
780-
auto generator = impl_.Generator();
781801
// Capture some overall getrusage() statistics as comments.
782802
SimpleTimer overall;
783803
overall.Start();
784804
for (int i = 0; i != config.samples; ++i) {
785-
auto const use_stubs = use_stubs_gen(generator) == 1;
786-
auto const thread_count = thread_count_gen(generator);
787-
// TODO(#1000) - avoid deadlocks with more than 100 threads per client
788-
auto const min_clients = (std::max<std::size_t>)(thread_count / 100 + 1,
789-
config.minimum_clients);
790-
auto const max_clients = clients.size();
791-
auto const client_count = [min_clients, max_clients, &generator] {
792-
if (min_clients <= max_clients) {
793-
return min_clients;
794-
}
795-
return std::uniform_int_distribution<std::size_t>(
796-
min_clients, max_clients - 1)(generator);
797-
}();
805+
auto const use_stubs = impl_.UseStub(config);
806+
auto const thread_count = impl_.ThreadCount(config);
807+
auto const client_count = impl_.ClientCount(config, thread_count);
798808
if (use_stubs) {
799809
std::vector<std::shared_ptr<cs::internal::SpannerStub>> iteration_stubs(
800810
stubs.begin(), stubs.begin() + client_count);
@@ -1023,34 +1033,61 @@ class RunAllExperiment : public Experiment {
10231033

10241034
Status Run(Config const& cfg, cs::Database const& database) override {
10251035
// Smoke test all the experiments by running a very small version of each.
1036+
1037+
std::vector<std::future<google::cloud::Status>> tasks;
10261038
for (auto& kv : AvailableExperiments()) {
10271039
// Do not recurse, skip this experiment.
10281040
if (kv.first == "run-all") continue;
10291041
Config config = cfg;
10301042
config.experiment = kv.first;
1031-
config.samples = 2;
1043+
config.samples = 1;
10321044
config.iteration_duration = std::chrono::seconds(1);
10331045
config.minimum_threads = 1;
10341046
config.maximum_threads = 1;
10351047
config.minimum_clients = 1;
10361048
config.maximum_clients = 1;
10371049
config.table_size = 10;
10381050
config.query_size = 1;
1039-
std::cout << "# Smoke test for experiment: " << kv.first << "\n";
1040-
std::cout << config << "\n" << std::flush;
1051+
10411052
auto experiment = kv.second(generator_);
1042-
auto status = experiment->SetUp(config, database);
1043-
if (!status.ok()) {
1044-
std::cout << "# ERROR in SetUp: " << status << "\n";
1045-
continue;
1053+
1054+
tasks.push_back(std::async(
1055+
std::launch::async,
1056+
[](Config config, cs::Database const& database, std::mutex& mu,
1057+
std::unique_ptr<Experiment> experiment) {
1058+
{
1059+
std::lock_guard<std::mutex> lk(mu);
1060+
std::cout << "# Smoke test for experiment\n";
1061+
std::cout << config << "\n" << std::flush;
1062+
}
1063+
auto status = experiment->SetUp(config, database);
1064+
if (!status.ok()) {
1065+
std::lock_guard<std::mutex> lk(mu);
1066+
std::cout << "# ERROR in SetUp: " << status << "\n";
1067+
return status;
1068+
}
1069+
config.use_only_clients = true;
1070+
experiment->Run(config, database);
1071+
config.use_only_stubs = true;
1072+
experiment->Run(config, database);
1073+
experiment->TearDown(config, database);
1074+
return google::cloud::Status();
1075+
},
1076+
config, database, std::ref(mu_), std::move(experiment)));
1077+
}
1078+
1079+
Status status;
1080+
for (auto& task : tasks) {
1081+
auto s = task.get();
1082+
if (!s.ok()) {
1083+
status = std::move(s);
10461084
}
1047-
experiment->Run(config, database);
1048-
experiment->TearDown(config, database);
10491085
}
1050-
return {};
1086+
return status;
10511087
}
10521088

10531089
private:
1090+
std::mutex mu_;
10541091
google::cloud::internal::DefaultPRNG generator_;
10551092
};
10561093

@@ -1144,6 +1181,10 @@ google::cloud::StatusOr<Config> ParseArgs(std::vector<std::string> args) {
11441181
[](Config& c, std::string const& v) { c.table_size = std::stol(v); }},
11451182
{"--query-size=",
11461183
[](Config& c, std::string const& v) { c.query_size = std::stol(v); }},
1184+
{"--use-only-stubs",
1185+
[](Config& c, std::string const&) { c.use_only_stubs = true; }},
1186+
{"--use-only-clients",
1187+
[](Config& c, std::string const&) { c.use_only_clients = true; }},
11471188
};
11481189

11491190
auto invalid_argument = [](std::string msg) {
@@ -1224,6 +1265,13 @@ google::cloud::StatusOr<Config> ParseArgs(std::vector<std::string> args) {
12241265
<< " than the query size (" << config.query_size << ")";
12251266
return invalid_argument(os.str());
12261267
}
1268+
1269+
if (config.use_only_stubs && config.use_only_clients) {
1270+
std::ostringstream os;
1271+
os << "Only one of --use-only-stubs or --use-only-clients can be set";
1272+
return invalid_argument(os.str());
1273+
}
1274+
12271275
return config;
12281276
}
12291277

0 commit comments

Comments
 (0)