From 55f0da0fe50790cb53dc94193b2ba21ef0e363e6 Mon Sep 17 00:00:00 2001 From: Carlos O'Ryan Date: Wed, 29 Jun 2022 15:08:50 -0700 Subject: [PATCH] test(storage): benchmark supports ranged reads (#9378) A lot of analytical workloads use "ranged" reads, i.e., they read a portion of a larger object. There seems to be performance advantages when reading up to 2MiB. Likewise, there are measurable performance differences when the offset is on a 2MiB boundary and/or the read size is on a 2 MiB boundary. We would like to measure these effects, and therefore we need new parameters for the benchmark. By now, we have 4 of these "quantized" size ranges, I did some refactoring to avoid code duplication. --- .../storage_throughput_vs_cpu_benchmark.cc | 157 +++++++++++------- .../benchmarks/throughput_experiment.cc | 12 +- .../benchmarks/throughput_experiment.h | 4 + .../benchmarks/throughput_experiment_test.cc | 14 +- .../storage/benchmarks/throughput_options.cc | 96 +++++++---- .../storage/benchmarks/throughput_options.h | 6 + .../benchmarks/throughput_options_test.cc | 82 +++++++-- .../storage/benchmarks/throughput_result.cc | 5 +- .../storage/benchmarks/throughput_result.h | 3 + .../benchmarks/throughput_result_test.cc | 6 +- 10 files changed, 277 insertions(+), 108 deletions(-) diff --git a/google/cloud/storage/benchmarks/storage_throughput_vs_cpu_benchmark.cc b/google/cloud/storage/benchmarks/storage_throughput_vs_cpu_benchmark.cc index ff20d971143b4..b9ce17412e560 100644 --- a/google/cloud/storage/benchmarks/storage_throughput_vs_cpu_benchmark.cc +++ b/google/cloud/storage/benchmarks/storage_throughput_vs_cpu_benchmark.cc @@ -25,6 +25,7 @@ #include "google/cloud/internal/getenv.h" #include "google/cloud/internal/random.h" #include "google/cloud/log.h" +#include "absl/time/time.h" #include #include #include @@ -149,52 +150,66 @@ int main(int argc, char* argv[]) { } }; + auto output_size_range = [](std::string const& name, auto minimum, + auto maximum) { + std::cout << "\n# " << name << " Range: [" << minimum << ',' << maximum + << ']'; + }; + + auto output_quantized_range = [](std::string const& name, auto minimum, + auto maximum, auto quantum) { + std::cout << "\n# " << name << " Range: [" << minimum << ',' << maximum + << "]\n# " << name << " Quantum: " << quantum; + }; + std::cout << "# Running test on bucket: " << bucket_name << "\n# Start time: " << google::cloud::internal::FormatRfc3339( std::chrono::system_clock::now()) - << "\n# Region: " << options->region - << "\n# Duration: " << options->duration.count() << "s" + << "\n# Region: " << options->region << "\n# Duration: " + << absl::FormatDuration(absl::FromChrono(options->duration)) << "\n# Thread Count: " << options->thread_count << "\n# Client Per Thread: " << options->client_per_thread << "\n# gRPC Channel Count: " << options->grpc_channel_count << "\n# DirectPath Channel Count: " - << options->direct_path_channel_count << "\n# Object Size Range: [" - << options->minimum_object_size << "," - << options->maximum_object_size << "]\n# Write Buffer Size Range: [" - << options->minimum_write_buffer_size << "," - << options->maximum_write_buffer_size - << "]\n# Write Buffer Quantum: " << options->write_buffer_quantum - << "\n# Read Buffer Size Range: [" - << options->minimum_read_buffer_size << "," - << options->maximum_read_buffer_size - << "]\n# Read Buffer Quantum: " << options->read_buffer_quantum - << "\n# Object Buffer Size Range (MiB): [" - << options->minimum_object_size / gcs_bm::kMiB << "," - << options->maximum_object_size / gcs_bm::kMiB - << "]\n# Write Buffer Size Range (KiB): [" - << options->minimum_write_buffer_size / gcs_bm::kKiB << "," - << options->maximum_write_buffer_size / gcs_bm::kKiB - << "]\n# Write Buffer Quantum (KiB): " - << options->write_buffer_quantum / gcs_bm::kKiB - << "\n# Read Buffer Size Range (KiB): [" - << options->minimum_read_buffer_size / gcs_bm::kKiB << "," - << options->maximum_read_buffer_size / gcs_bm::kKiB - << "]\n# Read Buffer Quantum (KiB): " - << options->read_buffer_quantum / gcs_bm::kKiB - << "\n# Minimum Sample Count: " << options->minimum_sample_count - << "\n# Maximum Sample Count: " << options->maximum_sample_count - << "\n# Enabled Libs: " - << absl::StrJoin(options->libs, ",", Formatter{}) - << "\n# Enabled Transports: " - << absl::StrJoin(options->transports, ",", Formatter{}) - << "\n# Enabled CRC32C: " - << absl::StrJoin(options->enabled_crc32c, ",", Formatter{}) - << "\n# Enabled MD5: " - << absl::StrJoin(options->enabled_md5, ",", Formatter{}) - << "\n# REST Endpoint: " << options->rest_endpoint - << "\n# Grpc Endpoint: " << options->grpc_endpoint - << "\n# Direct Path Endpoint: " << options->direct_path_endpoint - << "\n# Build info: " << notes << "\n"; + << options->direct_path_channel_count; + + output_size_range("Object Size", options->minimum_object_size, + options->maximum_object_size); + output_quantized_range( + "Write Buffer Size", options->minimum_write_buffer_size, + options->maximum_write_buffer_size, options->write_buffer_quantum); + output_quantized_range("Read Buffer Size", options->minimum_read_buffer_size, + options->maximum_read_buffer_size, + options->read_buffer_quantum); + + std::cout + << "\n# Minimum Sample Count: " << options->minimum_sample_count + << "\n# Maximum Sample Count: " << options->maximum_sample_count + << "\n# Enabled Libs: " << absl::StrJoin(options->libs, ",", Formatter{}) + << "\n# Enabled Transports: " + << absl::StrJoin(options->transports, ",", Formatter{}) + << "\n# Enabled CRC32C: " + << absl::StrJoin(options->enabled_crc32c, ",", Formatter{}) + << "\n# Enabled MD5: " + << absl::StrJoin(options->enabled_md5, ",", Formatter{}) + << "\n# REST Endpoint: " << options->rest_endpoint + << "\n# Grpc Endpoint: " << options->grpc_endpoint + << "\n# Direct Path Endpoint: " << options->direct_path_endpoint + << "\n# Transfer Stall Timeout: " + << absl::FormatDuration(absl::FromChrono(options->transfer_stall_timeout)) + << "\n# Download Stall Timeout: " + << absl::FormatDuration(absl::FromChrono(options->download_stall_timeout)) + << "\n# Minimum Sample Delay: " + << absl::FormatDuration(absl::FromChrono(options->minimum_sample_delay)); + + output_quantized_range("Read Offset", options->minimum_read_offset, + options->maximum_read_offset, + options->read_offset_quantum); + output_quantized_range("Read Size", options->minimum_read_size, + options->maximum_read_size, + options->read_size_quantum); + + std::cout << "\n# Build info: " << notes << "\n"; // Make the output generated so far immediately visible, helps with debugging. std::cout << std::flush; @@ -331,18 +346,45 @@ void RunThread(ThroughputOptions const& options, std::string const& bucket_name, std::uniform_int_distribution size_generator( options.minimum_object_size, options.maximum_object_size); - std::uniform_int_distribution write_buffer_size_generator( - options.minimum_write_buffer_size / options.write_buffer_quantum, - options.maximum_write_buffer_size / options.write_buffer_quantum); - std::uniform_int_distribution read_buffer_size_generator( - options.minimum_read_buffer_size / options.read_buffer_quantum, - options.maximum_read_buffer_size / options.read_buffer_quantum); + + auto quantized_range_generator = [](auto minimum, auto maximum, + auto quantum) { + auto distribution = std::uniform_int_distribution( + minimum / quantum, maximum / quantum); + return [d = std::move(distribution), quantum](auto& g) mutable { + return quantum * d(g); + }; + }; + + auto write_buffer_size_generator = quantized_range_generator( + options.minimum_write_buffer_size, options.maximum_write_buffer_size, + options.write_buffer_quantum); + auto read_buffer_size_generator = quantized_range_generator( + options.minimum_read_buffer_size, options.maximum_read_buffer_size, + options.read_buffer_quantum); + auto read_offset_generator = quantized_range_generator( + options.minimum_read_offset, options.maximum_read_offset, + options.read_offset_quantum); + auto read_size_generator = quantized_range_generator( + options.minimum_read_size, options.maximum_read_size, + options.read_size_quantum); + + auto const read_range_enabled = + options.minimum_read_size != options.maximum_read_size; + auto read_range_generator = [&](auto& g, std::int64_t object_size) + -> absl::optional> { + if (!read_range_enabled || !std::bernoulli_distribution{}(g)) { + return absl::nullopt; + } + auto offset = (std::min)(object_size, read_offset_generator(g)); + auto size = (std::min)(object_size - offset, read_size_generator(g)); + return std::make_pair(offset, size); + }; std::uniform_int_distribution crc32c_generator( 0, options.enabled_crc32c.size() - 1); std::uniform_int_distribution md5_generator( 0, options.enabled_crc32c.size() - 1); - std::bernoulli_distribution use_insert; auto deadline = std::chrono::steady_clock::now() + options.duration; @@ -351,21 +393,20 @@ void RunThread(ThroughputOptions const& options, std::string const& bucket_name, iteration_count < options.maximum_sample_count && (iteration_count < options.minimum_sample_count || start < deadline); start = std::chrono::steady_clock::now(), ++iteration_count) { - auto object_name = gcs_bm::MakeRandomObjectName(generator); - auto object_size = size_generator(generator); - auto write_buffer_size = - options.write_buffer_quantum * write_buffer_size_generator(generator); - auto read_buffer_size = - options.read_buffer_quantum * read_buffer_size_generator(generator); + auto const object_name = gcs_bm::MakeRandomObjectName(generator); + auto const object_size = size_generator(generator); + auto const write_buffer_size = write_buffer_size_generator(generator); + auto const read_buffer_size = read_buffer_size_generator(generator); bool const enable_crc = options.enabled_crc32c[crc32c_generator(generator)]; bool const enable_md5 = options.enabled_md5[md5_generator(generator)]; + auto const range = read_range_generator(generator, object_size); auto& uploader = uploaders[uploader_generator(generator)]; - auto upload_result = - uploader->Run(bucket_name, object_name, - gcs_bm::ThroughputExperimentConfig{ - gcs_bm::kOpWrite, object_size, write_buffer_size, - enable_crc, enable_md5}); + auto upload_result = uploader->Run( + bucket_name, object_name, + gcs_bm::ThroughputExperimentConfig{ + gcs_bm::kOpWrite, object_size, write_buffer_size, enable_crc, + enable_md5, /*read_range=*/absl::nullopt}); auto status = upload_result.status; handler(std::move(upload_result)); @@ -376,7 +417,7 @@ void RunThread(ThroughputOptions const& options, std::string const& bucket_name, handler(downloader->Run( bucket_name, object_name, gcs_bm::ThroughputExperimentConfig{op, object_size, read_buffer_size, - enable_crc, enable_md5})); + enable_crc, enable_md5, range})); } auto client = provider(ExperimentTransport::kJson); (void)client.DeleteObject(bucket_name, object_name); diff --git a/google/cloud/storage/benchmarks/throughput_experiment.cc b/google/cloud/storage/benchmarks/throughput_experiment.cc index 546ab5d838773..0c28a4f298862 100644 --- a/google/cloud/storage/benchmarks/throughput_experiment.cc +++ b/google/cloud/storage/benchmarks/throughput_experiment.cc @@ -85,6 +85,7 @@ class UploadObject : public ThroughputExperiment { kOpInsert, start, config.object_size, + /*transfer_offset=*/0, config.object_size, config.app_buffer_size, config.enable_crc32c, @@ -121,6 +122,7 @@ class UploadObject : public ThroughputExperiment { kOpWrite, start, config.object_size, + /*transfer_offset=*/0, config.object_size, config.app_buffer_size, config.enable_crc32c, @@ -163,8 +165,13 @@ class DownloadObject : public ThroughputExperiment { auto const start = std::chrono::system_clock::now(); auto timer = Timer::PerThread(); + auto const offset = config.read_range.value_or(std::make_pair(0, 0)).first; + auto read_range = + config.read_range.has_value() + ? gcs::ReadRange(offset, offset + config.read_range->second) + : gcs::ReadRange(); auto reader = client_.ReadObject( - bucket_name, object_name, + bucket_name, object_name, read_range, gcs::DisableCrc32cChecksum(!config.enable_crc32c), gcs::DisableMD5Hash(!config.enable_md5), api_selector); std::int64_t transfer_size = 0; @@ -180,6 +187,7 @@ class DownloadObject : public ThroughputExperiment { config.op, start, config.object_size, + offset, transfer_size, config.app_buffer_size, config.enable_crc32c, @@ -274,6 +282,7 @@ class DownloadObjectLibcurl : public ThroughputExperiment { config.op, start, config.object_size, + /*transfer_offset=*/0, config.object_size, config.app_buffer_size, config.enable_crc32c, @@ -344,6 +353,7 @@ class DownloadObjectRawGrpc : public ThroughputExperiment { config.op, start, config.object_size, + /*transfer_offset=*/0, bytes_received, config.app_buffer_size, /*crc_enabled=*/false, diff --git a/google/cloud/storage/benchmarks/throughput_experiment.h b/google/cloud/storage/benchmarks/throughput_experiment.h index 065836d0dd51d..04b8aa4ab6d94 100644 --- a/google/cloud/storage/benchmarks/throughput_experiment.h +++ b/google/cloud/storage/benchmarks/throughput_experiment.h @@ -18,9 +18,12 @@ #include "google/cloud/storage/benchmarks/benchmark_utils.h" #include "google/cloud/storage/benchmarks/throughput_options.h" #include "google/cloud/storage/benchmarks/throughput_result.h" +#include "absl/types/optional.h" +#include #include #include #include +#include #include namespace google { @@ -33,6 +36,7 @@ struct ThroughputExperimentConfig { std::size_t app_buffer_size; bool enable_crc32c; bool enable_md5; + absl::optional> read_range; }; /** diff --git a/google/cloud/storage/benchmarks/throughput_experiment_test.cc b/google/cloud/storage/benchmarks/throughput_experiment_test.cc index 8c4977923b5e6..55ccfa660b6ef 100644 --- a/google/cloud/storage/benchmarks/throughput_experiment_test.cc +++ b/google/cloud/storage/benchmarks/throughput_experiment_test.cc @@ -66,9 +66,10 @@ TEST_P(ThroughputExperimentIntegrationTest, Upload) { auto experiments = CreateUploadExperiments(options, provider); for (auto& e : experiments) { auto object_name = MakeRandomObjectName(); - ThroughputExperimentConfig config{OpType::kOpInsert, 16 * kKiB, 1 * kMiB, - /*enable_crc32c=*/false, - /*enable_md5=*/false}; + ThroughputExperimentConfig config{ + OpType::kOpInsert, 16 * kKiB, 1 * kMiB, + /*enable_crc32c=*/false, + /*enable_md5=*/false, absl::nullopt}; auto result = e->Run(bucket_name_, object_name, config); ASSERT_STATUS_OK(result.status); auto status = client->DeleteObject(bucket_name_, object_name); @@ -95,9 +96,12 @@ TEST_P(ThroughputExperimentIntegrationTest, Download) { auto object_name = MakeRandomObjectName(); auto constexpr kObjectSize = 16 * kKiB; - ThroughputExperimentConfig config{OpType::kOpRead0, kObjectSize, 1 * kMiB, + ThroughputExperimentConfig config{OpType::kOpRead0, + kObjectSize, + 1 * kMiB, /*enable_crc32c=*/false, - /*enable_md5=*/false}; + /*enable_md5=*/false, + std::make_pair(128 * kKiB, 256 * kKiB)}; auto contents = MakeRandomData(kObjectSize); auto insert = diff --git a/google/cloud/storage/benchmarks/throughput_options.cc b/google/cloud/storage/benchmarks/throughput_options.cc index 45fb9f599d1d9..b6925f270c2e4 100644 --- a/google/cloud/storage/benchmarks/throughput_options.cc +++ b/google/cloud/storage/benchmarks/throughput_options.cc @@ -20,6 +20,28 @@ namespace google { namespace cloud { namespace storage_benchmarks { +namespace { + +Status ValidateQuantizedRange(std::string const& name, std::int64_t minimum, + std::int64_t maximum, std::int64_t quantum) { + using ::google::cloud::StatusCode; + if (minimum > maximum) { + std::ostringstream os; + os << "Invalid range for " << name << " [" << minimum << ',' << maximum + << "]"; + return google::cloud::Status{StatusCode::kInvalidArgument, os.str()}; + } + if (quantum <= 0 || (quantum > minimum && minimum != 0)) { + std::ostringstream os; + os << "Invalid quantum for " << name << " (" << quantum + << "), it should be in the (0," << minimum << "] range"; + return google::cloud::Status{StatusCode::kInvalidArgument, os.str()}; + } + + return Status{}; +} + +} // namespace using ::google::cloud::testing_util::OptionDescriptor; @@ -191,6 +213,32 @@ google::cloud::StatusOr ParseThroughputOptions( options.minimum_sample_delay = std::chrono::milliseconds(-1); } }}, + + {"--minimum-read-offset", "configure the minimum offset of ranged reads", + [&options](std::string const& val) { + options.minimum_read_offset = ParseBufferSize(val); + }}, + {"--maximum-read-offset", "configure the maximum offset for ranged reads", + [&options](std::string const& val) { + options.maximum_read_offset = ParseBufferSize(val); + }}, + {"--read-offset-quantum", "quantize the ranged read offsets", + [&options](std::string const& val) { + options.read_offset_quantum = ParseBufferSize(val); + }}, + + {"--minimum-read-size", "configure the minimum size of ranged reads", + [&options](std::string const& val) { + options.minimum_read_size = ParseBufferSize(val); + }}, + {"--maximum-read-size", "configure the maximum size for ranged reads", + [&options](std::string const& val) { + options.maximum_read_size = ParseBufferSize(val); + }}, + {"--read-size-quantum", "quantize the ranged read sizes", + [&options](std::string const& val) { + options.read_size_quantum = ParseBufferSize(val); + }}, }; auto usage = BuildUsage(desc, argv[0]); @@ -243,37 +291,15 @@ google::cloud::StatusOr ParseThroughputOptions( return make_status(os); } - if (options.minimum_write_buffer_size > options.maximum_write_buffer_size) { - std::ostringstream os; - os << "Invalid range for write buffer size [" - << options.minimum_write_buffer_size << ',' - << options.maximum_write_buffer_size << "]"; - return make_status(os); - } - if (options.write_buffer_quantum <= 0 || - options.write_buffer_quantum > options.minimum_write_buffer_size) { - std::ostringstream os; - os << "Invalid value for --write-buffer-quantum (" - << options.write_buffer_quantum << "), it should be in the [1," - << options.minimum_write_buffer_size << "] range"; - return make_status(os); - } + auto status = ValidateQuantizedRange( + "write buffer size", options.minimum_write_buffer_size, + options.maximum_write_buffer_size, options.write_buffer_quantum); + if (!status.ok()) return status; - if (options.minimum_read_buffer_size > options.maximum_read_buffer_size) { - std::ostringstream os; - os << "Invalid range for read buffer size [" - << options.minimum_read_buffer_size << ',' - << options.maximum_read_buffer_size << "]"; - return make_status(os); - } - if (options.read_buffer_quantum <= 0 || - options.read_buffer_quantum > options.minimum_read_buffer_size) { - std::ostringstream os; - os << "Invalid value for --read-buffer-quantum (" - << options.read_buffer_quantum << "), it should be in the [1," - << options.minimum_read_buffer_size << "] range"; - return make_status(os); - } + status = ValidateQuantizedRange( + "read buffer size", options.minimum_read_buffer_size, + options.maximum_read_buffer_size, options.read_buffer_quantum); + if (!status.ok()) return status; if (options.minimum_sample_count > options.maximum_sample_count) { std::ostringstream os; @@ -331,6 +357,16 @@ google::cloud::StatusOr ParseThroughputOptions( return make_status(os); } + status = ValidateQuantizedRange("read offset", options.minimum_read_offset, + options.maximum_read_offset, + options.read_offset_quantum); + if (!status.ok()) return status; + + status = ValidateQuantizedRange("read size", options.minimum_read_size, + options.maximum_read_size, + options.read_size_quantum); + if (!status.ok()) return status; + return options; } diff --git a/google/cloud/storage/benchmarks/throughput_options.h b/google/cloud/storage/benchmarks/throughput_options.h index ed49e3e0b3a5f..95721c3817046 100644 --- a/google/cloud/storage/benchmarks/throughput_options.h +++ b/google/cloud/storage/benchmarks/throughput_options.h @@ -60,6 +60,12 @@ struct ThroughputOptions { std::chrono::seconds transfer_stall_timeout{}; std::chrono::seconds download_stall_timeout{}; std::chrono::milliseconds minimum_sample_delay{}; + std::int64_t minimum_read_offset = 0; + std::int64_t maximum_read_offset = 0; + std::int64_t read_offset_quantum = 128 * kKiB; + std::int64_t minimum_read_size = 0; + std::int64_t maximum_read_size = 0; + std::int64_t read_size_quantum = 128 * kKiB; }; google::cloud::StatusOr ParseThroughputOptions( diff --git a/google/cloud/storage/benchmarks/throughput_options_test.cc b/google/cloud/storage/benchmarks/throughput_options_test.cc index ca43776bc35bb..036591c6ed287 100644 --- a/google/cloud/storage/benchmarks/throughput_options_test.cc +++ b/google/cloud/storage/benchmarks/throughput_options_test.cc @@ -56,6 +56,12 @@ TEST(ThroughputOptions, Basic) { "--transfer-stall-timeout=86400s", "--download-stall-timeout=86401s", "--minimum-sample-delay=250ms", + "--minimum-read-offset=32KiB", + "--maximum-read-offset=48KiB", + "--read-offset-quantum=8KiB", + "--minimum-read-size=48KiB", + "--maximum-read-size=64KiB", + "--read-size-quantum=16KiB", }); ASSERT_STATUS_OK(options); EXPECT_EQ("test-project", options->project_id); @@ -154,6 +160,34 @@ TEST(ThroughputOptions, Transports) { ExperimentTransport::kJson)); } +TEST(ThroughputOptions, Readoffset) { + auto options = ParseThroughputOptions({ + "self-test", + "--region=r", + "--minimum-read-offset=0", + "--maximum-read-offset=2MiB", + "--read-offset-quantum=128KiB", + }); + ASSERT_STATUS_OK(options); + EXPECT_EQ(options->minimum_read_offset, 0); + EXPECT_EQ(options->maximum_read_offset, 2 * kMiB); + EXPECT_EQ(options->read_offset_quantum, 128 * kKiB); +} + +TEST(ThroughputOptions, ReadSize) { + auto options = ParseThroughputOptions({ + "self-test", + "--region=r", + "--minimum-read-size=0", + "--maximum-read-size=2MiB", + "--read-size-quantum=128KiB", + }); + ASSERT_STATUS_OK(options); + EXPECT_EQ(options->minimum_read_size, 0); + EXPECT_EQ(options->maximum_read_size, 2 * kMiB); + EXPECT_EQ(options->read_size_quantum, 128 * kKiB); +} + TEST(ThroughputOptions, Validate) { EXPECT_FALSE(ParseThroughputOptions({"self-test"})); EXPECT_FALSE(ParseThroughputOptions({"self-test", "unused-1", "unused-2"})); @@ -176,28 +210,28 @@ TEST(ThroughputOptions, Validate) { EXPECT_FALSE(ParseThroughputOptions({ "self-test", "--region=r", - "--minimum-write-size=8", - "--maximum-write-size=4", + "--minimum-write-buffer-size=8", + "--maximum-write-buffer-size=4", })); EXPECT_FALSE(ParseThroughputOptions({ "self-test", "--region=r", - "--minimum-write-size=4", - "--maximum-write-size=8", - "--write-quantum=5", + "--minimum-write-buffer-size=4", + "--maximum-write-buffer-size=8", + "--write-buffer-quantum=5", })); EXPECT_FALSE(ParseThroughputOptions({ "self-test", "--region=r", - "--minimum-read-size=8", - "--maximum-read-size=4", + "--minimum-read-buffer-size=8", + "--maximum-read-buffer-size=4", })); EXPECT_FALSE(ParseThroughputOptions({ "self-test", "--region=r", - "--minimum-read-size=4", - "--maximum-read-size=8", - "--read-quantum=5", + "--minimum-read-buffer-size=4", + "--maximum-read-buffer-size=8", + "--read-buffer-quantum=5", })); EXPECT_FALSE(ParseThroughputOptions({ "self-test", @@ -225,6 +259,34 @@ TEST(ThroughputOptions, Validate) { "--region=r", "--minimum-sample-delay=-1ms", })); + + EXPECT_FALSE(ParseThroughputOptions({ + "self-test", + "--region=r", + "--minimum-read-offset=8", + "--maximum-read-offset=4", + })); + EXPECT_FALSE(ParseThroughputOptions({ + "self-test", + "--region=r", + "--minimum-read-offset=4", + "--maximum-read-offset=8", + "--read-offset-quantum=5", + })); + + EXPECT_FALSE(ParseThroughputOptions({ + "self-test", + "--region=r", + "--minimum-read-size=8", + "--maximum-read-size=4", + })); + EXPECT_FALSE(ParseThroughputOptions({ + "self-test", + "--region=r", + "--minimum-read-size=4", + "--maximum-read-size=8", + "--read-size-quantum=5", + })); } } // namespace diff --git a/google/cloud/storage/benchmarks/throughput_result.cc b/google/cloud/storage/benchmarks/throughput_result.cc index e7dfbd237882f..d90feba3d82d2 100644 --- a/google/cloud/storage/benchmarks/throughput_result.cc +++ b/google/cloud/storage/benchmarks/throughput_result.cc @@ -43,6 +43,7 @@ void PrintAsCsv(std::ostream& os, ThroughputResult const& r) { << ',' << ToString(r.op) // << ',' << start // << ',' << r.object_size // + << ',' << r.transfer_offset // << ',' << r.transfer_size // << ',' << r.app_buffer_size // << ',' << r.crc_enabled // @@ -57,8 +58,8 @@ void PrintAsCsv(std::ostream& os, ThroughputResult const& r) { } void PrintThroughputResultHeader(std::ostream& os) { - os << "Library,Transport,Op,Start,ObjectSize,TransferSize,AppBufferSize" - << ",Crc32cEnabled,MD5Enabled" + os << "Library,Transport,Op,Start,ObjectSize,TransferOffset,TransferSize" + << ",AppBufferSize,Crc32cEnabled,MD5Enabled" << ",ElapsedTimeUs,CpuTimeUs,Peer,Notes,StatusCode,Status\n"; } diff --git a/google/cloud/storage/benchmarks/throughput_result.h b/google/cloud/storage/benchmarks/throughput_result.h index b3dd478bd46b9..7ac5c15e69c88 100644 --- a/google/cloud/storage/benchmarks/throughput_result.h +++ b/google/cloud/storage/benchmarks/throughput_result.h @@ -68,6 +68,9 @@ struct ThroughputResult { /// The total size of the object involved in this experiment. Currently also /// represents the number of bytes transferred. std::int64_t object_size; + /// The beginning offset of the transfer. Only meaningful for downloads as it + /// it is always 0 for uploads + std::int64_t transfer_offset; /// The size of the transfer. For uploads this is always equal to the object /// size. For downloads this can be smaller than the object size. std::int64_t transfer_size; diff --git a/google/cloud/storage/benchmarks/throughput_result_test.cc b/google/cloud/storage/benchmarks/throughput_result_test.cc index 3c5516b1ae1e0..2ae5b77dfb910 100644 --- a/google/cloud/storage/benchmarks/throughput_result_test.cc +++ b/google/cloud/storage/benchmarks/throughput_result_test.cc @@ -74,7 +74,8 @@ TEST(ThroughputResult, HeaderMatches) { auto const line = ToString(ThroughputResult{ ExperimentLibrary::kCppClient, ExperimentTransport::kGrpc, kOpInsert, std::chrono::system_clock::now(), - /*object_size=*/5 * kMiB, /*transfer_size=*/3 * kMiB, + /*object_size=*/8 * kMiB, /*transfer_offset=*/4 * kMiB, + /*transfer_size=*/3 * kMiB, /*app_buffer_size=*/2 * kMiB, /*crc_enabled=*/true, /*md5_enabled=*/false, std::chrono::microseconds(234000), std::chrono::microseconds(345000), @@ -95,7 +96,8 @@ TEST(ThroughputResult, HeaderMatches) { EXPECT_THAT(*line, HasSubstr(ToString(ExperimentLibrary::kCppClient))); EXPECT_THAT(*line, HasSubstr(ToString(ExperimentTransport::kGrpc))); EXPECT_THAT(*line, HasSubstr(ToString(kOpInsert))); - EXPECT_THAT(*line, HasSubstr("," + std::to_string(5 * kMiB) + ",")); + EXPECT_THAT(*line, HasSubstr("," + std::to_string(8 * kMiB) + ",")); + EXPECT_THAT(*line, HasSubstr("," + std::to_string(4 * kMiB) + ",")); EXPECT_THAT(*line, HasSubstr("," + std::to_string(3 * kMiB) + ",")); EXPECT_THAT(*line, HasSubstr("," + std::to_string(2 * kMiB) + ",")); EXPECT_THAT(*line, HasSubstr(",1,")); // crc_enabled==true