From 4171786b6f1786299b26cf2c82782bb505f09c95 Mon Sep 17 00:00:00 2001 From: Alejandro Saucedo Date: Thu, 10 Sep 2020 08:35:27 +0100 Subject: [PATCH] Added a buildTensor command to manager to simplify tensor creation workflows --- README.md | 12 +-- single_include/kompute/Kompute.hpp | 161 ++++++++++++++++------------- src/Tensor.cpp | 3 + src/include/kompute/Manager.hpp | 28 ++++- test/TestManager.cpp | 20 ++++ 5 files changed, 147 insertions(+), 77 deletions(-) diff --git a/README.md b/README.md index 866b81826..8adfd6c93 100644 --- a/README.md +++ b/README.md @@ -60,8 +60,9 @@ int main() { // You can allow Kompute to create the Vulkan components, or pass your existing ones kp::Manager mgr; // Selects device 0 unless explicitly requested - auto tensorA = std::make_shared(kp::Tensor({ 0, 1, 2 })); - auto tensorRhs = std::make_shared(kp::Tensor({ 2, 4, 6 })); + // Creates tensor an initializes GPU memory (below we show more granularity) + auto tensorA = mgr.buildTensor({ 3, 4, 5 }); + auto tensorB = mgr.buildTensor({ 0, 0, 0 }); // Define your shader as a string (using string literals for simplicity) // (You can also pass the raw compiled bytes, or even path to file) @@ -80,12 +81,9 @@ int main() { } )"); - // Create tensor data in GPU - mgr.evalOpDefault({ tensorA, tensorB }); - // Run Kompute operation on the parameters provided with dispatch layout mgr.evalOpDefault>( - { tensorLhs, tensorRhs, tensorOut }, + { tensorA, tensorB }, true, // Whether to retrieve the output from GPU memory std::vector(shader.begin(), shader.end())); @@ -126,7 +124,7 @@ int main() { auto tensorRhs = std::make_shared(kp::Tensor({ 2., 4., 6. })); auto tensorOut = std::make_shared(kp::Tensor({ 0., 0., 0. })); - // Create tensors data in GPU + // Create tensors data explicitly in GPU with an operation mgr.evalOpDefault({ tensorLhs, tensorRhs, tensorOut }); // Run Kompute operation on the parameters provided with dispatch layout diff --git a/single_include/kompute/Kompute.hpp b/single_include/kompute/Kompute.hpp index 9cb8e986d..afc873810 100755 --- a/single_include/kompute/Kompute.hpp +++ b/single_include/kompute/Kompute.hpp @@ -651,6 +651,74 @@ class Sequence } // End namespace kp +namespace kp { + +/** + Operation that creates tensor and manages the memory of the components + created +*/ +class OpTensorCreate : public OpBase +{ + public: + OpTensorCreate(); + + /** + * Default constructor with parameters that provides the bare minimum + * requirements for the operations to be able to create and manage their + * sub-components. + * + * @param physicalDevice Vulkan physical device used to find device queues + * @param device Vulkan logical device for passing to Algorithm + * @param commandBuffer Vulkan Command Buffer to record commands into + * @param tensors Tensors that will be used to create in operation. + * @param freeTensors Whether operation manages the memory of the Tensors + */ + OpTensorCreate(std::shared_ptr physicalDevice, + std::shared_ptr device, + std::shared_ptr commandBuffer, + std::vector> tensors); + + /** + * Default destructor which in this case expects the parent class to free + * the tensors + */ + ~OpTensorCreate() override; + + /** + * In charge of initialising the primary Tensor as well as the staging + * tensor as required. It will only initialise a staging tensor if the + * Primary tensor is of type Device. For staging tensors it performs a + * mapDataIntoHostMemory which would perform immediately as opposed to + * on sequence eval/submission. + */ + void init() override; + + /** + * Record runs the core actions to create the tensors. For device tensors + * it records a copyCommand to move the data from the staging tensor to the + * device tensor. The mapping for staging tensors happens in the init function + * not in the record function. + */ + void record() override; + + /** + * Does not perform any preEval commands. + */ + virtual void preEval() override; + + /** + * Performs a copy back into the main tensor to ensure that the data + * contained is the one that is now being stored in the GPU. + */ + virtual void postEval() override; + + private: + // Never owned resources + std::vector> mStagingTensors; +}; + +} // End namespace kp + #define KP_DEFAULT_SESSION "DEFAULT" namespace kp { @@ -660,7 +728,6 @@ namespace kp { */ class Manager { - private: public: /** Base constructor and default used which creates the base resources @@ -757,6 +824,30 @@ class Manager this->evalOp(tensors, KP_DEFAULT_SESSION, std::forward(params)...); } + /** + * Function that simplifies the common workflow of tensor creation and + * initialization. It will take the constructor parameters for a Tensor + * and will will us it to create a new Tensor and then create it using + * the OpCreateTensor command. + * + * @param data The data to initialize the tensor with + * @param tensorType The type of tensor to initialize + * @returns Initialized Tensor with memory Syncd to GPU device + */ + std::shared_ptr buildTensor( + const std::vector& data, + Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) + { + SPDLOG_DEBUG("Kompute Manager createInitTensor triggered"); + + SPDLOG_DEBUG("Kompute Manager creating new tensor shared ptr"); + std::shared_ptr tensor = std::make_shared(kp::Tensor(data, tensorType)); + + this->evalOpDefault({tensor}); + + return tensor; + } + private: // -------------- OPTIONALLY OWNED RESOURCES std::shared_ptr mInstance = nullptr; @@ -1531,74 +1622,6 @@ class OpMult : public OpAlgoBase namespace kp { -/** - Operation that creates tensor and manages the memory of the components - created -*/ -class OpTensorCreate : public OpBase -{ - public: - OpTensorCreate(); - - /** - * Default constructor with parameters that provides the bare minimum - * requirements for the operations to be able to create and manage their - * sub-components. - * - * @param physicalDevice Vulkan physical device used to find device queues - * @param device Vulkan logical device for passing to Algorithm - * @param commandBuffer Vulkan Command Buffer to record commands into - * @param tensors Tensors that will be used to create in operation. - * @param freeTensors Whether operation manages the memory of the Tensors - */ - OpTensorCreate(std::shared_ptr physicalDevice, - std::shared_ptr device, - std::shared_ptr commandBuffer, - std::vector> tensors); - - /** - * Default destructor which in this case expects the parent class to free - * the tensors - */ - ~OpTensorCreate() override; - - /** - * In charge of initialising the primary Tensor as well as the staging - * tensor as required. It will only initialise a staging tensor if the - * Primary tensor is of type Device. For staging tensors it performs a - * mapDataIntoHostMemory which would perform immediately as opposed to - * on sequence eval/submission. - */ - void init() override; - - /** - * Record runs the core actions to create the tensors. For device tensors - * it records a copyCommand to move the data from the staging tensor to the - * device tensor. The mapping for staging tensors happens in the init function - * not in the record function. - */ - void record() override; - - /** - * Does not perform any preEval commands. - */ - virtual void preEval() override; - - /** - * Performs a copy back into the main tensor to ensure that the data - * contained is the one that is now being stored in the GPU. - */ - virtual void postEval() override; - - private: - // Never owned resources - std::vector> mStagingTensors; -}; - -} // End namespace kp - -namespace kp { - /** Operation that copies the data from the first tensor to the rest of the tensors provided, using a record command for all the vectors. This operation does not own/manage the memory of the tensors passed to it. The operation must only receive tensors of type */ diff --git a/src/Tensor.cpp b/src/Tensor.cpp index 4445b12ca..4ff405028 100644 --- a/src/Tensor.cpp +++ b/src/Tensor.cpp @@ -100,6 +100,9 @@ Tensor::isInit() void Tensor::setData(const std::vector& data) { + if (data.size() != this->mData.size()) { + throw std::runtime_error("Kompute Tensor Cannot set data of different sizes"); + } this->mData = data; } diff --git a/src/include/kompute/Manager.hpp b/src/include/kompute/Manager.hpp index 71f7ef168..66b08670e 100644 --- a/src/include/kompute/Manager.hpp +++ b/src/include/kompute/Manager.hpp @@ -6,6 +6,8 @@ #include "kompute/Sequence.hpp" +#include "kompute/operations/OpTensorCreate.hpp" + #define KP_DEFAULT_SESSION "DEFAULT" namespace kp { @@ -15,7 +17,6 @@ namespace kp { */ class Manager { - private: public: /** Base constructor and default used which creates the base resources @@ -112,6 +113,31 @@ class Manager this->evalOp(tensors, KP_DEFAULT_SESSION, std::forward(params)...); } + + /** + * Function that simplifies the common workflow of tensor creation and + * initialization. It will take the constructor parameters for a Tensor + * and will will us it to create a new Tensor and then create it using + * the OpCreateTensor command. + * + * @param data The data to initialize the tensor with + * @param tensorType The type of tensor to initialize + * @returns Initialized Tensor with memory Syncd to GPU device + */ + std::shared_ptr buildTensor( + const std::vector& data, + Tensor::TensorTypes tensorType = Tensor::TensorTypes::eDevice) + { + SPDLOG_DEBUG("Kompute Manager createInitTensor triggered"); + + SPDLOG_DEBUG("Kompute Manager creating new tensor shared ptr"); + std::shared_ptr tensor = std::make_shared(kp::Tensor(data, tensorType)); + + this->evalOpDefault({tensor}); + + return tensor; + } + private: // -------------- OPTIONALLY OWNED RESOURCES std::shared_ptr mInstance = nullptr; diff --git a/test/TestManager.cpp b/test/TestManager.cpp index 086b55f2e..666ff3978 100755 --- a/test/TestManager.cpp +++ b/test/TestManager.cpp @@ -107,3 +107,23 @@ TEST(TestManager, TestMultipleTensorsAtOnce) { EXPECT_EQ(tensorOutput->data(), std::vector({0, 4, 12})); } + +TEST(TestManager, TestCreateInitTensor) { + kp::Manager mgr; + + std::shared_ptr tensorA = mgr.buildTensor({0,1,2}); + std::shared_ptr tensorB = mgr.buildTensor({0,0,0}); + + mgr.evalOpDefault({tensorA, tensorB}); + + mgr.evalOpDefault({tensorB}); + + EXPECT_EQ(tensorB->data(), std::vector({0,1,2})); + + std::shared_ptr tensorC = mgr.buildTensor({0,0,0}, kp::Tensor::TensorTypes::eStaging); + + mgr.evalOpDefault({tensorA, tensorC}); + + EXPECT_EQ(tensorC->data(), std::vector({0,1,2})); +} +