diff options
author | Geir Storli <geirst@yahoo-inc.com> | 2016-10-24 16:42:13 +0200 |
---|---|---|
committer | Geir Storli <geirst@yahoo-inc.com> | 2016-10-24 16:42:13 +0200 |
commit | 2bfc36412955addddb0e566ed8eefdffcdb2bfe2 (patch) | |
tree | 0ba4c73ad14fdb3d5ed67bf3f0082e08b6301c0a | |
parent | 292817945280d84896d3b137d3c2c2385d7b15b4 (diff) |
Use hw accelerated function to calculate tensor dot product.
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_); |