blob: 450e691323cae1ac5d34ca953b6f8cc0a2f9c340 [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 <stdlib.h>
#include "arrow/memory_pool.h"
#include "arrow/status.h"
#include "arrow/testing/gtest_util.h"
#include "arrow/type_fwd.h"
#include "benchmark/benchmark.h"
#include "gandiva/decimal_type_util.h"
#include "gandiva/projector.h"
#include "gandiva/tests/test_util.h"
#include "gandiva/tests/timed_evaluate.h"
#include "gandiva/tree_expr_builder.h"
namespace gandiva {
using arrow::boolean;
using arrow::int32;
using arrow::int64;
using arrow::utf8;
static void TimedTestAdd3(benchmark::State& state) {
// schema for input fields
auto field0 = field("f0", int64());
auto field1 = field("f1", int64());
auto field2 = field("f2", int64());
auto schema = arrow::schema({field0, field1, field2});
auto pool_ = arrow::default_memory_pool();
// output field
auto field_sum = field("add", int64());
// Build expression
auto part_sum = TreeExprBuilder::MakeFunction(
"add", {TreeExprBuilder::MakeField(field1), TreeExprBuilder::MakeField(field2)},
int64());
auto sum = TreeExprBuilder::MakeFunction(
"add", {TreeExprBuilder::MakeField(field0), part_sum}, int64());
auto sum_expr = TreeExprBuilder::MakeExpression(sum, field_sum);
std::shared_ptr<Projector> projector;
ASSERT_OK(Projector::Make(schema, {sum_expr}, TestConfiguration(), &projector));
Int64DataGenerator data_generator;
ProjectEvaluator evaluator(projector);
Status status = TimedEvaluate<arrow::Int64Type, int64_t>(
schema, evaluator, data_generator, pool_, 1 * MILLION, 16 * THOUSAND, state);
ASSERT_OK(status);
}
static void TimedTestBigNested(benchmark::State& state) {
// schema for input fields
auto fielda = field("a", int32());
auto schema = arrow::schema({fielda});
auto pool_ = arrow::default_memory_pool();
// output fields
auto field_result = field("res", int32());
// build expression.
// if (a < 10)
// 10
// else if (a < 20)
// 20
// ..
// ..
// else if (a < 190)
// 190
// else
// 200
auto node_a = TreeExprBuilder::MakeField(fielda);
auto top_node = TreeExprBuilder::MakeLiteral(200);
for (int thresh = 190; thresh > 0; thresh -= 10) {
auto literal = TreeExprBuilder::MakeLiteral(thresh);
auto condition =
TreeExprBuilder::MakeFunction("less_than", {node_a, literal}, boolean());
auto if_node = TreeExprBuilder::MakeIf(condition, literal, top_node, int32());
top_node = if_node;
}
auto expr = TreeExprBuilder::MakeExpression(top_node, field_result);
// Build a projector for the expressions.
std::shared_ptr<Projector> projector;
ASSERT_OK(Projector::Make(schema, {expr}, TestConfiguration(), &projector));
BoundedInt32DataGenerator data_generator(250);
ProjectEvaluator evaluator(projector);
Status status = TimedEvaluate<arrow::Int32Type, int32_t>(
schema, evaluator, data_generator, pool_, 1 * MILLION, 16 * THOUSAND, state);
ASSERT_TRUE(status.ok());
}
static void TimedTestExtractYear(benchmark::State& state) {
// schema for input fields
auto field0 = field("f0", arrow::date64());
auto schema = arrow::schema({field0});
auto pool_ = arrow::default_memory_pool();
// output field
auto field_res = field("res", int64());
// Build expression
auto expr = TreeExprBuilder::MakeExpression("extractYear", {field0}, field_res);
std::shared_ptr<Projector> projector;
ASSERT_OK(Projector::Make(schema, {expr}, TestConfiguration(), &projector));
Int64DataGenerator data_generator;
ProjectEvaluator evaluator(projector);
Status status = TimedEvaluate<arrow::Date64Type, int64_t>(
schema, evaluator, data_generator, pool_, 1 * MILLION, 16 * THOUSAND, state);
ASSERT_TRUE(status.ok());
}
static void TimedTestFilterAdd2(benchmark::State& state) {
// schema for input fields
auto field0 = field("f0", int64());
auto field1 = field("f1", int64());
auto field2 = field("f2", int64());
auto schema = arrow::schema({field0, field1, field2});
auto pool_ = arrow::default_memory_pool();
// Build expression
auto sum = TreeExprBuilder::MakeFunction(
"add", {TreeExprBuilder::MakeField(field1), TreeExprBuilder::MakeField(field0)},
int64());
auto less_than = TreeExprBuilder::MakeFunction(
"less_than", {sum, TreeExprBuilder::MakeField(field2)}, boolean());
auto condition = TreeExprBuilder::MakeCondition(less_than);
std::shared_ptr<Filter> filter;
ASSERT_OK(Filter::Make(schema, condition, TestConfiguration(), &filter));
Int64DataGenerator data_generator;
FilterEvaluator evaluator(filter);
Status status = TimedEvaluate<arrow::Int64Type, int64_t>(
schema, evaluator, data_generator, pool_, MILLION, 16 * THOUSAND, state);
ASSERT_TRUE(status.ok());
}
static void TimedTestFilterLike(benchmark::State& state) {
// schema for input fields
auto fielda = field("a", utf8());
auto schema = arrow::schema({fielda});
auto pool_ = arrow::default_memory_pool();
// build expression.
auto node_a = TreeExprBuilder::MakeField(fielda);
auto pattern_node = TreeExprBuilder::MakeStringLiteral("%yellow%");
auto like_yellow =
TreeExprBuilder::MakeFunction("like", {node_a, pattern_node}, arrow::boolean());
auto condition = TreeExprBuilder::MakeCondition(like_yellow);
std::shared_ptr<Filter> filter;
ASSERT_OK(Filter::Make(schema, condition, TestConfiguration(), &filter));
FastUtf8DataGenerator data_generator(32);
FilterEvaluator evaluator(filter);
Status status = TimedEvaluate<arrow::StringType, std::string>(
schema, evaluator, data_generator, pool_, 1 * MILLION, 16 * THOUSAND, state);
ASSERT_TRUE(status.ok());
}
static void TimedTestCastFloatFromString(benchmark::State& state) {
auto field_a = field("a", utf8());
auto schema = arrow::schema({field_a});
auto pool = arrow::default_memory_pool();
auto field_result = field("res", arrow::float64());
auto node_a = TreeExprBuilder::MakeField(field_a);
auto fn = TreeExprBuilder::MakeFunction("castFLOAT8", {node_a}, arrow::float64());
auto expr = TreeExprBuilder::MakeExpression(fn, field_result);
std::shared_ptr<Projector> projector;
ASSERT_OK(Projector::Make(schema, {expr}, TestConfiguration(), &projector));
Utf8FloatDataGenerator data_generator;
ProjectEvaluator evaluator(projector);
Status status = TimedEvaluate<arrow::StringType, std::string>(
schema, evaluator, data_generator, pool, 1 * MILLION, 16 * THOUSAND, state);
ASSERT_TRUE(status.ok());
}
static void TimedTestCastIntFromString(benchmark::State& state) {
auto field_a = field("a", utf8());
auto schema = arrow::schema({field_a});
auto pool = arrow::default_memory_pool();
auto field_result = field("res", int32());
auto node_a = TreeExprBuilder::MakeField(field_a);
auto fn = TreeExprBuilder::MakeFunction("castINT", {node_a}, int32());
auto expr = TreeExprBuilder::MakeExpression(fn, field_result);
std::shared_ptr<Projector> projector;
ASSERT_OK(Projector::Make(schema, {expr}, TestConfiguration(), &projector));
Utf8IntDataGenerator data_generator;
ProjectEvaluator evaluator(projector);
Status status = TimedEvaluate<arrow::StringType, std::string>(
schema, evaluator, data_generator, pool, 1 * MILLION, 16 * THOUSAND, state);
ASSERT_TRUE(status.ok());
}
static void TimedTestAllocs(benchmark::State& state) {
// schema for input fields
auto field_a = field("a", arrow::utf8());
auto schema = arrow::schema({field_a});
auto pool_ = arrow::default_memory_pool();
// output field
auto field_res = field("res", int32());
// Build expression
auto node_a = TreeExprBuilder::MakeField(field_a);
auto upper = TreeExprBuilder::MakeFunction("upper", {node_a}, utf8());
auto length = TreeExprBuilder::MakeFunction("octet_length", {upper}, int32());
auto expr = TreeExprBuilder::MakeExpression(length, field_res);
std::shared_ptr<Projector> projector;
ASSERT_OK(Projector::Make(schema, {expr}, TestConfiguration(), &projector));
FastUtf8DataGenerator data_generator(64);
ProjectEvaluator evaluator(projector);
Status status = TimedEvaluate<arrow::StringType, std::string>(
schema, evaluator, data_generator, pool_, 1 * MILLION, 16 * THOUSAND, state);
ASSERT_TRUE(status.ok());
}
static void TimedTestOutputStringAllocs(benchmark::State& state) {
// schema for input fields
auto field_a = field("abcdefghijklmnopqrstuvwxyz", arrow::utf8());
auto schema = arrow::schema({field_a});
auto pool_ = arrow::default_memory_pool();
// output field
auto field_res = field("res", utf8());
// Build expression
auto node_a = TreeExprBuilder::MakeField(field_a);
auto upper = TreeExprBuilder::MakeFunction("upper", {node_a}, utf8());
auto length = TreeExprBuilder::MakeFunction("octet_length", {upper}, int32());
auto expr = TreeExprBuilder::MakeExpression(upper, field_res);
std::shared_ptr<Projector> projector;
ASSERT_OK(Projector::Make(schema, {expr}, TestConfiguration(), &projector));
FastUtf8DataGenerator data_generator(64);
ProjectEvaluator evaluator(projector);
ASSERT_OK((TimedEvaluate<arrow::StringType, std::string>(
schema, evaluator, data_generator, pool_, 1 * MILLION, 16 * THOUSAND, state)));
}
// following two tests are for benchmark optimization of
// in expr. will be used in follow-up PRs to optimize in expr.
static void TimedTestMultiOr(benchmark::State& state) {
// schema for input fields
auto fielda = field("a", utf8());
auto schema = arrow::schema({fielda});
auto pool_ = arrow::default_memory_pool();
// output fields
auto field_result = field("res", boolean());
// build expression.
// booleanOr(a = string1, a = string2, ..)
auto node_a = TreeExprBuilder::MakeField(fielda);
NodeVector boolean_functions;
FastUtf8DataGenerator data_generator1(250);
for (int thresh = 1; thresh <= 32; thresh++) {
auto literal = TreeExprBuilder::MakeStringLiteral(data_generator1.GenerateData());
auto condition = TreeExprBuilder::MakeFunction("equal", {node_a, literal}, boolean());
boolean_functions.push_back(condition);
}
auto boolean_or = TreeExprBuilder::MakeOr(boolean_functions);
auto expr = TreeExprBuilder::MakeExpression(boolean_or, field_result);
// Build a projector for the expressions.
std::shared_ptr<Projector> projector;
ASSERT_OK(Projector::Make(schema, {expr}, TestConfiguration(), &projector));
FastUtf8DataGenerator data_generator(250);
ProjectEvaluator evaluator(projector);
Status status = TimedEvaluate<arrow::StringType, std::string>(
schema, evaluator, data_generator, pool_, 100 * THOUSAND, 16 * THOUSAND, state);
ASSERT_OK(status);
}
static void TimedTestInExpr(benchmark::State& state) {
// schema for input fields
auto fielda = field("a", utf8());
auto schema = arrow::schema({fielda});
auto pool_ = arrow::default_memory_pool();
// output fields
auto field_result = field("res", boolean());
// build expression.
// a in (string1, string2, ..)
auto node_a = TreeExprBuilder::MakeField(fielda);
std::unordered_set<std::string> values;
FastUtf8DataGenerator data_generator1(250);
for (int i = 1; i <= 32; i++) {
values.insert(data_generator1.GenerateData());
}
auto boolean_or = TreeExprBuilder::MakeInExpressionString(node_a, values);
auto expr = TreeExprBuilder::MakeExpression(boolean_or, field_result);
// Build a projector for the expressions.
std::shared_ptr<Projector> projector;
ASSERT_OK(Projector::Make(schema, {expr}, TestConfiguration(), &projector));
FastUtf8DataGenerator data_generator(250);
ProjectEvaluator evaluator(projector);
Status status = TimedEvaluate<arrow::StringType, std::string>(
schema, evaluator, data_generator, pool_, 100 * THOUSAND, 16 * THOUSAND, state);
ASSERT_OK(status);
}
static void DoDecimalAdd3(benchmark::State& state, int32_t precision, int32_t scale,
bool large = false) {
// schema for input fields
auto decimal_type = std::make_shared<arrow::Decimal128Type>(precision, scale);
auto field0 = field("f0", decimal_type);
auto field1 = field("f1", decimal_type);
auto field2 = field("f2", decimal_type);
auto schema = arrow::schema({field0, field1, field2});
Decimal128TypePtr add2_type;
auto status = DecimalTypeUtil::GetResultType(DecimalTypeUtil::kOpAdd,
{decimal_type, decimal_type}, &add2_type);
Decimal128TypePtr output_type;
status = DecimalTypeUtil::GetResultType(DecimalTypeUtil::kOpAdd,
{add2_type, decimal_type}, &output_type);
// output field
auto field_sum = field("add", output_type);
// Build expression
auto part_sum = TreeExprBuilder::MakeFunction(
"add", {TreeExprBuilder::MakeField(field1), TreeExprBuilder::MakeField(field2)},
add2_type);
auto sum = TreeExprBuilder::MakeFunction(
"add", {TreeExprBuilder::MakeField(field0), part_sum}, output_type);
auto sum_expr = TreeExprBuilder::MakeExpression(sum, field_sum);
std::shared_ptr<Projector> projector;
status = Projector::Make(schema, {sum_expr}, TestConfiguration(), &projector);
EXPECT_TRUE(status.ok());
Decimal128DataGenerator data_generator(large);
ProjectEvaluator evaluator(projector);
status = TimedEvaluate<arrow::Decimal128Type, arrow::Decimal128>(
schema, evaluator, data_generator, arrow::default_memory_pool(), 1 * MILLION,
16 * THOUSAND, state);
ASSERT_OK(status);
}
static void DoDecimalAdd2(benchmark::State& state, int32_t precision, int32_t scale,
bool large = false) {
// schema for input fields
auto decimal_type = std::make_shared<arrow::Decimal128Type>(precision, scale);
auto field0 = field("f0", decimal_type);
auto field1 = field("f1", decimal_type);
auto schema = arrow::schema({field0, field1});
Decimal128TypePtr output_type;
auto status = DecimalTypeUtil::GetResultType(
DecimalTypeUtil::kOpAdd, {decimal_type, decimal_type}, &output_type);
// output field
auto field_sum = field("add", output_type);
// Build expression
auto sum = TreeExprBuilder::MakeExpression("add", {field0, field1}, field_sum);
std::shared_ptr<Projector> projector;
status = Projector::Make(schema, {sum}, TestConfiguration(), &projector);
EXPECT_TRUE(status.ok());
Decimal128DataGenerator data_generator(large);
ProjectEvaluator evaluator(projector);
status = TimedEvaluate<arrow::Decimal128Type, arrow::Decimal128>(
schema, evaluator, data_generator, arrow::default_memory_pool(), 1 * MILLION,
16 * THOUSAND, state);
ASSERT_OK(status);
}
static void TimedTestExprCompilation(benchmark::State& state) {
int64_t iteration = 0;
for (auto _ : state) {
// schema for input fields
auto field0 = field("f0", int64());
auto field1 = field("f1", int64());
auto literal = TreeExprBuilder::MakeLiteral(iteration);
auto schema = arrow::schema({field0, field1});
// output field
auto field_add = field("c1", int64());
auto field_less_than = field("c2", boolean());
// Build expression
auto add_func = TreeExprBuilder::MakeFunction(
"add", {TreeExprBuilder::MakeField(field0), literal}, int64());
auto less_than_func = TreeExprBuilder::MakeFunction(
"less_than", {TreeExprBuilder::MakeField(field1), literal}, boolean());
auto expr_0 = TreeExprBuilder::MakeExpression(add_func, field_add);
auto expr_1 = TreeExprBuilder::MakeExpression(less_than_func, field_less_than);
std::shared_ptr<Projector> projector;
ASSERT_OK(Projector::Make(schema, {expr_0, expr_1}, TestConfiguration(), &projector));
++iteration;
}
}
static void DecimalAdd2Fast(benchmark::State& state) {
// use lesser precision to test the fast-path
DoDecimalAdd2(state, DecimalTypeUtil::kMaxPrecision - 6, 18);
}
static void DecimalAdd2LeadingZeroes(benchmark::State& state) {
// use max precision to test the large-integer-path
DoDecimalAdd2(state, DecimalTypeUtil::kMaxPrecision, 6);
}
static void DecimalAdd2LeadingZeroesWithDiv(benchmark::State& state) {
// use max precision to test the large-integer-path
DoDecimalAdd2(state, DecimalTypeUtil::kMaxPrecision, 18);
}
static void DecimalAdd2Large(benchmark::State& state) {
// use max precision to test the large-integer-path
DoDecimalAdd2(state, DecimalTypeUtil::kMaxPrecision, 18, true);
}
static void DecimalAdd3Fast(benchmark::State& state) {
// use lesser precision to test the fast-path
DoDecimalAdd3(state, DecimalTypeUtil::kMaxPrecision - 6, 18);
}
static void DecimalAdd3LeadingZeroes(benchmark::State& state) {
// use max precision to test the large-integer-path
DoDecimalAdd3(state, DecimalTypeUtil::kMaxPrecision, 6);
}
static void DecimalAdd3LeadingZeroesWithDiv(benchmark::State& state) {
// use max precision to test the large-integer-path
DoDecimalAdd3(state, DecimalTypeUtil::kMaxPrecision, 18);
}
static void DecimalAdd3Large(benchmark::State& state) {
// use max precision to test the large-integer-path
DoDecimalAdd3(state, DecimalTypeUtil::kMaxPrecision, 18, true);
}
BENCHMARK(TimedTestExprCompilation)->Unit(benchmark::kMicrosecond);
BENCHMARK(TimedTestAdd3)->Unit(benchmark::kMicrosecond);
BENCHMARK(TimedTestBigNested)->Unit(benchmark::kMicrosecond);
BENCHMARK(TimedTestExtractYear)->Unit(benchmark::kMicrosecond);
BENCHMARK(TimedTestFilterAdd2)->Unit(benchmark::kMicrosecond);
BENCHMARK(TimedTestFilterLike)->Unit(benchmark::kMicrosecond);
BENCHMARK(TimedTestCastFloatFromString)->Unit(benchmark::kMicrosecond);
BENCHMARK(TimedTestCastIntFromString)->Unit(benchmark::kMicrosecond);
BENCHMARK(TimedTestAllocs)->Unit(benchmark::kMicrosecond);
BENCHMARK(TimedTestOutputStringAllocs)->Unit(benchmark::kMicrosecond);
BENCHMARK(TimedTestMultiOr)->Unit(benchmark::kMicrosecond);
BENCHMARK(TimedTestInExpr)->Unit(benchmark::kMicrosecond);
BENCHMARK(DecimalAdd2Fast)->Unit(benchmark::kMicrosecond);
BENCHMARK(DecimalAdd2LeadingZeroes)->Unit(benchmark::kMicrosecond);
BENCHMARK(DecimalAdd2LeadingZeroesWithDiv)->Unit(benchmark::kMicrosecond);
BENCHMARK(DecimalAdd2Large)->Unit(benchmark::kMicrosecond);
BENCHMARK(DecimalAdd3Fast)->Unit(benchmark::kMicrosecond);
BENCHMARK(DecimalAdd3LeadingZeroes)->Unit(benchmark::kMicrosecond);
BENCHMARK(DecimalAdd3LeadingZeroesWithDiv)->Unit(benchmark::kMicrosecond);
BENCHMARK(DecimalAdd3Large)->Unit(benchmark::kMicrosecond);
} // namespace gandiva