Skip to content

Commit 8e0a2f9

Browse files
authored
feat(GCS+gRPC): benchmark with Direct Path (#8489)
Change the existing throughput_vs_cpu benchmark to compare gRPC and gRPC+DirectPath side-by-side. Add peer information when available.
1 parent d1020b1 commit 8e0a2f9

12 files changed

+384
-235
lines changed

google/cloud/storage/benchmarks/benchmark_utils.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,47 @@ StatusOr<ApiName> ParseApiName(std::string const& val) {
110110
return Status{StatusCode::kInvalidArgument, "unknown ApiName " + val};
111111
}
112112

113+
StatusOr<ExperimentLibrary> ParseExperimentLibrary(std::string const& val) {
114+
for (auto v : {ExperimentLibrary::kRaw, ExperimentLibrary::kCppClient}) {
115+
if (val == ToString(v)) return v;
116+
}
117+
return Status{StatusCode::kInvalidArgument,
118+
"unknown ExperimentLibrary " + val};
119+
}
120+
121+
StatusOr<ExperimentTransport> ParseExperimentTransport(std::string const& val) {
122+
for (auto v : {ExperimentTransport::kDirectPath, ExperimentTransport::kGrpc,
123+
ExperimentTransport::kJson, ExperimentTransport::kXml}) {
124+
if (val == ToString(v)) return v;
125+
}
126+
return Status{StatusCode::kInvalidArgument,
127+
"unknown ExperimentTransport " + val};
128+
}
129+
130+
std::string ToString(ExperimentLibrary v) {
131+
switch (v) {
132+
case ExperimentLibrary::kCppClient:
133+
return "CppClient";
134+
case ExperimentLibrary::kRaw:
135+
return "Raw";
136+
}
137+
return "";
138+
}
139+
140+
std::string ToString(ExperimentTransport v) {
141+
switch (v) {
142+
case ExperimentTransport::kDirectPath:
143+
return "DirectPath";
144+
case ExperimentTransport::kGrpc:
145+
return "Grpc";
146+
case ExperimentTransport::kJson:
147+
return "Json";
148+
case ExperimentTransport::kXml:
149+
return "Xml";
150+
}
151+
return "";
152+
}
153+
113154
std::string RandomBucketPrefix() { return "cloud-cpp-testing-bm"; }
114155

115156
std::string MakeRandomBucketName(google::cloud::internal::DefaultPRNG& gen) {

google/cloud/storage/benchmarks/benchmark_utils.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,36 @@ char const* ToString(ApiName api);
6868

6969
StatusOr<ApiName> ParseApiName(std::string const& val);
7070

71+
// We want to compare the following alternatives.
72+
//
73+
// - Raw (no C++ client library) JSON Download
74+
// - Raw XML Download
75+
// - Raw gRPC Download
76+
// - Raw gRPC+DirectPath Download
77+
// - JSON Download
78+
// - XML Download
79+
// - gRPC Download
80+
// - gRPC+DirectPath Download
81+
// - JSON Upload
82+
// - gRPC Upload
83+
// - gRPC+DirectPath Upload
84+
//
85+
// We will model this with 3 dimensions for each experiment:
86+
// - Direction: Upload vs. Download
87+
// - Library: Raw vs. Client library
88+
// - Transport: XML vs. JSON vs. gRPC vs. gRPC+DirectPath
89+
//
90+
// Some combinations are simply not implemented and ignored when building the
91+
// set of experiments.
92+
enum class ExperimentLibrary { kRaw, kCppClient };
93+
enum class ExperimentTransport { kDirectPath, kGrpc, kJson, kXml };
94+
95+
StatusOr<ExperimentLibrary> ParseExperimentLibrary(std::string const& val);
96+
StatusOr<ExperimentTransport> ParseExperimentTransport(std::string const& val);
97+
98+
std::string ToString(ExperimentLibrary v);
99+
std::string ToString(ExperimentTransport v);
100+
71101
std::string RandomBucketPrefix();
72102

73103
std::string MakeRandomBucketName(google::cloud::internal::DefaultPRNG& gen);

google/cloud/storage/benchmarks/storage_throughput_vs_cpu_benchmark.cc

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@
2929

3030
namespace {
3131
namespace gcs = ::google::cloud::storage;
32+
namespace gcs_ex = ::google::cloud::storage_experimental;
3233
namespace gcs_bm = ::google::cloud::storage_benchmarks;
33-
using gcs_bm::ApiName;
34+
using gcs_bm::ExperimentLibrary;
35+
using gcs_bm::ExperimentTransport;
3436
using gcs_bm::ThroughputOptions;
3537
using gcs_bm::ThroughputResult;
3638

@@ -104,16 +106,13 @@ this program.
104106
using TestResults = std::vector<ThroughputResult>;
105107

106108
TestResults RunThread(ThroughputOptions const& ThroughputOptions,
107-
gcs::Client rest_client, gcs::Client grpc_client,
108109
std::string const& bucket_name, int thread_id);
109110
void PrintResults(TestResults const& results);
110111

111112
google::cloud::StatusOr<ThroughputOptions> ParseArgs(int argc, char* argv[]);
112113

113114
} // namespace
114115

115-
using ::google::cloud::storage_experimental::DefaultGrpcClient;
116-
117116
int main(int argc, char* argv[]) {
118117
google::cloud::StatusOr<ThroughputOptions> options = ParseArgs(argc, argv);
119118
if (!options) {
@@ -123,12 +122,7 @@ int main(int argc, char* argv[]) {
123122

124123
auto client_options =
125124
google::cloud::Options{}.set<gcs::ProjectIdOption>(options->project_id);
126-
auto rest_client = gcs::Client(client_options);
127-
#if GOOGLE_CLOUD_CPP_STORAGE_HAVE_GRPC
128-
auto grpc_client = DefaultGrpcClient(client_options);
129-
#else
130-
auto grpc_client = rest_client;
131-
#endif // GOOGLE_CLOUD_CPP_STORAGE_HAVE_GRPC
125+
auto client = gcs::Client(client_options);
132126

133127
auto generator = google::cloud::internal::DefaultPRNG(std::random_device{}());
134128
auto bucket_name = gcs_bm::MakeRandomBucketName(generator);
@@ -139,8 +133,11 @@ int main(int argc, char* argv[]) {
139133
[](char c) { return c == '\n' ? ';' : c; });
140134

141135
struct Formatter {
142-
void operator()(std::string* out, ApiName api) const {
143-
out->append(gcs_bm::ToString(api));
136+
void operator()(std::string* out, ExperimentLibrary v) const {
137+
out->append(gcs_bm::ToString(v));
138+
}
139+
void operator()(std::string* out, ExperimentTransport v) const {
140+
out->append(gcs_bm::ToString(v));
144141
}
145142
void operator()(std::string* out, bool b) const {
146143
out->append(b ? "true" : "false");
@@ -179,8 +176,10 @@ int main(int argc, char* argv[]) {
179176
<< options->read_quantum / gcs_bm::kKiB
180177
<< "\n# Minimum Sample Count: " << options->minimum_sample_count
181178
<< "\n# Maximum Sample Count: " << options->maximum_sample_count
182-
<< "\n# Enabled APIs: "
183-
<< absl::StrJoin(options->enabled_apis, ",", Formatter{})
179+
<< "\n# Enabled Libs: "
180+
<< absl::StrJoin(options->libs, ",", Formatter{})
181+
<< "\n# Enabled Transports: "
182+
<< absl::StrJoin(options->transports, ",", Formatter{})
184183
<< "\n# Enabled CRC32C: "
185184
<< absl::StrJoin(options->enabled_crc32c, ",", Formatter{})
186185
<< "\n# Enabled MD5: "
@@ -189,14 +188,14 @@ int main(int argc, char* argv[]) {
189188
// Make the output generated so far immediately visible, helps with debugging.
190189
std::cout << std::flush;
191190

192-
auto meta = rest_client.CreateBucket(
193-
bucket_name,
194-
gcs::BucketMetadata()
195-
.set_storage_class(gcs::storage_class::Standard())
196-
.set_location(options->region),
197-
gcs::PredefinedAcl::ProjectPrivate(),
198-
gcs::PredefinedDefaultObjectAcl::ProjectPrivate(),
199-
gcs::Projection("full"));
191+
auto meta =
192+
client.CreateBucket(bucket_name,
193+
gcs::BucketMetadata()
194+
.set_storage_class(gcs::storage_class::Standard())
195+
.set_location(options->region),
196+
gcs::PredefinedAcl::ProjectPrivate(),
197+
gcs::PredefinedDefaultObjectAcl::ProjectPrivate(),
198+
gcs::Projection("full"));
200199
if (!meta) {
201200
std::cerr << "Error creating bucket: " << meta.status() << "\n";
202201
return 1;
@@ -205,15 +204,15 @@ int main(int argc, char* argv[]) {
205204
gcs_bm::PrintThroughputResultHeader(std::cout);
206205
std::vector<std::future<TestResults>> tasks;
207206
for (int i = 0; i != options->thread_count; ++i) {
208-
tasks.emplace_back(std::async(std::launch::async, RunThread, *options,
209-
rest_client, grpc_client, bucket_name, i));
207+
tasks.emplace_back(
208+
std::async(std::launch::async, RunThread, *options, bucket_name, i));
210209
}
211210
for (auto& f : tasks) {
212211
PrintResults(f.get());
213212
}
214213

215-
gcs_bm::DeleteAllObjects(rest_client, bucket_name, options->thread_count);
216-
auto status = rest_client.DeleteBucket(bucket_name);
214+
gcs_bm::DeleteAllObjects(client, bucket_name, options->thread_count);
215+
auto status = client.DeleteBucket(bucket_name);
217216
if (!status.ok()) {
218217
std::cerr << "# Error deleting bucket, status=" << status << "\n";
219218
return 1;
@@ -232,9 +231,30 @@ void PrintResults(TestResults const& results) {
232231
std::cout << std::flush;
233232
}
234233

235-
TestResults RunThread(ThroughputOptions const& options, gcs::Client rest_client,
236-
gcs::Client grpc_client, std::string const& bucket_name,
237-
int thread_id) {
234+
gcs_bm::ClientProvider MakeProvider(ThroughputOptions const& options) {
235+
return [=](ExperimentTransport t) {
236+
auto opts =
237+
google::cloud::Options{}.set<gcs::ProjectIdOption>(options.project_id);
238+
#if GOOGLE_CLOUD_CPP_STORAGE_HAVE_GRPC
239+
using ::google::cloud::storage_experimental::DefaultGrpcClient;
240+
if (t == ExperimentTransport::kDirectPath) {
241+
return DefaultGrpcClient(
242+
opts.set<gcs_ex::GrpcPluginOption>("media")
243+
.set<google::cloud::EndpointOption>(
244+
"google-c2p-experimental:///storage.googleapis.com"));
245+
}
246+
if (t == ExperimentTransport::kGrpc) {
247+
return DefaultGrpcClient(opts.set<gcs_ex::GrpcPluginOption>("none"));
248+
}
249+
#else
250+
(void)t; // disable unused parameter warning
251+
#endif // GOOGLE_CLOUD_CPP_STORAGE_HAVE_GRPC
252+
return gcs::Client(opts);
253+
};
254+
}
255+
256+
TestResults RunThread(ThroughputOptions const& options,
257+
std::string const& bucket_name, int thread_id) {
238258
auto generator = google::cloud::internal::DefaultPRNG(std::random_device{}());
239259

240260
google::cloud::StatusOr<gcs::ClientOptions> client_options =
@@ -247,16 +267,17 @@ TestResults RunThread(ThroughputOptions const& options, gcs::Client rest_client,
247267
auto const upload_buffer_size = client_options->upload_buffer_size();
248268
auto const download_buffer_size = client_options->download_buffer_size();
249269

250-
auto uploaders =
251-
gcs_bm::CreateUploadExperiments(options, rest_client, grpc_client, {});
270+
auto provider = MakeProvider(options);
271+
272+
auto uploaders = gcs_bm::CreateUploadExperiments(options, provider);
252273
if (uploaders.empty()) {
253274
// This is possible if only gRPC is requested but the benchmark was compiled
254275
// without gRPC support.
255276
std::cout << "# None of the APIs configured are available\n";
256277
return {};
257278
}
258-
auto downloaders = gcs_bm::CreateDownloadExperiments(
259-
options, rest_client, std::move(grpc_client), {}, thread_id);
279+
auto downloaders =
280+
gcs_bm::CreateDownloadExperiments(options, provider, thread_id);
260281
if (downloaders.empty()) {
261282
// This is possible if only gRPC is requested but the benchmark was compiled
262283
// without gRPC support.
@@ -284,9 +305,6 @@ TestResults RunThread(ThroughputOptions const& options, gcs::Client rest_client,
284305
0, options.enabled_crc32c.size() - 1);
285306
std::bernoulli_distribution use_insert;
286307

287-
std::uniform_int_distribution<std::size_t> api_generator(
288-
0, options.enabled_apis.size() - 1);
289-
290308
auto deadline = std::chrono::steady_clock::now() + options.duration;
291309

292310
TestResults results;
@@ -302,7 +320,6 @@ TestResults RunThread(ThroughputOptions const& options, gcs::Client rest_client,
302320
auto read_size = options.read_quantum * read_size_generator(generator);
303321
bool const enable_crc = options.enabled_crc32c[crc32c_generator(generator)];
304322
bool const enable_md5 = options.enabled_md5[md5_generator(generator)];
305-
auto const api = options.enabled_apis[api_generator(generator)];
306323

307324
auto& uploader = uploaders[uploader_generator(generator)];
308325
auto upload_result =
@@ -315,8 +332,7 @@ TestResults RunThread(ThroughputOptions const& options, gcs::Client rest_client,
315332

316333
if (!status.ok()) {
317334
if (options.thread_count == 1) {
318-
std::cout << "# status=" << status << ", api=" << gcs_bm::ToString(api)
319-
<< "\n";
335+
std::cout << "# status=" << status << "\n";
320336
}
321337
continue;
322338
}
@@ -334,8 +350,8 @@ TestResults RunThread(ThroughputOptions const& options, gcs::Client rest_client,
334350
PrintResults(results);
335351
results.clear();
336352
}
337-
338-
(void)rest_client.DeleteObject(bucket_name, object_name);
353+
auto client = provider(ExperimentTransport::kJson);
354+
(void)client.DeleteObject(bucket_name, object_name);
339355
}
340356
return results;
341357
}
@@ -371,7 +387,7 @@ google::cloud::StatusOr<ThroughputOptions> SelfTest(char const* argv0) {
371387
"--duration=1s",
372388
"--minimum-sample-count=4",
373389
"--maximum-sample-count=10",
374-
"--enabled-apis=JSON,XML",
390+
"--enabled-transports=Json,Xml",
375391
"--enabled-crc32c=enabled",
376392
"--enabled-md5=disabled",
377393
},

0 commit comments

Comments
 (0)