diff options
author | Tor Egge <Tor.Egge@broadpark.no> | 2017-02-24 14:55:33 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-24 14:55:33 +0100 |
commit | f03d03c6402fa7c0b96e21b8664733c6ec3feeb1 (patch) | |
tree | 99747879b8c82cacbe95885459baf7577c420714 /searchlib | |
parent | 2269a8f3dab0785fe8dab0a6f35a74a05a0c2a10 (diff) | |
parent | 08dbb5ac796492a55bc0921cfb25d06e63311c51 (diff) |
Merge pull request #1861 from yahoo/havardpe/lazy-rank-expressions
Havardpe/lazy rank expressions
Diffstat (limited to 'searchlib')
5 files changed, 123 insertions, 2 deletions
diff --git a/searchlib/src/tests/fef/properties/properties_test.cpp b/searchlib/src/tests/fef/properties/properties_test.cpp index 7ca79599d5c..7f5594ea821 100644 --- a/searchlib/src/tests/fef/properties/properties_test.cpp +++ b/searchlib/src/tests/fef/properties/properties_test.cpp @@ -215,6 +215,14 @@ TEST("test stuff") { } { // test index properties known by the framework + { // vespa.eval.lazy_expressions + EXPECT_EQUAL(eval::LazyExpressions::NAME, vespalib::string("vespa.eval.lazy_expressions")); + EXPECT_EQUAL(eval::LazyExpressions::DEFAULT_VALUE, vespalib::string("false")); + Properties p; + EXPECT_TRUE(!eval::LazyExpressions::check(p)); + p.add("vespa.eval.lazy_expressions", "true"); + EXPECT_TRUE(eval::LazyExpressions::check(p)); + } { // vespa.rank.firstphase EXPECT_EQUAL(rank::FirstPhase::NAME, vespalib::string("vespa.rank.firstphase")); EXPECT_EQUAL(rank::FirstPhase::DEFAULT_VALUE, vespalib::string("nativeRank")); 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 62c6ffb1559..261be51a3c7 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; @@ -275,4 +294,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()); + } } //----------------------------------------------------------------------------- diff --git a/searchlib/src/vespa/searchlib/fef/indexproperties.cpp b/searchlib/src/vespa/searchlib/fef/indexproperties.cpp index 23874924ddb..b75a0f33393 100644 --- a/searchlib/src/vespa/searchlib/fef/indexproperties.cpp +++ b/searchlib/src/vespa/searchlib/fef/indexproperties.cpp @@ -74,6 +74,19 @@ checkIfTrue(const Properties &props, const vespalib::string &name, } +namespace eval { + +const vespalib::string LazyExpressions::NAME("vespa.eval.lazy_expressions"); +const vespalib::string LazyExpressions::DEFAULT_VALUE("false"); + +bool +LazyExpressions::check(const Properties &props) +{ + return checkIfTrue(props, NAME, DEFAULT_VALUE); +} + +} // namespace eval + namespace rank { const vespalib::string FirstPhase::NAME("vespa.rank.firstphase"); diff --git a/searchlib/src/vespa/searchlib/fef/indexproperties.h b/searchlib/src/vespa/searchlib/fef/indexproperties.h index 2a37f2f93fd..4a97535879b 100644 --- a/searchlib/src/vespa/searchlib/fef/indexproperties.h +++ b/searchlib/src/vespa/searchlib/fef/indexproperties.h @@ -21,6 +21,17 @@ class Properties; **/ namespace indexproperties { +namespace eval { + +// lazy evaluation of expressions. affects rank/summary/dump +struct LazyExpressions { + static const vespalib::string NAME; + static const vespalib::string DEFAULT_VALUE; + static bool check(const Properties &props); +}; + +} // namespace eval + namespace rank { /** |