diff options
author | Haavard <havardpe@yahoo-inc.com> | 2017-02-24 13:44:53 +0000 |
---|---|---|
committer | Haavard <havardpe@yahoo-inc.com> | 2017-02-24 13:44:53 +0000 |
commit | 08dbb5ac796492a55bc0921cfb25d06e63311c51 (patch) | |
tree | 7b5b2ceabf01bcc398f85452453691276222d191 /searchlib | |
parent | 93110996e8384cb4e191084583c7c068fd91a566 (diff) |
add and test lazy ranking expression executor
Diffstat (limited to 'searchlib')
-rw-r--r-- | searchlib/src/tests/fef/rank_program/rank_program_test.cpp | 47 | ||||
-rw-r--r-- | searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp | 46 |
2 files changed, 91 insertions, 2 deletions
diff --git a/searchlib/src/tests/fef/rank_program/rank_program_test.cpp b/searchlib/src/tests/fef/rank_program/rank_program_test.cpp index 07f82db390e..a92a3572a36 100644 --- a/searchlib/src/tests/fef/rank_program/rank_program_test.cpp +++ b/searchlib/src/tests/fef/rank_program/rank_program_test.cpp @@ -3,7 +3,9 @@ #include <vespa/vespalib/stllike/string.h> #include <vespa/vespalib/util/stringfmt.h> #include <vespa/searchlib/features/valuefeature.h> +#include <vespa/searchlib/features/rankingexpressionfeature.h> #include <vespa/searchlib/fef/blueprintfactory.h> +#include <vespa/searchlib/fef/indexproperties.h> #include <vespa/searchlib/fef/test/indexenvironment.h> #include <vespa/searchlib/fef/test/queryenvironment.h> #include <vespa/searchlib/fef/test/plugin/sum.h> @@ -58,6 +60,10 @@ size_t count_const_features(const RankProgram &program) { return count(program, [](const LazyValue &value){ return value.is_const(); }); } +vespalib::string expr_feature(const vespalib::string &name) { + return vespalib::make_string("rankingExpression(%s)", name.c_str()); +} + struct Fixture { BlueprintFactory factory; IndexEnvironment indexEnv; @@ -72,10 +78,23 @@ struct Fixture { factory.addPrototype(Blueprint::SP(new DocidBlueprint())); factory.addPrototype(Blueprint::SP(new DoubleBlueprint())); factory.addPrototype(Blueprint::SP(new ImpureValueBlueprint())); + factory.addPrototype(Blueprint::SP(new RankingExpressionBlueprint())); factory.addPrototype(Blueprint::SP(new SumBlueprint())); factory.addPrototype(Blueprint::SP(new TrackingBlueprint(track_cnt))); factory.addPrototype(Blueprint::SP(new ValueBlueprint())); } + Fixture &lazy_expressions(bool value) { + indexEnv.getProperties().add(indexproperties::eval::LazyExpressions::NAME, + value ? "true" : "false"); + return *this; + } + Fixture &add_expr(const vespalib::string &name, const vespalib::string &expr) { + vespalib::string feature_name = expr_feature(name); + vespalib::string expr_name = feature_name + ".rankingScript"; + indexEnv.getProperties().add(expr_name, expr); + add(feature_name); + return *this; + } Fixture &add(const vespalib::string &feature) { resolver->addSeed(feature); return *this; @@ -262,4 +281,32 @@ TEST_F("require that auto-unboxing of non-const object values work", Fixture()) EXPECT_EQUAL(0u, count_const_features(f1.program)); } +TEST_F("require that non-lazy ranking expression always calculates all inputs", Fixture()) { + f1.lazy_expressions(false); + f1.add_expr("rank", "if(docid<10,track(ivalue(1)),track(ivalue(2)))"); + f1.compile(); + EXPECT_EQUAL(6u, f1.program.num_executors()); + EXPECT_EQUAL(6u, count_features(f1.program)); + EXPECT_EQUAL(0u, count_const_features(f1.program)); + EXPECT_EQUAL(f1.track_cnt, 0u); + EXPECT_EQUAL(f1.get(expr_feature("rank"), 5), 1.0); + EXPECT_EQUAL(f1.track_cnt, 2u); + EXPECT_EQUAL(f1.get(expr_feature("rank"), 15), 2.0); + EXPECT_EQUAL(f1.track_cnt, 4u); +} + +TEST_F("require that lazy ranking expression only calculates needed inputs", Fixture()) { + f1.lazy_expressions(true); + f1.add_expr("rank", "if(docid<10,track(ivalue(1)),track(ivalue(2)))"); + f1.compile(); + EXPECT_EQUAL(6u, f1.program.num_executors()); + EXPECT_EQUAL(6u, count_features(f1.program)); + EXPECT_EQUAL(0u, count_const_features(f1.program)); + EXPECT_EQUAL(f1.track_cnt, 0u); + EXPECT_EQUAL(f1.get(expr_feature("rank"), 5), 1.0); + EXPECT_EQUAL(f1.track_cnt, 1u); + EXPECT_EQUAL(f1.get(expr_feature("rank"), 15), 2.0); + EXPECT_EQUAL(f1.track_cnt, 2u); +} + TEST_MAIN() { TEST_RUN_ALL(); } diff --git a/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp b/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp index 8c966b2456a..6839ee83ad7 100644 --- a/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp +++ b/searchlib/src/vespa/searchlib/features/rankingexpressionfeature.cpp @@ -57,6 +57,22 @@ public: //----------------------------------------------------------------------------- /** + * Implements the executor for lazy compiled ranking expressions + **/ +class LazyCompiledRankingExpressionExecutor : public fef::FeatureExecutor +{ +private: + using function_type = CompiledFunction::lazy_function; + function_type _ranking_function; + +public: + LazyCompiledRankingExpressionExecutor(const CompiledFunction &compiled_function); + void execute(uint32_t docId) override; +}; + +//----------------------------------------------------------------------------- + +/** * Implements the executor for interpreted ranking expressions (with tensor support) **/ class InterpretedRankingExpressionExecutor : public fef::FeatureExecutor @@ -98,6 +114,23 @@ CompiledRankingExpressionExecutor::execute(uint32_t) //----------------------------------------------------------------------------- +using Context = fef::FeatureExecutor::Inputs; +double resolve_input(void *ctx, size_t idx) { return ((const Context *)(ctx))->get_number(idx); } +Context *make_ctx(const Context &inputs) { return const_cast<Context *>(&inputs); } + +LazyCompiledRankingExpressionExecutor::LazyCompiledRankingExpressionExecutor(const CompiledFunction &compiled_function) + : _ranking_function(compiled_function.get_lazy_function()) +{ +} + +void +LazyCompiledRankingExpressionExecutor::execute(uint32_t) +{ + outputs().set_number(0, _ranking_function(resolve_input, make_ctx(inputs()))); +} + +//----------------------------------------------------------------------------- + InterpretedRankingExpressionExecutor::InterpretedRankingExpressionExecutor(const InterpretedFunction &function, ConstArrayRef<char> input_is_object) : _function(function), @@ -199,7 +232,11 @@ RankingExpressionBlueprint::setup(const fef::IIndexEnvironment &env, // avoid costly compilation when only verifying setup if (env.getFeatureMotivation() != env.FeatureMotivation::VERIFY_SETUP) { if (do_compile) { - _compile_token = CompileCache::compile(rank_function, PassParams::ARRAY); + if (fef::indexproperties::eval::LazyExpressions::check(env.getProperties())) { + _compile_token = CompileCache::compile(rank_function, PassParams::LAZY); + } else { + _compile_token = CompileCache::compile(rank_function, PassParams::ARRAY); + } } else { _interpreted_function.reset(new InterpretedFunction(DefaultTensorEngine::ref(), rank_function, node_types)); } @@ -225,7 +262,12 @@ RankingExpressionBlueprint::createExecutor(const fef::IQueryEnvironment &, vespa return stash.create<InterpretedRankingExpressionExecutor>(*_interpreted_function, input_is_object); } assert(_compile_token.get() != nullptr); // will be nullptr for VERIFY_SETUP feature motivation - return stash.create<CompiledRankingExpressionExecutor>(_compile_token->get()); + if (_compile_token->get().pass_params() == PassParams::ARRAY) { + return stash.create<CompiledRankingExpressionExecutor>(_compile_token->get()); + } else { + assert(_compile_token->get().pass_params() == PassParams::LAZY); + return stash.create<LazyCompiledRankingExpressionExecutor>(_compile_token->get()); + } } //----------------------------------------------------------------------------- |