summaryrefslogtreecommitdiffstats
path: root/eval
diff options
context:
space:
mode:
authorHåvard Pettersen <havardpe@oath.com>2019-11-08 13:45:50 +0000
committerHåvard Pettersen <havardpe@oath.com>2019-11-08 14:28:44 +0000
commit098bb570a3986771a22e88ab24739ac6433d721f (patch)
treecb1ac9f0b59a89802620d4e297a841687a8533fc /eval
parent1ee969c38c200a2faf7f586d0e3077655dffb79e (diff)
optimize dense tensor create
Diffstat (limited to 'eval')
-rw-r--r--eval/CMakeLists.txt3
-rw-r--r--eval/src/tests/tensor/dense_tensor_create_function/CMakeLists.txt8
-rw-r--r--eval/src/tests/tensor/dense_tensor_create_function/dense_tensor_create_function_test.cpp64
-rw-r--r--eval/src/vespa/eval/eval/value_type.cpp12
-rw-r--r--eval/src/vespa/eval/eval/value_type.h1
-rw-r--r--eval/src/vespa/eval/tensor/default_tensor_engine.cpp2
-rw-r--r--eval/src/vespa/eval/tensor/dense/CMakeLists.txt3
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.cpp101
-rw-r--r--eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.h33
9 files changed, 225 insertions, 2 deletions
diff --git a/eval/CMakeLists.txt b/eval/CMakeLists.txt
index d91e35f2d63..b3d4585a176 100644
--- a/eval/CMakeLists.txt
+++ b/eval/CMakeLists.txt
@@ -25,8 +25,8 @@ vespa_define_module(
src/tests/eval/value_cache
src/tests/eval/value_type
src/tests/gp/ponder_nov2017
- src/tests/tensor/dense_dimension_combiner
src/tests/tensor/dense_add_dimension_optimizer
+ src/tests/tensor/dense_dimension_combiner
src/tests/tensor/dense_dot_product_function
src/tests/tensor/dense_fast_rename_optimizer
src/tests/tensor/dense_generic_join
@@ -34,6 +34,7 @@ vespa_define_module(
src/tests/tensor/dense_inplace_map_function
src/tests/tensor/dense_remove_dimension_optimizer
src/tests/tensor/dense_replace_type_function
+ src/tests/tensor/dense_tensor_create_function
src/tests/tensor/dense_xw_product_function
src/tests/tensor/direct_dense_tensor_builder
src/tests/tensor/direct_sparse_tensor_builder
diff --git a/eval/src/tests/tensor/dense_tensor_create_function/CMakeLists.txt b/eval/src/tests/tensor/dense_tensor_create_function/CMakeLists.txt
new file mode 100644
index 00000000000..883f331bda8
--- /dev/null
+++ b/eval/src/tests/tensor/dense_tensor_create_function/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(eval_dense_tensor_create_function_test_app TEST
+ SOURCES
+ dense_tensor_create_function_test.cpp
+ DEPENDS
+ vespaeval
+)
+vespa_add_test(NAME eval_dense_tensor_create_function_test_app COMMAND eval_dense_tensor_create_function_test_app)
diff --git a/eval/src/tests/tensor/dense_tensor_create_function/dense_tensor_create_function_test.cpp b/eval/src/tests/tensor/dense_tensor_create_function/dense_tensor_create_function_test.cpp
new file mode 100644
index 00000000000..0fa09e6c46e
--- /dev/null
+++ b/eval/src/tests/tensor/dense_tensor_create_function/dense_tensor_create_function_test.cpp
@@ -0,0 +1,64 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/eval/eval/tensor_function.h>
+#include <vespa/eval/eval/simple_tensor.h>
+#include <vespa/eval/eval/simple_tensor_engine.h>
+#include <vespa/eval/tensor/default_tensor_engine.h>
+#include <vespa/eval/tensor/dense/dense_tensor_create_function.h>
+#include <vespa/eval/tensor/dense/dense_tensor.h>
+#include <vespa/eval/eval/test/tensor_model.hpp>
+#include <vespa/eval/eval/test/eval_fixture.h>
+
+#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/vespalib/util/stash.h>
+
+using namespace vespalib;
+using namespace vespalib::eval;
+using namespace vespalib::eval::test;
+using namespace vespalib::tensor;
+using namespace vespalib::eval::tensor_function;
+
+const TensorEngine &prod_engine = DefaultTensorEngine::ref();
+
+EvalFixture::ParamRepo make_params() {
+ return EvalFixture::ParamRepo()
+ .add("a", spec(1.0))
+ .add("b", spec(2.0))
+ .add("c", spec(3.0));
+}
+EvalFixture::ParamRepo param_repo = make_params();
+
+void verify(const vespalib::string &expr, size_t expect_optimized_cnt, size_t expect_not_optimized_cnt) {
+ EvalFixture fixture(prod_engine, expr, param_repo, true);
+ EXPECT_EQUAL(fixture.result(), EvalFixture::ref(expr, param_repo));
+ auto info = fixture.find_all<DenseTensorCreateFunction>();
+ EXPECT_EQUAL(info.size(), expect_optimized_cnt);
+ for (size_t i = 0; i < info.size(); ++i) {
+ EXPECT_TRUE(info[i]->result_is_mutable());
+ }
+ EXPECT_EQUAL(fixture.find_all<Create>().size(), expect_not_optimized_cnt);
+}
+
+//-----------------------------------------------------------------------------
+
+TEST("require that tensor create can be optimized") {
+ TEST_DO(verify("tensor(x[3]):{{x:0}:1,{x:1}:2,{x:2}:3}", 1, 0));
+ TEST_DO(verify("tensor(x[3]):{{x:0}:a,{x:1}:b,{x:2}:c}", 1, 0));
+ TEST_DO(verify("tensor<float>(x[3]):{{x:0}:a,{x:1}:b,{x:2}:c}", 1, 0));
+ TEST_DO(verify("tensor(x[3]):{{x:0}:a+b,{x:1}:b-c,{x:2}:c*a}", 1, 0));
+}
+
+TEST("require that tensor create can be optimized with missing cells (padded with 0.0)") {
+ TEST_DO(verify("tensor(x[3],y[5]):{{x:0,y:1}:a,{x:1,y:3}:b,{x:2,y:4}:c}", 1, 0));
+}
+
+TEST("require that tensor create in not optimized for sparse tensor") {
+ TEST_DO(verify("tensor(x{}):{{x:0}:a,{x:1}:b,{x:2}:c}", 0, 1));
+}
+
+TEST("require that tensor create in not optimized for mixed tensor") {
+ TEST_DO(verify("tensor(x{},y[3]):{{x:a,y:0}:a,{x:a,y:1}:b,{x:a,y:2}:c}", 0, 1));
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/eval/src/vespa/eval/eval/value_type.cpp b/eval/src/vespa/eval/eval/value_type.cpp
index d6ba8e83855..688112bbb8a 100644
--- a/eval/src/vespa/eval/eval/value_type.cpp
+++ b/eval/src/vespa/eval/eval/value_type.cpp
@@ -174,6 +174,18 @@ ValueType::is_dense() const
}
size_t
+ValueType::dense_subspace_size() const
+{
+ size_t size = 1;
+ for (const auto &dim : dimensions()) {
+ if (dim.is_indexed()) {
+ size *= dim.size;
+ }
+ }
+ return size;
+}
+
+size_t
ValueType::dimension_index(const vespalib::string &name) const {
return my_dimension_index(_dimensions, name);
}
diff --git a/eval/src/vespa/eval/eval/value_type.h b/eval/src/vespa/eval/eval/value_type.h
index 64003e2636e..df9c582aee8 100644
--- a/eval/src/vespa/eval/eval/value_type.h
+++ b/eval/src/vespa/eval/eval/value_type.h
@@ -58,6 +58,7 @@ public:
bool is_tensor() const { return (_type == Type::TENSOR); }
bool is_sparse() const;
bool is_dense() const;
+ size_t dense_subspace_size() const;
const std::vector<Dimension> &dimensions() const { return _dimensions; }
size_t dimension_index(const vespalib::string &name) const;
std::vector<vespalib::string> dimension_names() const;
diff --git a/eval/src/vespa/eval/tensor/default_tensor_engine.cpp b/eval/src/vespa/eval/tensor/default_tensor_engine.cpp
index f1eb9ff1523..18733b3c733 100644
--- a/eval/src/vespa/eval/tensor/default_tensor_engine.cpp
+++ b/eval/src/vespa/eval/tensor/default_tensor_engine.cpp
@@ -16,6 +16,7 @@
#include "dense/dense_inplace_join_function.h"
#include "dense/dense_inplace_map_function.h"
#include "dense/vector_from_doubles_function.h"
+#include "dense/dense_tensor_create_function.h"
#include <vespa/eval/eval/value.h>
#include <vespa/eval/eval/tensor_spec.h>
#include <vespa/eval/eval/tensor_spec.h>
@@ -258,6 +259,7 @@ DefaultTensorEngine::optimize(const TensorFunction &expr, Stash &stash) const
while (!nodes.empty()) {
const Child &child = nodes.back();
child.set(VectorFromDoublesFunction::optimize(child.get(), stash));
+ child.set(DenseTensorCreateFunction::optimize(child.get(), stash));
child.set(DenseDotProductFunction::optimize(child.get(), stash));
child.set(DenseXWProductFunction::optimize(child.get(), stash));
child.set(DenseFastRenameOptimizer::optimize(child.get(), stash));
diff --git a/eval/src/vespa/eval/tensor/dense/CMakeLists.txt b/eval/src/vespa/eval/tensor/dense/CMakeLists.txt
index 78723fddc17..e071de3e925 100644
--- a/eval/src/vespa/eval/tensor/dense/CMakeLists.txt
+++ b/eval/src/vespa/eval/tensor/dense/CMakeLists.txt
@@ -9,9 +9,10 @@ vespa_add_library(eval_tensor_dense OBJECT
dense_inplace_map_function.cpp
dense_remove_dimension_optimizer.cpp
dense_replace_type_function.cpp
+ dense_tensor.cpp
dense_tensor_address_mapper.cpp
dense_tensor_cells_iterator.cpp
- dense_tensor.cpp
+ dense_tensor_create_function.cpp
dense_tensor_modify.cpp
dense_tensor_reduce.cpp
dense_tensor_view.cpp
diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.cpp b/eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.cpp
new file mode 100644
index 00000000000..d590e870cfe
--- /dev/null
+++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.cpp
@@ -0,0 +1,101 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include "dense_tensor_create_function.h"
+#include "dense_tensor_view.h"
+#include <vespa/eval/eval/operation.h>
+#include <vespa/eval/eval/value.h>
+#include <vespa/eval/tensor/tensor.h>
+
+namespace vespalib::tensor {
+
+using eval::Value;
+using eval::DoubleValue;
+using eval::ValueType;
+using eval::TensorSpec;
+using eval::TensorFunction;
+using Child = eval::TensorFunction::Child;
+using eval::as;
+using namespace eval::tensor_function;
+using namespace eval::operation;
+
+namespace {
+
+template <typename CT>
+void my_tensor_create_op(eval::InterpretedFunction::State &state, uint64_t param) {
+ const auto *self = (const DenseTensorCreateFunction::Self *)(param);
+ size_t pending_cells = self->result_size;
+ ArrayRef<CT> cells = state.stash.create_array<CT>(pending_cells);
+ while (pending_cells-- > 0) {
+ cells[pending_cells] = (CT) state.peek(0).as_double();
+ state.stack.pop_back();
+ }
+ const Value &result = state.stash.create<DenseTensorView>(self->result_type, TypedCells(cells));
+ state.stack.push_back(result);
+}
+
+struct MyTensorCreateOp {
+ template <typename CT>
+ static auto get_fun() { return my_tensor_create_op<CT>; }
+};
+
+size_t get_index(const TensorSpec::Address &addr, const ValueType &type) {
+ size_t cell_idx = 0;
+ for (const auto &binding: addr) {
+ size_t dim_idx = type.dimension_index(binding.first);
+ assert(dim_idx != ValueType::Dimension::npos);
+ assert(binding.second.is_indexed());
+ cell_idx *= type.dimensions()[dim_idx].size;
+ cell_idx += binding.second.index;
+ }
+ return cell_idx;
+}
+
+} // namespace vespalib::tensor::<unnamed>
+
+DenseTensorCreateFunction::DenseTensorCreateFunction(const ValueType &res_type, std::vector<Child> children)
+ : TensorFunction(),
+ _self(res_type, children.size()),
+ _children(std::move(children))
+{
+}
+
+DenseTensorCreateFunction::~DenseTensorCreateFunction()
+{
+}
+
+void
+DenseTensorCreateFunction::push_children(std::vector<Child::CREF> &target) const
+{
+ for (const Child &c : _children) {
+ target.push_back(c);
+ }
+}
+
+eval::InterpretedFunction::Instruction
+DenseTensorCreateFunction::compile_self(Stash &) const
+{
+ static_assert(sizeof(uint64_t) == sizeof(&_self));
+ auto op = select_1<MyTensorCreateOp>(result_type().cell_type());
+ return eval::InterpretedFunction::Instruction(op, (uint64_t)&_self);
+}
+
+const TensorFunction &
+DenseTensorCreateFunction::optimize(const eval::TensorFunction &expr, Stash &stash)
+{
+ if (auto create = as<Create>(expr)) {
+ if (expr.result_type().is_dense()) {
+ size_t num_cells = expr.result_type().dense_subspace_size();
+ const auto &zero_value = stash.create<DoubleValue>(0.0);
+ const auto &zero_node = const_value(zero_value, stash);
+ std::vector<Child> children(num_cells, zero_node);
+ for (const auto &cell: create->spec()) {
+ size_t cell_idx = get_index(cell.first, expr.result_type());
+ children[cell_idx] = cell.second;
+ }
+ return stash.create<DenseTensorCreateFunction>(expr.result_type(), std::move(children));
+ }
+ }
+ return expr;
+}
+
+} // namespace vespalib::tensor
diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.h b/eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.h
new file mode 100644
index 00000000000..6d262f48aa6
--- /dev/null
+++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_create_function.h
@@ -0,0 +1,33 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#pragma once
+
+#include <vespa/eval/eval/tensor_function.h>
+
+namespace vespalib::tensor {
+
+/**
+ * Tensor function for creating a dense tensor from double values.
+ */
+class DenseTensorCreateFunction : public eval::TensorFunction
+{
+public:
+ struct Self {
+ eval::ValueType result_type;
+ size_t result_size;
+ Self(const eval::ValueType &r, size_t n) : result_type(r), result_size(n) {}
+ };
+private:
+ Self _self;
+ std::vector<Child> _children;
+public:
+ DenseTensorCreateFunction(const eval::ValueType &res_type, std::vector<Child> children);
+ ~DenseTensorCreateFunction();
+ const eval::ValueType &result_type() const override { return _self.result_type; }
+ void push_children(std::vector<Child::CREF> &children) const override;
+ eval::InterpretedFunction::Instruction compile_self(Stash &stash) const override;
+ bool result_is_mutable() const override { return true; }
+ static const eval::TensorFunction &optimize(const eval::TensorFunction &expr, Stash &stash);
+};
+
+} // namespace vespalib::tensor