Skip to content

Commit 7a998cd

Browse files
authored
Pass partition ID to rand expression for achieving genuine random distribution globally (apache#80)
* Add an offset for seed to achieve genuine random distribution globally * Filter out rand in projection cache * Evaluate expr with literal input for getting seed value
1 parent b947d5f commit 7a998cd

6 files changed

Lines changed: 117 additions & 14 deletions

File tree

cpp/src/gandiva/function_registry_math_ops.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ std::vector<NativeFunction> GetMathOpsFunctionRegistry() {
9898
NativeFunction::kNeedsFunctionHolder),
9999
NativeFunction("random", {"rand"}, DataTypeVector{int64()}, float64(),
100100
kResultNullNever, "gdv_fn_random_with_seed64",
101+
NativeFunction::kNeedsFunctionHolder),
102+
NativeFunction("random", {"rand"}, DataTypeVector{int64(), int32()}, float64(),
103+
kResultNullNever, "gdv_fn_random_with_seed64_offset",
101104
NativeFunction::kNeedsFunctionHolder)};
102105

103106
return math_fn_registry_;

cpp/src/gandiva/gdv_function_stubs.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,13 @@ double gdv_fn_random_with_seed64(int64_t ptr, int64_t seed, bool seed_validity)
118118
return (*holder)();
119119
}
120120

121+
double gdv_fn_random_with_seed64_offset(int64_t ptr, int64_t seed, bool seed_validity,
122+
int32_t offset, bool offset_validity) {
123+
gandiva::RandomGeneratorHolder* holder =
124+
reinterpret_cast<gandiva::RandomGeneratorHolder*>(ptr);
125+
return (*holder)();
126+
}
127+
121128
int64_t gdv_fn_to_date_utf8_utf8(int64_t context_ptr, int64_t holder_ptr,
122129
const char* data, int data_len, bool in1_validity,
123130
const char* pattern, int pattern_len, bool in2_validity,

cpp/src/gandiva/gdv_function_stubs.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ double gdv_fn_random_with_seed(int64_t ptr, int32_t seed, bool seed_validity);
8989

9090
double gdv_fn_random_with_seed64(int64_t ptr, int64_t seed, bool seed_validity);
9191

92+
double gdv_fn_random_with_seed64_offset(int64_t ptr, int64_t seed, bool seed_validity,
93+
int32_t offset, bool offset_validity);
94+
9295
GANDIVA_EXPORT
9396
const char* gdv_fn_sha256_decimal128(int64_t context, int64_t x_high, uint64_t x_low,
9497
int32_t x_precision, int32_t x_scale,

cpp/src/gandiva/projector.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,10 @@ Status Projector::Make(SchemaPtr schema, const ExpressionVector& exprs,
186186
// Instantiate the projector with the completely built llvm generator
187187
*projector = std::shared_ptr<Projector>(
188188
new Projector(std::move(llvm_gen), schema, output_fields, configuration));
189-
cache.PutModule(cache_key, *projector);
189+
// For statful projection, we should not cache it.
190+
if (cache_key.ToString().find(" rand(") == std::string::npos) {
191+
cache.PutModule(cache_key, *projector);
192+
}
190193

191194
return Status::OK();
192195
}

cpp/src/gandiva/random_generator_holder.cc

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,34 +17,91 @@
1717

1818
#include "gandiva/random_generator_holder.h"
1919
#include "gandiva/node.h"
20+
#include "gandiva/projector.h"
21+
#include "arrow/memory_pool.h"
22+
#include "arrow/status.h"
23+
#include "gandiva/tree_expr_builder.h"
24+
//#include "gandiva/tests/test_util.h"
25+
#include "arrow/type_traits.h"
26+
#include "arrow/array/builder_binary.h"
27+
#include "arrow/array/builder_primitive.h"
28+
#include "arrow/array/builder_base.h"
29+
#include <type_traits>
30+
2031

2132
namespace gandiva {
2233
Status RandomGeneratorHolder::Make(const FunctionNode& node,
2334
std::shared_ptr<RandomGeneratorHolder>* holder) {
24-
ARROW_RETURN_IF(node.children().size() > 1,
25-
Status::Invalid("'random' function requires at most one parameter"));
35+
ARROW_RETURN_IF(node.children().size() > 2,
36+
Status::Invalid("'random' function requires at most two parameters"));
2637

2738
if (node.children().size() == 0) {
2839
*holder = std::shared_ptr<RandomGeneratorHolder>(new RandomGeneratorHolder());
2940
return Status::OK();
3041
}
3142

3243
auto literal = dynamic_cast<LiteralNode*>(node.children().at(0).get());
33-
ARROW_RETURN_IF(literal == nullptr,
34-
Status::Invalid("'random' function requires a literal as parameter"));
35-
36-
auto literal_type = literal->return_type()->id();
37-
ARROW_RETURN_IF(
44+
//ARROW_RETURN_IF(literal == nullptr,
45+
// Status::Invalid("'random' function requires a literal as parameter"));
46+
int64_t seed;
47+
if (literal != nullptr) {
48+
auto literal_type = literal->return_type()->id();
49+
ARROW_RETURN_IF(
3850
literal_type != arrow::Type::INT32 && literal_type != arrow::Type::INT64,
3951
Status::Invalid("'random' function requires an int32/int64 literal as parameter"));
40-
41-
if (literal_type == arrow::Type::INT32) {
42-
*holder = std::shared_ptr<RandomGeneratorHolder>(new RandomGeneratorHolder(
43-
literal->is_null() ? 0 : arrow::util::get<int32_t>(literal->holder())));
52+
if (literal_type == arrow::Type::INT32) {
53+
seed = literal->is_null() ? 0 : arrow::util::get<int32_t>(literal->holder());
54+
} else {
55+
seed = literal->is_null() ? 0 : arrow::util::get<int64_t>(literal->holder());
56+
}
4457
} else {
45-
*holder = std::shared_ptr<RandomGeneratorHolder>(new RandomGeneratorHolder(
46-
literal->is_null() ? 0 : arrow::util::get<int64_t>(literal->holder())));
58+
auto first_children_node_ptr = node.children().at(0);
59+
//auto schema = arrow::schema({});
60+
auto f0 = arrow::field("f0", arrow::float64());
61+
auto schema = arrow::schema({f0});
62+
std::shared_ptr<Projector> projector;
63+
// Not actually used.
64+
auto res = field("res", arrow::int32());
65+
auto expr = TreeExprBuilder::MakeExpression(first_children_node_ptr, res);
66+
auto builder = ConfigurationBuilder();
67+
auto config = builder.DefaultConfiguration();
68+
auto status = Projector::Make(schema, {expr}, config, &projector);
69+
arrow::ArrayVector outputs;
70+
arrow::MemoryPool* pool = arrow::default_memory_pool();
71+
72+
//arrow::ArrayVector inputs;
73+
//auto in_batch = arrow::RecordBatch::Make(schema, 0, inputs);
74+
75+
std::vector<int> input0 = {16, 10, -14, 8};
76+
std::vector<bool> validity = {true, true, true, true};
77+
std::shared_ptr<arrow::Array> array0;
78+
//arrow::ArrayFromVector<arrow::DoubleType, double>(validity, values, &array0);
79+
//auto array0 = MakeArrowArray<arrow::DoubleType, double>(input0, validity);
80+
81+
auto type = arrow::TypeTraits<arrow::Int32Type>::type_singleton();
82+
std::unique_ptr<arrow::ArrayBuilder> builder_ptr;
83+
MakeBuilder(pool, type, &builder_ptr);
84+
auto& arrow_array_builder = dynamic_cast<typename arrow::TypeTraits<arrow::Int32Type>::BuilderType&>(*builder_ptr);
85+
for (size_t i = 0; i < input0.size(); ++i) {
86+
arrow_array_builder.Append(input0[i]);
87+
}
88+
arrow_array_builder.Finish(&array0);
89+
90+
auto in_batch = arrow::RecordBatch::Make(schema, 4, {array0});
91+
92+
//arrow::RecordBatch in_batch;
93+
projector->Evaluate(*in_batch, pool, &outputs);
94+
auto result_arr = std::dynamic_pointer_cast<arrow::Int32Array>(outputs.at(0));
95+
//seed = dynamic_cast<int32_t>(result_arr->Value(0));
96+
seed = result_arr->Value(0);
97+
}
98+
// The offset is a partition ID in spark SQL. It is used to achieve genuine random distribution globally.
99+
int32_t offset = 0;
100+
if (node.children().size() > 1) {
101+
auto offset_node = dynamic_cast<LiteralNode*>(node.children().at(1).get());
102+
offset = offset_node->is_null() ? 0 : arrow::util::get<int32_t>(offset_node->holder());
47103
}
104+
*holder = std::shared_ptr<RandomGeneratorHolder>(new RandomGeneratorHolder(seed + offset));
48105
return Status::OK();
49106
}
50107
} // namespace gandiva

cpp/src/gandiva/random_generator_holder_test.cc

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,20 @@ class TestRandGenHolder : public ::testing::Test {
3232
std::make_shared<LiteralNode>(arrow::int32(), LiteralHolder(seed), seed_is_null);
3333
return FunctionNode("rand", {seed_node}, arrow::float64());
3434
}
35+
36+
FunctionNode BuildRandWithSeedFunc(int64_t seed, bool seed_is_null) {
37+
auto seed_node =
38+
std::make_shared<LiteralNode>(arrow::int64(), LiteralHolder(seed), seed_is_null);
39+
return FunctionNode("rand", {seed_node}, arrow::float64());
40+
}
41+
42+
FunctionNode BuildRandWithSeedFunc(int64_t seed, bool seed_is_null, int32_t offset,
43+
bool offset_is_null) {
44+
auto seed_node =
45+
std::make_shared<LiteralNode>(arrow::int64(), LiteralHolder(seed), seed_is_null);
46+
auto offset_node = std::make_shared<LiteralNode>(arrow::int32(), LiteralHolder(offset), offset_is_null);
47+
return FunctionNode("rand", {seed_node, offset_node}, arrow::float64());
48+
}
3549
};
3650

3751
TEST_F(TestRandGenHolder, NoSeed) {
@@ -106,6 +120,22 @@ TEST_F(TestRandGenHolder, WithValidSeedsInLongType) {
106120
EXPECT_NE(random_1(), random_2());
107121
}
108122

123+
// Test valid seed with offset given.
124+
TEST_F(TestRandGenHolder, WithValidSeedsAndOffset) {
125+
std::shared_ptr<RandomGeneratorHolder> rand_gen_holder_1;
126+
std::shared_ptr<RandomGeneratorHolder> rand_gen_holder_2;
127+
FunctionNode rand_func_1 = BuildRandWithSeedFunc(1000L, false);
128+
FunctionNode rand_func_2 = BuildRandWithSeedFunc(900L, false, 100, false);
129+
auto status = RandomGeneratorHolder::Make(rand_func_1, &rand_gen_holder_1);
130+
EXPECT_EQ(status.ok(), true) << status.message();
131+
status = RandomGeneratorHolder::Make(rand_func_2, &rand_gen_holder_2);
132+
EXPECT_EQ(status.ok(), true) << status.message();
133+
134+
auto& random_1 = *rand_gen_holder_1;
135+
auto& random_2 = *rand_gen_holder_2;
136+
EXPECT_EQ(random_1(), random_2());
137+
}
138+
109139
TEST_F(TestRandGenHolder, WithInValidSeed) {
110140
std::shared_ptr<RandomGeneratorHolder> rand_gen_holder_1;
111141
std::shared_ptr<RandomGeneratorHolder> rand_gen_holder_2;

0 commit comments

Comments
 (0)