blob: 0a48a917343a387a256835544b0aacffdc22c998 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "benchmark/benchmark_suite.h"
#include <atomic>
#include <cstdint>
#include <iostream>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "arrow/api.h"
#include "arrow/c/bridge.h"
#include "arrow/io/api.h"
#include "benchmark/benchmark_helpers.h"
#include "benchmark/cli_option_parsing.h"
#include "paimon/api.h"
#include "paimon/catalog/catalog.h"
#include "paimon/testing/utils/testharness.h"
#if __has_include("parquet/arrow/reader.h")
#include "parquet/arrow/reader.h"
#include "parquet/file_reader.h"
#define PAIMON_BENCHMARK_HAS_PARQUET_READER 1
#else
#define PAIMON_BENCHMARK_HAS_PARQUET_READER 0
#endif
namespace paimon::benchmark {
namespace {
constexpr int64_t kSourceBatchMaxRows = 4096;
constexpr int32_t kRowToBatchThreadNumber = 3;
struct BenchmarkCliOptions {
std::string source_data_file;
std::string source_table_path;
std::vector<std::string> pk_columns;
std::vector<std::pair<std::string, std::string>> extra_options;
};
struct SourceDataSpec {
std::string format;
std::string path;
};
BenchmarkCliOptions& MutableBenchmarkCliOptions() {
static BenchmarkCliOptions options;
return options;
}
const BenchmarkCliOptions& GetBenchmarkCliOptions() {
return MutableBenchmarkCliOptions();
}
Status ParsePaimonBenchmarkCliArgsImpl(int32_t* argc, char** argv) {
auto& options = MutableBenchmarkCliOptions();
options = BenchmarkCliOptions{};
const int32_t parsed_argc = *argc;
int32_t write_index = 1;
for (int32_t arg_index = 1; arg_index < parsed_argc; ++arg_index) {
const std::string arg(argv[arg_index]);
PAIMON_ASSIGN_OR_RAISE(bool is_parsed,
paimon::benchmark::ParseStringOptionArg(
parsed_argc, argv, arg, "--paimon_source_data_file", &arg_index,
&options.source_data_file));
if (is_parsed) {
continue;
}
PAIMON_ASSIGN_OR_RAISE(is_parsed, paimon::benchmark::ParseStringOptionArg(
parsed_argc, argv, arg, "--paimon_source_table_path",
&arg_index, &options.source_table_path));
if (is_parsed) {
continue;
}
PAIMON_ASSIGN_OR_RAISE(is_parsed, paimon::benchmark::ParseCommaSeparatedOptionArg(
parsed_argc, argv, arg, "--paimon_pk_columns",
&arg_index, &options.pk_columns));
if (is_parsed) {
continue;
}
PAIMON_ASSIGN_OR_RAISE(is_parsed, paimon::benchmark::ParseDelimitedRepeatableOptionArg(
parsed_argc, argv, arg, "--paimon_option", &arg_index,
&options.extra_options));
if (is_parsed) {
continue;
}
argv[write_index++] = argv[arg_index];
}
*argc = write_index;
argv[write_index] = nullptr;
return Status::OK();
}
bool HasHelpFlagImpl(int32_t argc, char** argv) {
for (int32_t arg_index = 1; arg_index < argc; ++arg_index) {
const std::string arg(argv[arg_index]);
if (arg == "-h" || arg == "--help" || arg == "--help=true") {
return true;
}
}
return false;
}
void PrintPaimonBenchmarkCliHelpImpl() {
std::cout
<< "Paimon benchmark custom options:\n"
<< " --paimon_source_data_file=<path>\n"
<< " Required. External source data file used to build benchmark data.\n"
<< " Currently supports Parquet source files.\n"
<< " Also supports: --paimon_source_data_file <path>\n"
<< " --paimon_source_table_path=<path>\n"
<< " Optional for BM_Read and BM_MOR_Read. If set, read directly from existing\n"
<< " table path and skip source file loading and pre-write stage.\n"
<< " Also supports: --paimon_source_table_path <path>\n"
<< " --paimon_pk_columns=<col1,col2,...>\n"
<< " Required by BM_PK_Write and BM_MOR_Read.\n"
<< " Also supports: --paimon_pk_columns <col1,col2,...>\n"
<< " --paimon_option=<key1>:<value1>;<key2>:<value2>\n"
<< " Optional and repeatable. Pass through table options as-is.\n"
<< " Default table file format is parquet; use file.format:<format> to override.\n"
<< " Also supports: --paimon_option <key1>:<value1>;<key2>:<value2>\n"
<< " Note: use quotes in shell, e.g. \"--paimon_option k1:v1;k2:v2\".\n"
<< "\n"
<< "Example:\n"
<< " paimon-read-write-benchmark --paimon_source_data_file /path/data.parquet \\\n"
<< " --paimon_pk_columns=id --paimon_option \"read.batch-size:8192\" \\\n"
<< " --benchmark_filter=BM_Read\n"
<< std::endl;
}
Result<std::unique_ptr<paimon::test::UniqueTestDirectory>> CreateBenchmarkWorkspace() {
auto workspace = paimon::test::UniqueTestDirectory::Create();
if (workspace == nullptr) {
return Status::Invalid("failed to create benchmark workspace");
}
return workspace;
}
uint64_t NextTableId() {
static std::atomic<uint64_t> id{0};
return ++id;
}
std::string RequirePath(const std::string& root_path, const std::string& db_name,
const std::string& table_name) {
return root_path + "/" + db_name + ".db/" + table_name;
}
template <typename T>
Result<T> AddContext(paimon::Result<T>&& result, const std::string& context) {
if (!result.ok()) {
const Status status = result.status();
return status.WithMessage(context, ": ", status.message());
}
return std::move(result).value();
}
Status AddContext(const paimon::Status& status, const std::string& context) {
if (!status.ok()) {
return status.WithMessage(context, ": ", status.message());
}
return Status::OK();
}
void SkipWithMessage(::benchmark::State& state, const std::string& message) {
state.SkipWithError(message);
}
std::string GetConfiguredFileFormat() {
std::string file_format = "parquet";
for (const auto& kv : GetBenchmarkCliOptions().extra_options) {
if (kv.first == paimon::Options::FILE_FORMAT) {
file_format = kv.second;
}
}
return file_format;
}
bool IsFileFormatSupported(const std::string& format) {
if (format == "parquet") {
return true;
}
if (format == "orc") {
#ifdef PAIMON_ENABLE_ORC
return true;
#else
return false;
#endif
}
return false;
}
void ApplyExtraOptions(std::map<std::string, std::string>* options) {
for (const auto& kv : GetBenchmarkCliOptions().extra_options) {
(*options)[kv.first] = kv.second;
}
}
std::map<std::string, std::string> BuildOptions(const std::string& file_format) {
std::map<std::string, std::string> options = {
{paimon::Options::FILE_FORMAT, file_format},
};
ApplyExtraOptions(&options);
return options;
}
std::map<std::string, std::string> BuildPkOptions(const std::string& file_format) {
auto options = BuildOptions(file_format);
options[paimon::Options::BUCKET] = "1";
options[paimon::Options::MERGE_ENGINE] = "deduplicate";
return options;
}
std::string GetSourceDataFilePath() {
return GetBenchmarkCliOptions().source_data_file;
}
std::string GetSourceTablePath() {
return GetBenchmarkCliOptions().source_table_path;
}
const std::vector<std::string>& GetPkColumns() {
return GetBenchmarkCliOptions().pk_columns;
}
SourceDataSpec GetSourceDataSpec() {
const std::string source_data_file_path = GetSourceDataFilePath();
if (!source_data_file_path.empty()) {
return {"parquet", source_data_file_path};
}
return {"", ""};
}
int64_t GetSourceBatchMaxRows() {
return kSourceBatchMaxRows;
}
int32_t GetRowToBatchThreadNumber() {
return kRowToBatchThreadNumber;
}
bool SupportsParquetSourceDataMode() {
#if PAIMON_BENCHMARK_HAS_PARQUET_READER
return true;
#else
return false;
#endif
}
bool SupportsSourceDataMode(const std::string& source_format) {
if (source_format == "parquet") {
return SupportsParquetSourceDataMode();
}
return false;
}
struct SourceDataMetadata {
std::shared_ptr<arrow::Schema> schema;
int64_t total_rows = 0;
std::string format;
std::string path;
};
#if PAIMON_BENCHMARK_HAS_PARQUET_READER
Result<std::unique_ptr<parquet::arrow::FileReader>> OpenParquetSourceReader(
const std::string& path) {
auto input = arrow::io::ReadableFile::Open(path);
if (!input.ok()) {
return Status::Invalid("open Parquet source failed: ", path, ", ",
input.status().ToString());
}
std::unique_ptr<parquet::arrow::FileReader> parquet_reader;
const auto open_status = parquet::arrow::OpenFile(
input.ValueUnsafe(), arrow::default_memory_pool(), &parquet_reader);
if (!open_status.ok()) {
return Status::Invalid("create Parquet reader failed: ", open_status.ToString());
}
parquet_reader->set_batch_size(GetSourceBatchMaxRows());
return parquet_reader;
}
#endif
Result<SourceDataMetadata> LoadParquetSourceMetadata(const std::string& path) {
#if !PAIMON_BENCHMARK_HAS_PARQUET_READER
return Status::Invalid(
"Parquet source data mode requires parquet::arrow reader support in this build");
#else
static SourceDataMetadata cache;
static std::mutex cache_mutex;
std::lock_guard<std::mutex> lock(cache_mutex);
if (cache.path == path && cache.format == "parquet") {
return cache;
}
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<parquet::arrow::FileReader> parquet_reader,
OpenParquetSourceReader(path));
std::shared_ptr<arrow::Schema> schema;
const auto schema_status = parquet_reader->GetSchema(&schema);
if (!schema_status.ok()) {
return Status::Invalid("read Parquet source schema failed: ", schema_status.ToString());
}
const int64_t total_rows = parquet_reader->parquet_reader()->metadata()->num_rows();
if (total_rows <= 0) {
return Status::Invalid("Parquet source is empty: ", path);
}
cache.schema = std::move(schema);
cache.total_rows = total_rows;
cache.format = "parquet";
cache.path = path;
return cache;
#endif
}
Result<SourceDataMetadata> LoadSourceDataMetadata(const SourceDataSpec& source_spec) {
if (source_spec.format == "parquet") {
return LoadParquetSourceMetadata(source_spec.path);
}
return Status::Invalid("unknown source format: ", source_spec.format);
}
std::shared_ptr<arrow::StructArray> BuildStructArrayFromRecordBatch(
const std::shared_ptr<arrow::RecordBatch>& batch) {
return std::make_shared<arrow::StructArray>(arrow::struct_(batch->schema()->fields()),
batch->num_rows(), batch->columns());
}
Result<std::unique_ptr<paimon::RecordBatch>> MakeRecordBatch(
const std::shared_ptr<arrow::StructArray>& arr) {
ArrowArray c_array;
if (!arrow::ExportArray(*arr, &c_array).ok()) {
return Status::Invalid("failed to export arrow array");
}
paimon::RecordBatchBuilder builder(&c_array);
return AddContext(builder.Finish(), "build paimon record batch");
}
Status EnsureTable(const std::string& root_path, const std::string& db_name,
const std::string& table_name, const std::map<std::string, std::string>& options,
const std::shared_ptr<arrow::Schema>& schema,
const std::vector<std::string>& primary_keys = {}) {
PAIMON_ASSIGN_OR_RAISE(
std::unique_ptr<paimon::Catalog> catalog,
AddContext(paimon::Catalog::Create(root_path, options), "create catalog"));
PAIMON_RETURN_NOT_OK(
AddContext(catalog->CreateDatabase(db_name, options, true), "create database"));
ArrowSchema c_schema;
if (!arrow::ExportSchema(*schema, &c_schema).ok()) {
return Status::Invalid("failed to export table schema");
}
PAIMON_RETURN_NOT_OK(
AddContext(catalog->CreateTable(paimon::Identifier(db_name, table_name), &c_schema,
/*partition_keys=*/{}, primary_keys, options,
/*ignore_if_exists=*/false),
"create table"));
return Status::OK();
}
Status WriteSourceDataToWriter(paimon::FileStoreWrite* writer, const SourceDataSpec& source_spec) {
if (source_spec.format != "parquet") {
return Status::Invalid("unknown source format: ", source_spec.format);
}
#if !PAIMON_BENCHMARK_HAS_PARQUET_READER
return Status::Invalid(
"Parquet source data mode requires parquet::arrow reader support in this build");
#else
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<parquet::arrow::FileReader> parquet_reader,
OpenParquetSourceReader(source_spec.path));
std::unique_ptr<arrow::RecordBatchReader> batch_reader;
const auto reader_status = parquet_reader->GetRecordBatchReader(&batch_reader);
if (!reader_status.ok()) {
return Status::Invalid("create Parquet source batch reader failed: ",
reader_status.ToString());
}
int64_t written_rows = 0;
while (true) {
std::shared_ptr<arrow::RecordBatch> record_batch;
const auto read_status = batch_reader->ReadNext(&record_batch);
if (!read_status.ok()) {
return Status::Invalid("read Parquet source batch failed: ", read_status.ToString());
}
if (record_batch == nullptr) {
break;
}
if (record_batch->num_rows() <= 0) {
continue;
}
auto struct_array = BuildStructArrayFromRecordBatch(record_batch);
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::RecordBatch> batch,
MakeRecordBatch(struct_array));
PAIMON_RETURN_NOT_OK(AddContext(writer->Write(std::move(batch)), "write batch"));
written_rows += record_batch->num_rows();
}
if (written_rows <= 0) {
return Status::Invalid("source file has no non-empty data batches: ", source_spec.path);
}
return Status::OK();
#endif
}
Status WriteAndCommit(const std::string& table_path,
const std::map<std::string, std::string>& options,
const SourceDataSpec& source_spec) {
paimon::WriteContextBuilder write_builder(table_path, "benchmark-writer");
PAIMON_ASSIGN_OR_RAISE(
std::unique_ptr<paimon::WriteContext> write_ctx,
AddContext(write_builder.SetOptions(options).Finish(), "create write context"));
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::FileStoreWrite> writer,
AddContext(paimon::FileStoreWrite::Create(std::move(write_ctx)),
"create file store writer"));
PAIMON_RETURN_NOT_OK(WriteSourceDataToWriter(writer.get(), source_spec));
PAIMON_ASSIGN_OR_RAISE(std::vector<std::shared_ptr<paimon::CommitMessage>> messages,
AddContext(writer->PrepareCommit(), "prepare commit"));
paimon::CommitContextBuilder commit_builder(table_path, "benchmark-writer");
PAIMON_ASSIGN_OR_RAISE(
std::unique_ptr<paimon::CommitContext> commit_ctx,
AddContext(commit_builder.SetOptions(options).Finish(), "create commit context"));
PAIMON_ASSIGN_OR_RAISE(
std::unique_ptr<paimon::FileStoreCommit> committer,
AddContext(paimon::FileStoreCommit::Create(std::move(commit_ctx)), "create committer"));
PAIMON_RETURN_NOT_OK(AddContext(committer->Commit(messages), "commit write"));
return Status::OK();
}
struct SharedReadTableCache {
std::string key;
std::unique_ptr<paimon::test::UniqueTestDirectory> workspace;
std::string table_path;
int64_t total_rows = 0;
};
struct SharedMorReadTableCache {
std::string key;
std::unique_ptr<paimon::test::UniqueTestDirectory> workspace;
std::string table_path;
int64_t total_rows = 0;
};
std::string BuildReadTableCacheKey(const std::string& file_format,
const SourceDataSpec& source_spec) {
return file_format + "|" + source_spec.format + "|" + source_spec.path + "|" +
std::to_string(GetSourceBatchMaxRows());
}
std::string JoinColumns(const std::vector<std::string>& columns) {
std::string joined;
for (size_t i = 0; i < columns.size(); ++i) {
if (i > 0) {
joined.append(",");
}
joined.append(columns[i]);
}
return joined;
}
Result<const SharedMorReadTableCache*> GetOrCreateSharedMorReadTable(
const std::string& file_format, const SourceDataSpec& source_spec) {
static SharedMorReadTableCache cache;
static std::mutex cache_mutex;
const std::vector<std::string>& pk_columns = GetPkColumns();
const std::string cache_key =
BuildReadTableCacheKey(file_format, source_spec) + "|pk=" + JoinColumns(pk_columns);
std::lock_guard<std::mutex> lock(cache_mutex);
if (cache.workspace != nullptr && cache.key == cache_key) {
std::cout << "[benchmark][mor-read] reuse_output_table_path=" << cache.table_path
<< std::endl;
return &cache;
}
auto options = BuildPkOptions(file_format);
PAIMON_ASSIGN_OR_RAISE(const SourceDataMetadata source_metadata,
LoadSourceDataMetadata(source_spec));
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::test::UniqueTestDirectory> workspace,
CreateBenchmarkWorkspace());
const std::string db_name = "bench_db";
const std::string table_name = "mor_read_shared_" + std::to_string(NextTableId());
PAIMON_RETURN_NOT_OK(EnsureTable(workspace->Str(), db_name, table_name, options,
source_metadata.schema,
/*primary_keys=*/pk_columns));
const std::string table_path = RequirePath(workspace->Str(), db_name, table_name);
std::cout << "[benchmark][mor-read] create_shared_output_table_path=" << table_path
<< std::endl;
PAIMON_RETURN_NOT_OK(WriteAndCommit(table_path, options, source_spec));
cache.key = cache_key;
cache.workspace = std::move(workspace);
cache.table_path = table_path;
cache.total_rows = source_metadata.total_rows;
return &cache;
}
Result<const SharedReadTableCache*> GetOrCreateSharedReadTable(const std::string& file_format,
const SourceDataSpec& source_spec) {
static SharedReadTableCache cache;
static std::mutex cache_mutex;
const std::string cache_key = BuildReadTableCacheKey(file_format, source_spec);
std::lock_guard<std::mutex> lock(cache_mutex);
if (cache.workspace != nullptr && cache.key == cache_key) {
std::cout << "[benchmark][read] reuse_output_table_path=" << cache.table_path << std::endl;
return &cache;
}
auto options = BuildOptions(file_format);
PAIMON_ASSIGN_OR_RAISE(const SourceDataMetadata source_metadata,
LoadSourceDataMetadata(source_spec));
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::test::UniqueTestDirectory> workspace,
CreateBenchmarkWorkspace());
const std::string db_name = "bench_db";
const std::string table_name = "read_shared_" + std::to_string(NextTableId());
PAIMON_RETURN_NOT_OK(
EnsureTable(workspace->Str(), db_name, table_name, options, source_metadata.schema));
const std::string table_path = RequirePath(workspace->Str(), db_name, table_name);
std::cout << "[benchmark][read] create_shared_output_table_path=" << table_path << std::endl;
PAIMON_RETURN_NOT_OK(WriteAndCommit(table_path, options, source_spec));
cache.key = cache_key;
cache.workspace = std::move(workspace);
cache.table_path = table_path;
cache.total_rows = source_metadata.total_rows;
return &cache;
}
Result<int64_t> ReadRows(const std::string& table_path,
const std::map<std::string, std::string>& options,
int32_t prefetch_parallel_num) {
paimon::ScanContextBuilder scan_builder(table_path);
PAIMON_ASSIGN_OR_RAISE(
std::unique_ptr<paimon::ScanContext> scan_ctx,
AddContext(scan_builder.SetOptions(options).Finish(), "create scan context"));
PAIMON_ASSIGN_OR_RAISE(
std::unique_ptr<paimon::TableScan> scanner,
AddContext(paimon::TableScan::Create(std::move(scan_ctx)), "create scanner"));
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<paimon::Plan> plan,
AddContext(scanner->CreatePlan(), "create plan"));
paimon::ReadContextBuilder read_builder(table_path);
constexpr int32_t kPrefetchBatchCount = 600;
read_builder.SetOptions(options)
.EnablePrefetch(true)
.SetPrefetchBatchCount(kPrefetchBatchCount)
.SetPrefetchMaxParallelNum(prefetch_parallel_num)
.EnableMultiThreadRowToBatch(GetRowToBatchThreadNumber() > 1)
.SetRowToBatchThreadNumber(GetRowToBatchThreadNumber());
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::ReadContext> read_ctx,
AddContext(read_builder.Finish(), "create read context"));
PAIMON_ASSIGN_OR_RAISE(
std::unique_ptr<paimon::TableRead> reader,
AddContext(paimon::TableRead::Create(std::move(read_ctx)), "create table reader"));
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<paimon::BatchReader> batch_reader,
AddContext(reader->CreateReader(plan->Splits()), "create batch reader"));
int64_t total_rows = 0;
while (true) {
PAIMON_ASSIGN_OR_RAISE(paimon::BatchReader::ReadBatch batch,
AddContext(batch_reader->NextBatch(), "read next batch"));
if (paimon::BatchReader::IsEofBatch(batch)) {
break;
}
auto& [array, schema] = batch;
auto imported = arrow::ImportArray(array.get(), schema.get());
if (!imported.ok()) {
return Status::Invalid("import c data array failed: ", imported.status().ToString());
}
total_rows += imported.ValueUnsafe()->length();
}
return total_rows;
}
struct PreparedSourceData {
std::shared_ptr<arrow::Schema> schema;
int64_t total_rows = 0;
};
bool TryGetSourceSpec(::benchmark::State& state, SourceDataSpec* source_spec) {
(void)state;
*source_spec = GetSourceDataSpec();
return true;
}
bool TryPrepareSourceData(::benchmark::State& state, const SourceDataSpec& source_spec,
PreparedSourceData* prepared) {
auto source_metadata = LoadSourceDataMetadata(source_spec);
if (!source_metadata.ok()) {
SkipWithMessage(state, source_metadata.status().ToString());
return false;
}
prepared->schema = source_metadata.value().schema;
prepared->total_rows = source_metadata.value().total_rows;
return true;
}
} // namespace
Status ParsePaimonBenchmarkCliArgs(int* argc, char** argv) {
auto parsed_argc = static_cast<int32_t>(*argc);
PAIMON_RETURN_NOT_OK(ParsePaimonBenchmarkCliArgsImpl(&parsed_argc, argv));
*argc = static_cast<int>(parsed_argc);
return Status::OK();
}
bool HasHelpFlag(int32_t argc, char** argv) {
return HasHelpFlagImpl(argc, argv);
}
void PrintPaimonBenchmarkCliHelp() {
PrintPaimonBenchmarkCliHelpImpl();
}
void RunBMWrite(::benchmark::State& state) {
const std::string file_format = GetConfiguredFileFormat();
SourceDataSpec source_spec;
if (!TryGetSourceSpec(state, &source_spec)) {
return;
}
if (!BenchmarkHelpers::ValidateSourcePresenceOrSkip(
state, source_spec.path, "--paimon_source_data_file is required", &SkipWithMessage)) {
return;
}
if (!BenchmarkHelpers::ValidateSourceSupportOrSkip(state, source_spec.format,
SupportsSourceDataMode(source_spec.format),
&SkipWithMessage)) {
return;
}
if (!BenchmarkHelpers::ValidateFileFormatOrSkip(
state, file_format, IsFileFormatSupported(file_format), &SkipWithMessage)) {
return;
}
auto options = BuildOptions(file_format);
PreparedSourceData prepared;
if (!TryPrepareSourceData(state, source_spec, &prepared)) {
return;
}
auto workspace = CreateBenchmarkWorkspace();
if (!workspace.ok()) {
SkipWithMessage(state, workspace.status().ToString());
return;
}
for (auto _ : state) {
const std::string db_name = "bench_db";
const std::string table_name = "write_" + std::to_string(NextTableId());
const Status ensure_status =
EnsureTable(workspace.value()->Str(), db_name, table_name, options, prepared.schema);
if (!ensure_status.ok()) {
SkipWithMessage(state, ensure_status.ToString());
return;
}
const std::string table_path = RequirePath(workspace.value()->Str(), db_name, table_name);
std::cout << "[benchmark][write] output_table_path=" << table_path << std::endl;
const Status write_status = WriteAndCommit(table_path, options, source_spec);
if (!write_status.ok()) {
SkipWithMessage(state, write_status.ToString());
return;
}
}
state.SetItemsProcessed(state.iterations() * prepared.total_rows);
}
void RunBMRead(::benchmark::State& state) {
const auto prefetch_parallel_num = static_cast<int32_t>(state.range(0));
const std::string file_format = GetConfiguredFileFormat();
const std::string source_table_path = GetSourceTablePath();
SourceDataSpec source_spec;
if (!TryGetSourceSpec(state, &source_spec)) {
return;
}
if (!BenchmarkHelpers::ValidateFileFormatOrSkip(
state, file_format, IsFileFormatSupported(file_format), &SkipWithMessage)) {
return;
}
if (!BenchmarkHelpers::ValidatePrefetchParallelOrSkip(state, prefetch_parallel_num,
&SkipWithMessage)) {
return;
}
auto options = BuildOptions(file_format);
auto source_table_read_result = BenchmarkHelpers::TryRunSourceTableReadMode(
state, "read", source_table_path,
[&]() { return ReadRows(source_table_path, options, prefetch_parallel_num); });
if (!source_table_read_result.ok()) {
SkipWithMessage(state, source_table_read_result.status().ToString());
return;
}
if (source_table_read_result.value()) {
return;
}
if (!BenchmarkHelpers::ValidateSourcePresenceOrSkip(
state, source_spec.path,
"--paimon_source_data_file is required when --paimon_source_table_path is not set",
&SkipWithMessage)) {
return;
}
if (!BenchmarkHelpers::ValidateSourceSupportOrSkip(state, source_spec.format,
SupportsSourceDataMode(source_spec.format),
&SkipWithMessage)) {
return;
}
auto shared_table = GetOrCreateSharedReadTable(file_format, source_spec);
if (!shared_table.ok()) {
SkipWithMessage(state, shared_table.status().ToString());
return;
}
auto rows_read = BenchmarkHelpers::RunReadIterations(state, [&]() {
return ReadRows(shared_table.value()->table_path, options, prefetch_parallel_num);
});
if (!rows_read.ok()) {
SkipWithMessage(state, rows_read.status().ToString());
return;
}
state.SetItemsProcessed(state.iterations() * rows_read.value());
}
void RunBMPkWrite(::benchmark::State& state) {
const std::string file_format = GetConfiguredFileFormat();
SourceDataSpec source_spec;
if (!TryGetSourceSpec(state, &source_spec)) {
return;
}
if (!BenchmarkHelpers::ValidateSourcePresenceOrSkip(
state, source_spec.path, "--paimon_source_data_file is required", &SkipWithMessage)) {
return;
}
if (!BenchmarkHelpers::ValidateSourceSupportOrSkip(state, source_spec.format,
SupportsSourceDataMode(source_spec.format),
&SkipWithMessage)) {
return;
}
if (!BenchmarkHelpers::ValidateFileFormatOrSkip(
state, file_format, IsFileFormatSupported(file_format), &SkipWithMessage)) {
return;
}
const std::vector<std::string>& pk_columns = GetPkColumns();
if (pk_columns.empty()) {
SkipWithMessage(state, "--paimon_pk_columns is required for BM_PK_Write");
return;
}
auto options = BuildPkOptions(file_format);
PreparedSourceData prepared;
if (!TryPrepareSourceData(state, source_spec, &prepared)) {
return;
}
auto workspace = CreateBenchmarkWorkspace();
if (!workspace.ok()) {
SkipWithMessage(state, workspace.status().ToString());
return;
}
for (auto _ : state) {
const std::string db_name = "bench_db";
const std::string table_name = "pk_write_" + std::to_string(NextTableId());
const Status ensure_status =
EnsureTable(workspace.value()->Str(), db_name, table_name, options, prepared.schema,
/*primary_keys=*/pk_columns);
if (!ensure_status.ok()) {
SkipWithMessage(state, ensure_status.ToString());
return;
}
const std::string table_path = RequirePath(workspace.value()->Str(), db_name, table_name);
std::cout << "[benchmark][pk-write] output_table_path=" << table_path << std::endl;
const Status write_status = WriteAndCommit(table_path, options, source_spec);
if (!write_status.ok()) {
SkipWithMessage(state, write_status.ToString());
return;
}
}
state.SetItemsProcessed(state.iterations() * prepared.total_rows);
}
void RunBMMorRead(::benchmark::State& state) {
const auto prefetch_parallel_num = static_cast<int32_t>(state.range(0));
const std::string file_format = GetConfiguredFileFormat();
const std::string source_table_path = GetSourceTablePath();
SourceDataSpec source_spec;
if (!TryGetSourceSpec(state, &source_spec)) {
return;
}
if (!BenchmarkHelpers::ValidateFileFormatOrSkip(
state, file_format, IsFileFormatSupported(file_format), &SkipWithMessage)) {
return;
}
if (!BenchmarkHelpers::ValidatePrefetchParallelOrSkip(state, prefetch_parallel_num,
&SkipWithMessage)) {
return;
}
const auto source_table_read_options = BuildOptions(file_format);
auto source_table_read_result =
BenchmarkHelpers::TryRunSourceTableReadMode(state, "mor-read", source_table_path, [&]() {
return ReadRows(source_table_path, source_table_read_options, prefetch_parallel_num);
});
if (!source_table_read_result.ok()) {
SkipWithMessage(state, source_table_read_result.status().ToString());
return;
}
if (source_table_read_result.value()) {
return;
}
if (!BenchmarkHelpers::ValidateSourcePresenceOrSkip(
state, source_spec.path,
"--paimon_source_data_file is required when --paimon_source_table_path is not set",
&SkipWithMessage)) {
return;
}
if (!BenchmarkHelpers::ValidateSourceSupportOrSkip(state, source_spec.format,
SupportsSourceDataMode(source_spec.format),
&SkipWithMessage)) {
return;
}
if (GetPkColumns().empty()) {
SkipWithMessage(state, "--paimon_pk_columns is required for BM_MOR_Read");
return;
}
auto options = BuildPkOptions(file_format);
auto shared_table = GetOrCreateSharedMorReadTable(file_format, source_spec);
if (!shared_table.ok()) {
SkipWithMessage(state, shared_table.status().ToString());
return;
}
auto rows_read = BenchmarkHelpers::RunReadIterations(state, [&]() {
return ReadRows(shared_table.value()->table_path, options, prefetch_parallel_num);
});
if (!rows_read.ok()) {
SkipWithMessage(state, rows_read.status().ToString());
return;
}
state.SetItemsProcessed(state.iterations() * rows_read.value());
}
} // namespace paimon::benchmark