1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
|
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <vespa/eval/eval/fast_value.h>
#include <vespa/eval/eval/simple_value.h>
#include <vespa/eval/instruction/mapped_lookup.h>
#include <vespa/eval/eval/test/eval_fixture.h>
#include <vespa/eval/eval/test/gen_spec.h>
#include <vespa/vespalib/gtest/gtest.h>
using namespace vespalib::eval;
using namespace vespalib::eval::test;
//-----------------------------------------------------------------------------
struct FunInfo {
using LookFor = MappedLookup;
bool expect_mutable;
FunInfo(bool expect_mutable_in)
: expect_mutable(expect_mutable_in) {}
void verify(const LookFor &fun) const {
EXPECT_EQ(fun.result_is_mutable(), expect_mutable);
}
};
void verify_optimized_cell_types(const vespalib::string &expr) {
auto same_stable_types = CellTypeSpace(CellTypeUtils::list_stable_types(), 2).same();
auto same_unstable_types = CellTypeSpace(CellTypeUtils::list_unstable_types(), 2).same();
auto different_types = CellTypeSpace(CellTypeUtils::list_types(), 2).different();
EvalFixture::verify<FunInfo>(expr, {FunInfo(false)}, same_stable_types);
EvalFixture::verify<FunInfo>(expr, {}, same_unstable_types);
EvalFixture::verify<FunInfo>(expr, {}, different_types);
}
void verify_optimized(const vespalib::string &expr, bool expect_mutable = false) {
CellTypeSpace just_float({CellType::FLOAT}, 2);
EvalFixture::verify<FunInfo>(expr, {FunInfo(expect_mutable)}, just_float);
}
void verify_not_optimized(const vespalib::string &expr) {
CellTypeSpace just_float({CellType::FLOAT}, 2);
EvalFixture::verify<FunInfo>(expr, {}, just_float);
}
//-----------------------------------------------------------------------------
TEST(MappedLookup, expression_can_be_optimized) {
verify_optimized_cell_types("reduce(x1_1*x5_1y5,sum,x)");
}
TEST(MappedLookup, key_and_map_can_be_swapped) {
verify_optimized("reduce(x5_1y5*x1_1,sum,x)");
}
TEST(MappedLookup, trivial_indexed_dimensions_are_ignored) {
verify_optimized("reduce(c1d1x1_1*a1b1x5_1y5,sum,x,c,d,a,b)");
verify_optimized("reduce(c1d1x1_1*a1b1x5_1y5,sum,x,c,a)");
verify_optimized("reduce(c1d1x1_1*a1b1x5_1y5,sum,x)");
}
TEST(MappedLookup, mutable_map_gives_mutable_result) {
verify_optimized("reduce(@x1_1*x5_1y5,sum,x)", false);
verify_optimized("reduce(x1_1*@x5_1y5,sum,x)", true);
verify_optimized("reduce(@x5_1y5*x1_1,sum,x)", true);
verify_optimized("reduce(x5_1y5*@x1_1,sum,x)", false);
verify_optimized("reduce(@x5_1y5*@x1_1,sum,x)", true);
}
TEST(MappedLookup, similar_expressions_are_not_optimized) {
verify_not_optimized("reduce(x1_1*x5_1,sum,x)");
verify_not_optimized("reduce(x1_1*x5_1y5,sum,y)");
verify_not_optimized("reduce(x1_1*x5_1y5,sum)");
verify_not_optimized("reduce(x1_1*x5_1y5z8,sum,x,y)");
verify_not_optimized("reduce(x1_1*x5_1y5,prod,x)");
verify_not_optimized("reduce(x1_1y3_3*x5_1y3_2z5,sum,x)");
verify_not_optimized("reduce(x1_1y3_3*x5_1y3_2z5,sum,x,y)");
verify_not_optimized("reduce(x1_1y5*x5_1z5,sum,x)");
}
enum class KeyType { EMPTY, UNIT, SCALING, MULTI };
GenSpec make_key(KeyType type) {
switch (type) {
case KeyType::EMPTY: return GenSpec().cells_float().map("x", {});
case KeyType::UNIT: return GenSpec().cells_float().map("x", {"1"}).seq({1.0});
case KeyType::SCALING: return GenSpec().cells_float().map("x", {"1"}).seq({5.0});
case KeyType::MULTI: return GenSpec().cells_float().map("x", {"1", "2", "3"}).seq({1.0});
}
abort();
}
enum class MapType { EMPTY, SMALL, MEDIUM, LARGE1, LARGE2, LARGE3 };
GenSpec make_map(MapType type) {
switch (type) {
case MapType::EMPTY: return GenSpec().cells_float().idx("y", 5).map("x", {});
case MapType::SMALL: return GenSpec().cells_float().idx("y", 5).map("x", {"1"}).seq(N(10));
case MapType::MEDIUM: return GenSpec().cells_float().idx("y", 5).map("x", {"1", "2"}).seq(N(10));
case MapType::LARGE1: return GenSpec().cells_float().idx("y", 5).map("x", 5, 100).seq(N(10));
case MapType::LARGE2: return GenSpec().cells_float().idx("y", 5).map("x", 5, 2).seq(N(10));
case MapType::LARGE3: return GenSpec().cells_float().idx("y", 5).map("x", 5, 1).seq(N(10));
}
abort();
}
std::vector<MapType> map_types_for(KeyType key_type) {
if (key_type == KeyType::MULTI) {
return {MapType::EMPTY, MapType::SMALL, MapType::MEDIUM, MapType::LARGE1, MapType::LARGE2, MapType::LARGE3};
} else {
return {MapType::EMPTY, MapType::SMALL, MapType::MEDIUM};
}
}
TEST(MappedLookup, test_case_interactions) {
for (bool mutable_map: {false, true}) {
vespalib::string expr = mutable_map ? "reduce(a*@b,sum,x)" : "reduce(a*b,sum,x)";
for (KeyType key_type: {KeyType::EMPTY, KeyType::UNIT, KeyType::SCALING, KeyType::MULTI}) {
auto key = make_key(key_type);
for (MapType map_type: map_types_for(key_type)) {
auto map = make_map(map_type);
EvalFixture::verify<FunInfo>(expr, {FunInfo(mutable_map)}, {key,map});
}
}
}
}
//-----------------------------------------------------------------------------
GTEST_MAIN_RUN_ALL_TESTS()
|