summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--vespalib/CMakeLists.txt1
-rw-r--r--vespalib/src/tests/tensor/dense_dot_product_function/CMakeLists.txt9
-rw-r--r--vespalib/src/tests/tensor/dense_dot_product_function/FILES1
-rw-r--r--vespalib/src/tests/tensor/dense_dot_product_function/dense_dot_product_function_test.cpp185
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_dot_product_function.cpp47
-rw-r--r--vespalib/src/vespa/vespalib/tensor/dense/dense_dot_product_function.h2
6 files changed, 230 insertions, 15 deletions
diff --git a/vespalib/CMakeLists.txt b/vespalib/CMakeLists.txt
index f286a79489b..7dbc7f91ae2 100644
--- a/vespalib/CMakeLists.txt
+++ b/vespalib/CMakeLists.txt
@@ -74,6 +74,7 @@ vespa_define_module(
src/tests/stringfmt
src/tests/sync
src/tests/tensor/sparse_tensor_builder
+ src/tests/tensor/dense_dot_product_function
src/tests/tensor/dense_tensor_address_combiner
src/tests/tensor/dense_tensor_builder
src/tests/tensor/dense_tensor_function_compiler
diff --git a/vespalib/src/tests/tensor/dense_dot_product_function/CMakeLists.txt b/vespalib/src/tests/tensor/dense_dot_product_function/CMakeLists.txt
new file mode 100644
index 00000000000..d02f2cf7646
--- /dev/null
+++ b/vespalib/src/tests/tensor/dense_dot_product_function/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(vespalib_dense_dot_product_function_test_app TEST
+ SOURCES
+ dense_dot_product_function_test.cpp
+ DEPENDS
+ vespalib
+ vespalib_vespalib_tensor
+)
+vespa_add_test(NAME vespalib_dense_dot_product_function_test_app COMMAND vespalib_dense_dot_product_function_test_app)
diff --git a/vespalib/src/tests/tensor/dense_dot_product_function/FILES b/vespalib/src/tests/tensor/dense_dot_product_function/FILES
new file mode 100644
index 00000000000..c79d4ae29de
--- /dev/null
+++ b/vespalib/src/tests/tensor/dense_dot_product_function/FILES
@@ -0,0 +1 @@
+dense_dot_product_function_test.cpp
diff --git a/vespalib/src/tests/tensor/dense_dot_product_function/dense_dot_product_function_test.cpp b/vespalib/src/tests/tensor/dense_dot_product_function/dense_dot_product_function_test.cpp
new file mode 100644
index 00000000000..e9126433a01
--- /dev/null
+++ b/vespalib/src/tests/tensor/dense_dot_product_function/dense_dot_product_function_test.cpp
@@ -0,0 +1,185 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+#include <vespa/log/log.h>
+LOG_SETUP("dense_dot_product_function_test");
+
+#include <vespa/vespalib/testkit/test_kit.h>
+#include <vespa/vespalib/eval/tensor_function.h>
+#include <vespa/vespalib/tensor/dense/dense_dot_product_function.h>
+#include <vespa/vespalib/tensor/dense/dense_tensor.h>
+#include <vespa/vespalib/tensor/dense/dense_tensor_builder.h>
+#include <vespa/vespalib/tensor/dense/dense_tensor_view.h>
+#include <vespa/vespalib/util/stringfmt.h>
+#include <vespa/vespalib/util/stash.h>
+
+using namespace vespalib;
+using namespace vespalib::eval;
+using namespace vespalib::tensor;
+
+ValueType
+makeType(size_t numCells)
+{
+ return ValueType::from_spec(make_string("tensor(x[%zu]", numCells));
+}
+
+std::unique_ptr<tensor_function::Inject>
+makeInject(size_t numCells, size_t tensorId)
+{
+ return std::make_unique<tensor_function::Inject>(makeType(numCells), tensorId);
+}
+
+tensor::Tensor::UP
+makeTensor(size_t numCells, double cellBias)
+{
+ DenseTensorBuilder builder;
+ DenseTensorBuilder::Dimension dim = builder.defineDimension("x", numCells);
+ for (size_t i = 0; i < numCells; ++i) {
+ builder.addLabel(dim, i).addCell(i + cellBias);
+ }
+ return builder.build();
+}
+
+double
+calcDotProduct(const DenseTensor &lhs, const DenseTensor &rhs)
+{
+ size_t numCells = std::min(lhs.cells().size(), rhs.cells().size());
+ double result = 0;
+ for (size_t i = 0; i < numCells; ++i) {
+ result += (lhs.cells()[i] * rhs.cells()[i]);
+ }
+ return result;
+}
+
+const DenseTensor &
+asDenseTensor(const tensor::Tensor &tensor)
+{
+ return dynamic_cast<const DenseTensor &>(tensor);
+}
+
+class FunctionInput : public TensorFunction::Input
+{
+private:
+ tensor::Tensor::UP _lhsTensor;
+ tensor::Tensor::UP _rhsTensor;
+ const DenseTensor &_lhsDenseTensor;
+ const DenseTensor &_rhsDenseTensor;
+ DenseTensorView _rhsView;
+ TensorValue _lhsValue;
+ TensorValue _rhsValue;
+
+public:
+ FunctionInput(size_t lhsNumCells, size_t rhsNumCells)
+ : _lhsTensor(makeTensor(lhsNumCells, 3.0)),
+ _rhsTensor(makeTensor(rhsNumCells, 5.0)),
+ _lhsDenseTensor(asDenseTensor(*_lhsTensor)),
+ _rhsDenseTensor(asDenseTensor(*_rhsTensor)),
+ _rhsView(_rhsDenseTensor),
+ _lhsValue(std::make_unique<DenseTensor>(_lhsDenseTensor.type(),
+ _lhsDenseTensor.cells())),
+ _rhsValue(std::make_unique<DenseTensorView>(_rhsView.type(),
+ _rhsView.cells()))
+ {}
+ virtual const Value &get_tensor(size_t id) const override {
+ if (id == 0) {
+ return _lhsValue;
+ } else {
+ return _rhsValue;
+ }
+ }
+ virtual const UnaryOperation &get_map_operation(size_t) const override {
+ abort();
+ }
+ double expectedDotProduct() const {
+ return calcDotProduct(_lhsDenseTensor, _rhsDenseTensor);
+ }
+};
+
+struct Fixture
+{
+ DenseDotProductFunction function;
+ FunctionInput input;
+ Fixture(size_t lhsNumCells, size_t rhsNumCells)
+ : function(makeInject(lhsNumCells, 0), makeInject(rhsNumCells, 1)),
+ input(lhsNumCells, rhsNumCells)
+ {
+ }
+ double eval() const {
+ Stash stash;
+ const Value &result = function.eval(input, stash);
+ ASSERT_TRUE(result.is_double());
+ LOG(info, "eval(): (%s) * (%s) = %f",
+ input.get_tensor(0).type().to_spec().c_str(),
+ input.get_tensor(1).type().to_spec().c_str(),
+ result.as_double());
+ return result.as_double();
+ }
+};
+
+void
+assertDotProduct(size_t numCells)
+{
+ Fixture f(numCells, numCells);
+ EXPECT_EQUAL(f.input.expectedDotProduct(), f.eval());
+}
+
+void
+assertDotProduct(size_t lhsNumCells, size_t rhsNumCells)
+{
+ Fixture f(lhsNumCells, rhsNumCells);
+ EXPECT_EQUAL(f.input.expectedDotProduct(), f.eval());
+}
+
+TEST_F("require that empty dot product is correct", Fixture(0, 0))
+{
+ EXPECT_EQUAL(0.0, f.eval());
+}
+
+TEST_F("require that basic dot product with equal sizes is correct", Fixture(2, 2))
+{
+ EXPECT_EQUAL((3.0 * 5.0) + (4.0 * 6.0), f.eval());
+}
+
+TEST_F("require that basic dot product with un-equal sizes is correct", Fixture(2, 3))
+{
+ EXPECT_EQUAL((3.0 * 5.0) + (4.0 * 6.0), f.eval());
+}
+
+TEST_F("require that basic dot product with un-equal sizes is correct", Fixture(3, 2))
+{
+ EXPECT_EQUAL((3.0 * 5.0) + (4.0 * 6.0), f.eval());
+}
+
+TEST("require that dot product with equal sizes is correct")
+{
+ TEST_DO(assertDotProduct(8));
+ TEST_DO(assertDotProduct(16));
+ TEST_DO(assertDotProduct(32));
+ TEST_DO(assertDotProduct(64));
+ TEST_DO(assertDotProduct(128));
+ TEST_DO(assertDotProduct(256));
+ TEST_DO(assertDotProduct(512));
+ TEST_DO(assertDotProduct(1024));
+
+ TEST_DO(assertDotProduct(8 + 3));
+ TEST_DO(assertDotProduct(16 + 3));
+ TEST_DO(assertDotProduct(32 + 3));
+ TEST_DO(assertDotProduct(64 + 3));
+ TEST_DO(assertDotProduct(128 + 3));
+ TEST_DO(assertDotProduct(256 + 3));
+ TEST_DO(assertDotProduct(512 + 3));
+ TEST_DO(assertDotProduct(1024 + 3));
+}
+
+TEST("require that dot product with un-equal sizes is correct")
+{
+ TEST_DO(assertDotProduct(8, 8 + 3));
+ TEST_DO(assertDotProduct(16, 16 + 3));
+ TEST_DO(assertDotProduct(32, 32 + 3));
+ TEST_DO(assertDotProduct(64, 64 + 3));
+ TEST_DO(assertDotProduct(128, 128 + 3));
+ TEST_DO(assertDotProduct(256, 256 + 3));
+ TEST_DO(assertDotProduct(512, 512 + 3));
+ TEST_DO(assertDotProduct(1024, 1024 + 3));
+}
+
+TEST_MAIN() { TEST_RUN_ALL(); }
diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_dot_product_function.cpp b/vespalib/src/vespa/vespalib/tensor/dense/dense_dot_product_function.cpp
index 1be9dd64bec..fe623a430c7 100644
--- a/vespalib/src/vespa/vespalib/tensor/dense/dense_dot_product_function.cpp
+++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_dot_product_function.cpp
@@ -2,35 +2,52 @@
#include <vespa/fastos/fastos.h>
#include "dense_dot_product_function.h"
+#include "dense_tensor.h"
+#include "dense_tensor_view.h"
#include <vespa/vespalib/eval/value.h>
#include <vespa/vespalib/tensor/tensor.h>
namespace vespalib {
namespace tensor {
+using CellsRef = DenseTensorView::CellsRef;
+
DenseDotProductFunction::DenseDotProductFunction(InjectUP lhsTensor_, InjectUP rhsTensor_)
: _lhsTensor(std::move(lhsTensor_)),
- _rhsTensor(std::move(rhsTensor_))
+ _rhsTensor(std::move(rhsTensor_)),
+ _hwAccelerator(hwaccelrated::IAccelrated::getAccelrator())
{
}
-const eval::Value &
-DenseDotProductFunction::eval(const Input &input, Stash &stash) const
+namespace {
+
+CellsRef
+getCellsRef(const eval::Value &value)
{
- const eval::Value &lhsValue = input.get_tensor(_lhsTensor->tensor_id);
- const eval::Value &rhsValue = input.get_tensor(_rhsTensor->tensor_id);
- assert(lhsValue.is_tensor());
- assert(rhsValue.is_tensor());
- const Tensor *lhsTensor = dynamic_cast<const Tensor *>(lhsValue.as_tensor());
- const Tensor *rhsTensor = dynamic_cast<const Tensor *>(rhsValue.as_tensor());
- assert(lhsTensor);
- assert(rhsTensor);
- double result;
- if (_lhsTensor->result_type == _rhsTensor->result_type) {
- result = lhsTensor->match(*rhsTensor)->sum();
+ assert(value.is_tensor());
+ const Tensor *tensor = dynamic_cast<const Tensor *>(value.as_tensor());
+ assert(tensor);
+ const DenseTensorView *denseTensorView = dynamic_cast<const DenseTensorView *>(tensor);
+ if (denseTensorView) {
+ return denseTensorView->cells();
} else {
- result = lhsTensor->multiply(*rhsTensor)->sum();
+ // TODO: Make DenseTensor inherit DenseTensorView
+ const DenseTensor *denseTensor = dynamic_cast<const DenseTensor *>(tensor);
+ assert(denseTensor);
+ return CellsRef(&denseTensor->cells()[0],
+ denseTensor->cells().size());
}
+}
+
+}
+
+const eval::Value &
+DenseDotProductFunction::eval(const Input &input, Stash &stash) const
+{
+ DenseTensorView::CellsRef lhsCells = getCellsRef(input.get_tensor(_lhsTensor->tensor_id));
+ DenseTensorView::CellsRef rhsCells = getCellsRef(input.get_tensor(_rhsTensor->tensor_id));
+ size_t numCells = std::min(lhsCells.size(), rhsCells.size());
+ double result = _hwAccelerator->dotProduct(lhsCells.cbegin(), rhsCells.cbegin(), numCells);
return stash.create<eval::DoubleValue>(result);
}
diff --git a/vespalib/src/vespa/vespalib/tensor/dense/dense_dot_product_function.h b/vespalib/src/vespa/vespalib/tensor/dense/dense_dot_product_function.h
index b2b87c5b1d6..82d8f49bc47 100644
--- a/vespalib/src/vespa/vespalib/tensor/dense/dense_dot_product_function.h
+++ b/vespalib/src/vespa/vespalib/tensor/dense/dense_dot_product_function.h
@@ -3,6 +3,7 @@
#pragma once
#include <vespa/vespalib/eval/tensor_function.h>
+#include <vespa/vespalib/hwaccelrated/iaccelrated.h>
namespace vespalib {
namespace tensor {
@@ -17,6 +18,7 @@ private:
InjectUP _lhsTensor;
InjectUP _rhsTensor;
+ hwaccelrated::IAccelrated::UP _hwAccelerator;
public:
DenseDotProductFunction(InjectUP lhsTensor_, InjectUP rhsTensor_);