blob: 820c51ba8ec5ab43d547241580331e67a947799f [file] [log] [blame]
// 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.h"
#include "arrow/compute/api_vector.h"
#include "arrow/compute/kernels/test_util.h"
#include "arrow/table.h"
#include "arrow/testing/gtest_util.h"
#include "arrow/testing/random.h"
#include "arrow/util/benchmark_util.h"
#include "arrow/util/logging.h"
namespace arrow {
namespace compute {
constexpr auto kSeed = 0x0ff1ce;
static void ArraySortIndicesBenchmark(benchmark::State& state,
const std::shared_ptr<Array>& values) {
for (auto _ : state) {
ABORT_NOT_OK(SortIndices(*values).status());
}
state.SetItemsProcessed(state.iterations() * values->length());
}
static void ChunkedArraySortIndicesBenchmark(
benchmark::State& state, const std::shared_ptr<ChunkedArray>& values) {
for (auto _ : state) {
ABORT_NOT_OK(SortIndices(*values).status());
}
state.SetItemsProcessed(state.iterations() * values->length());
}
static void ArraySortIndicesInt64Benchmark(benchmark::State& state, int64_t min,
int64_t max) {
RegressionArgs args(state);
const int64_t array_size = args.size / sizeof(int64_t);
auto rand = random::RandomArrayGenerator(kSeed);
auto values = rand.Int64(array_size, min, max, args.null_proportion);
ArraySortIndicesBenchmark(state, values);
}
static void ChunkedArraySortIndicesInt64Benchmark(benchmark::State& state, int64_t min,
int64_t max) {
RegressionArgs args(state);
const int64_t n_chunks = 10;
const int64_t array_size = args.size / n_chunks / sizeof(int64_t);
auto rand = random::RandomArrayGenerator(kSeed);
ArrayVector chunks;
for (int64_t i = 0; i < n_chunks; ++i) {
chunks.push_back(rand.Int64(array_size, min, max, args.null_proportion));
}
ChunkedArraySortIndicesBenchmark(state, std::make_shared<ChunkedArray>(chunks));
}
static void ArraySortIndicesInt64Narrow(benchmark::State& state) {
ArraySortIndicesInt64Benchmark(state, -100, 100);
}
static void ArraySortIndicesInt64Wide(benchmark::State& state) {
const auto min = std::numeric_limits<int64_t>::min();
const auto max = std::numeric_limits<int64_t>::max();
ArraySortIndicesInt64Benchmark(state, min, max);
}
static void ChunkedArraySortIndicesInt64Narrow(benchmark::State& state) {
ChunkedArraySortIndicesInt64Benchmark(state, -100, 100);
}
static void ChunkedArraySortIndicesInt64Wide(benchmark::State& state) {
const auto min = std::numeric_limits<int64_t>::min();
const auto max = std::numeric_limits<int64_t>::max();
ChunkedArraySortIndicesInt64Benchmark(state, min, max);
}
static void DatumSortIndicesBenchmark(benchmark::State& state, const Datum& datum,
const SortOptions& options) {
for (auto _ : state) {
ABORT_NOT_OK(SortIndices(datum, options).status());
}
}
// Extract benchmark args from benchmark::State
struct RecordBatchSortIndicesArgs {
// the number of records
const int64_t num_records;
// proportion of nulls in generated arrays
const double null_proportion;
// the number of columns
const int64_t num_columns;
// Extract args
explicit RecordBatchSortIndicesArgs(benchmark::State& state)
: num_records(state.range(0)),
null_proportion(ComputeNullProportion(state.range(1))),
num_columns(state.range(2)),
state_(state) {}
~RecordBatchSortIndicesArgs() {
state_.SetItemsProcessed(state_.iterations() * num_records);
}
protected:
double ComputeNullProportion(int64_t inverse_null_proportion) {
if (inverse_null_proportion == 0) {
return 0.0;
} else {
return std::min(1., 1. / static_cast<double>(inverse_null_proportion));
}
}
benchmark::State& state_;
};
struct TableSortIndicesArgs : public RecordBatchSortIndicesArgs {
// the number of chunks in each generated column
const int64_t num_chunks;
// Extract args
explicit TableSortIndicesArgs(benchmark::State& state)
: RecordBatchSortIndicesArgs(state), num_chunks(state.range(3)) {}
};
struct BatchOrTableBenchmarkData {
std::shared_ptr<Schema> schema;
std::vector<SortKey> sort_keys;
ChunkedArrayVector columns;
};
BatchOrTableBenchmarkData MakeBatchOrTableBenchmarkDataInt64(
const RecordBatchSortIndicesArgs& args, int64_t num_chunks, int64_t min_value,
int64_t max_value) {
auto rand = random::RandomArrayGenerator(kSeed);
FieldVector fields;
BatchOrTableBenchmarkData data;
for (int64_t i = 0; i < args.num_columns; ++i) {
auto name = std::to_string(i);
fields.push_back(field(name, int64()));
auto order = (i % 2) == 0 ? SortOrder::Ascending : SortOrder::Descending;
data.sort_keys.emplace_back(name, order);
ArrayVector chunks;
if ((args.num_records % num_chunks) != 0) {
Status::Invalid("The number of chunks (", num_chunks,
") must be "
"a multiple of the number of records (",
args.num_records, ")")
.Abort();
}
auto num_records_in_array = args.num_records / num_chunks;
for (int64_t j = 0; j < num_chunks; ++j) {
chunks.push_back(
rand.Int64(num_records_in_array, min_value, max_value, args.null_proportion));
}
ASSIGN_OR_ABORT(auto chunked_array, ChunkedArray::Make(chunks, int64()));
data.columns.push_back(chunked_array);
}
data.schema = schema(fields);
return data;
}
static void RecordBatchSortIndicesInt64(benchmark::State& state, int64_t min,
int64_t max) {
RecordBatchSortIndicesArgs args(state);
auto data = MakeBatchOrTableBenchmarkDataInt64(args, /*num_chunks=*/1, min, max);
ArrayVector columns;
for (const auto& chunked : data.columns) {
ARROW_CHECK_EQ(chunked->num_chunks(), 1);
columns.push_back(chunked->chunk(0));
}
auto batch = RecordBatch::Make(data.schema, args.num_records, columns);
SortOptions options(data.sort_keys);
DatumSortIndicesBenchmark(state, Datum(*batch), options);
}
static void TableSortIndicesInt64(benchmark::State& state, int64_t min, int64_t max) {
TableSortIndicesArgs args(state);
auto data = MakeBatchOrTableBenchmarkDataInt64(args, args.num_chunks, min, max);
auto table = Table::Make(data.schema, data.columns, args.num_records);
SortOptions options(data.sort_keys);
DatumSortIndicesBenchmark(state, Datum(*table), options);
}
static void RecordBatchSortIndicesInt64Narrow(benchmark::State& state) {
RecordBatchSortIndicesInt64(state, -100, 100);
}
static void RecordBatchSortIndicesInt64Wide(benchmark::State& state) {
RecordBatchSortIndicesInt64(state, std::numeric_limits<int64_t>::min(),
std::numeric_limits<int64_t>::max());
}
static void TableSortIndicesInt64Narrow(benchmark::State& state) {
TableSortIndicesInt64(state, -100, 100);
}
static void TableSortIndicesInt64Wide(benchmark::State& state) {
TableSortIndicesInt64(state, std::numeric_limits<int64_t>::min(),
std::numeric_limits<int64_t>::max());
}
BENCHMARK(ArraySortIndicesInt64Narrow)
->Apply(RegressionSetArgs)
->Args({1 << 20, 100})
->Args({1 << 23, 100})
->Unit(benchmark::TimeUnit::kNanosecond);
BENCHMARK(ArraySortIndicesInt64Wide)
->Apply(RegressionSetArgs)
->Args({1 << 20, 100})
->Args({1 << 23, 100})
->Unit(benchmark::TimeUnit::kNanosecond);
BENCHMARK(ChunkedArraySortIndicesInt64Narrow)
->Apply(RegressionSetArgs)
->Args({1 << 20, 100})
->Args({1 << 23, 100})
->Unit(benchmark::TimeUnit::kNanosecond);
BENCHMARK(ChunkedArraySortIndicesInt64Wide)
->Apply(RegressionSetArgs)
->Args({1 << 20, 100})
->Args({1 << 23, 100})
->Unit(benchmark::TimeUnit::kNanosecond);
BENCHMARK(RecordBatchSortIndicesInt64Narrow)
->ArgsProduct({
{1 << 20}, // the number of records
{100, 0}, // inverse null proportion
{16, 8, 2, 1}, // the number of columns
})
->Unit(benchmark::TimeUnit::kNanosecond);
BENCHMARK(RecordBatchSortIndicesInt64Wide)
->ArgsProduct({
{1 << 20}, // the number of records
{100, 0}, // inverse null proportion
{16, 8, 2, 1}, // the number of columns
})
->Unit(benchmark::TimeUnit::kNanosecond);
BENCHMARK(TableSortIndicesInt64Narrow)
->ArgsProduct({
{1 << 20}, // the number of records
{100, 0}, // inverse null proportion
{16, 8, 2, 1}, // the number of columns
{32, 4, 1}, // the number of chunks
})
->Unit(benchmark::TimeUnit::kNanosecond);
BENCHMARK(TableSortIndicesInt64Wide)
->ArgsProduct({
{1 << 20}, // the number of records
{100, 0}, // inverse null proportion
{16, 8, 2, 1}, // the number of columns
{32, 4, 1}, // the number of chunks
})
->Unit(benchmark::TimeUnit::kNanosecond);
} // namespace compute
} // namespace arrow