From 23270bbd14c0b33326e7e20e64c7d69b84d36042 Mon Sep 17 00:00:00 2001 From: blee-bot <93bslee@gmail.com> Date: Fri, 1 Nov 2024 18:17:47 +0900 Subject: [PATCH 1/5] This commit introduce record hessian. This commit introduce record hessian. ONE-DCO-1.0-Signed-off-by: Banseok Lee --- .../include/record-hessian/RecordHessian.h | 53 +++++ compiler/record-hessian/src/RecordHessian.cpp | 191 ++++++++++++++++++ .../record-hessian/src/RecordHessian.test.cpp | 62 ++++++ 3 files changed, 306 insertions(+) create mode 100644 compiler/record-hessian/include/record-hessian/RecordHessian.h create mode 100644 compiler/record-hessian/src/RecordHessian.cpp create mode 100644 compiler/record-hessian/src/RecordHessian.test.cpp diff --git a/compiler/record-hessian/include/record-hessian/RecordHessian.h b/compiler/record-hessian/include/record-hessian/RecordHessian.h new file mode 100644 index 00000000000..8261c0e5042 --- /dev/null +++ b/compiler/record-hessian/include/record-hessian/RecordHessian.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __RECORD_HESSIAN_H__ +#define __RECORD_HESSIAN_H__ + +#include +#include + +#include "record-hessian/HessianObserver.h" + +namespace record_hessian +{ + +class RecordHessian +{ +public: + explicit RecordHessian() {} + + ~RecordHessian() = default; + + void initialize(luci::Module *module); + // TODO Refactor profile functions + std::unique_ptr profileData(const std::string &input_data_path); + +private: + luci_interpreter::Interpreter *getInterpreter() const { return _interpreter.get(); } + + // Never return nullptr + HessianObserver *getObserver() const { return _observer.get(); } + + luci::Module *_module; + + std::unique_ptr _interpreter; + std::unique_ptr _observer; +}; + +} // namespace record_hessian + +#endif // __RECORD_HESSIAN_H__ diff --git a/compiler/record-hessian/src/RecordHessian.cpp b/compiler/record-hessian/src/RecordHessian.cpp new file mode 100644 index 00000000000..4f54170bf34 --- /dev/null +++ b/compiler/record-hessian/src/RecordHessian.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "record-hessian/RecordHessian.h" +#include "record-hessian/HessianObserver.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using Shape = std::vector; +using DataType = loco::DataType; + +namespace +{ + +// Return a string with no whitespace from both ends +std::string trim(std::string s) +{ + // Trim left side + s.erase(s.begin(), + std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); })); + + // Trim right side + s.erase( + std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), + s.end()); + + return s; +} + +uint32_t numElements(const luci::CircleNode *node) +{ + uint32_t num_elements = 1; + for (uint32_t i = 0; i < node->rank(); i++) + num_elements *= node->dim(i).value(); + + return num_elements; +} + +void checkInputDimension(const luci::CircleInput *input) +{ + for (uint32_t i = 0; i < input->rank(); i++) + if (!input->dim(i).known()) + throw std::runtime_error("RecordHessian: " + input->name() + " has unknown dimension"); + + if (numElements(input) == 0) + throw std::runtime_error("RecordHessian: " + input->name() + " is a zero-sized input"); +} + +/** + * @brief getTensorSize will return size in bytes + */ +template size_t getTensorSize(const NodeT *node) +{ + uint32_t tensor_size = luci::size(node->dtype()); + for (uint32_t i = 0; i < node->rank(); ++i) + tensor_size *= node->dim(i).value(); + return tensor_size; +} + +/** + * @brief verifyTypeShape checks the type and the shape of CircleInput + * This throws an exception if type or shape does not match + */ +void verifyTypeShape(const luci::CircleInput *input_node, const DataType &dtype, const Shape &shape) +{ + // Type check + if (dtype != input_node->dtype()) + throw std::runtime_error("RecordHessian: Wrong input type."); + + if (shape.size() != input_node->rank()) + throw std::runtime_error("RecordHessian: Input rank mismatch."); + + for (uint32_t i = 0; i < shape.size(); i++) + { + if (not(shape.at(i) == input_node->dim(i))) + throw std::runtime_error("RecordHessian: Input shape mismatch."); + } +} + +} // namespace + +namespace record_hessian +{ + +void RecordHessian::initialize(luci::Module *module) +{ + // Create and initialize interpreters and observers + + _module = module; + + auto interpreter = std::make_unique(module); + auto observer = std::make_unique(); + + interpreter->attachObserver(observer.get()); + + _observer = std::move(observer); + _interpreter = std::move(interpreter); +} + +std::unique_ptr RecordHessian::profileData(const std::string &input_data_path) +{ + try + { + dio::hdf5::HDF5Importer importer(input_data_path); + importer.importGroup("value"); + + bool is_raw_data = importer.isRawData(); + + const auto num_records = importer.numData(); + if (num_records == 0) + throw std::runtime_error("RecordHessian: The input data file does not contain any record."); + + const auto input_nodes = loco::input_nodes(_module->graph()); + const auto num_inputs = input_nodes.size(); + + for (int32_t record_idx = 0; record_idx < num_records; record_idx++) + { + if (num_inputs != static_cast(importer.numInputs(record_idx))) + throw std::runtime_error("RecordHessian: Wrong number of inputs."); + + std::cout << "Recording " << record_idx << "'th data for hessian." << std::endl; + + for (uint32_t input_idx = 0; input_idx < num_inputs; input_idx++) + { + const auto *input_node = loco::must_cast(input_nodes[input_idx]); + assert(input_node->index() == input_idx); + checkInputDimension(input_node); + std::vector input_data(getTensorSize(input_node)); + + if (!is_raw_data) + { + DataType dtype; + Shape shape; + importer.readTensor(record_idx, input_idx, &dtype, &shape, input_data.data(), + input_data.size()); + + // Check the type and the shape of the input data is valid + verifyTypeShape(input_node, dtype, shape); + } + else + { + // Skip type/shape check for raw data + importer.readTensor(record_idx, input_idx, input_data.data(), input_data.size()); + } + + // TODO: Input data is copied twice (file -> buffer (input_data) -> interpreter inputs) + // We can redcue the copy by directly writing data from file to interpreter inputs + getInterpreter()->writeInputTensor(input_node, input_data.data(), input_data.size()); + } + + getInterpreter()->interpret(); + } + + std::cout << "Recording finished. Number of recorded data: " << num_records << std::endl; + } + catch (const H5::Exception &e) + { + H5::Exception::printErrorStack(); + throw std::runtime_error("RecordHessian: HDF5 error occurred."); + } + + return getObserver()->hessianData(); +} + +} // namespace record_hessian diff --git a/compiler/record-hessian/src/RecordHessian.test.cpp b/compiler/record-hessian/src/RecordHessian.test.cpp new file mode 100644 index 00000000000..66e5fe0a299 --- /dev/null +++ b/compiler/record-hessian/src/RecordHessian.test.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "record-hessian/RecordHessian.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace record_hessian; + +TEST(RecordHessianTest, profileDataInvalidInputPath_NEG) +{ + // Create a module and a graph + auto m = luci::make_module(); + + // Initialize RecordHessian + RecordHessian rh; + rh.initialize(m.get()); + + // Provide an invalid input_data_path + std::string invalid_input_data_path = "invalid_h5_file"; + + // Call profileData and expect an exception + EXPECT_ANY_THROW( + { std::unique_ptr hessian_map = rh.profileData(invalid_input_data_path); }); +} + +TEST(RecordHessianTest, profileDataNonexistingFile_NEG) +{ + // Create a module and a graph + auto m = luci::make_module(); + + // Initialize RecordHessian + RecordHessian rh; + rh.initialize(m.get()); + + // // Provide an invalid input_data_path + std::string non_existing_h5 = "non_existing.h5"; + + // // Call profileData and expect an exception + EXPECT_ANY_THROW({ std::unique_ptr hessian_map = rh.profileData(non_existing_h5); }); +} From 00f30732c5198826794616bce127f88264e23d41 Mon Sep 17 00:00:00 2001 From: blee-bot <93bslee@gmail.com> Date: Mon, 4 Nov 2024 10:06:19 +0900 Subject: [PATCH 2/5] Split codes into core snippets for small PR. Split codes into core snippets for small PR. ONE-DCO-1.0-Signed-off-by: Banseok Lee --- .../include/record-hessian/HessianObserver.h | 45 ++++++++++ compiler/record-hessian/src/RecordHessian.cpp | 85 ------------------- .../record-hessian/src/RecordHessian.test.cpp | 62 -------------- 3 files changed, 45 insertions(+), 147 deletions(-) create mode 100644 compiler/record-hessian/include/record-hessian/HessianObserver.h delete mode 100644 compiler/record-hessian/src/RecordHessian.test.cpp diff --git a/compiler/record-hessian/include/record-hessian/HessianObserver.h b/compiler/record-hessian/include/record-hessian/HessianObserver.h new file mode 100644 index 00000000000..46a39b28b46 --- /dev/null +++ b/compiler/record-hessian/include/record-hessian/HessianObserver.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __RECORD_HESSIAN_HESSIANOBSERVER_H__ +#define __RECORD_HESSIAN_HESSIANOBSERVER_H__ + +#include "record-hessian/HessianComputer.h" + +#include +#include +#include + +namespace record_hessian +{ + +class HessianObserver : public luci_interpreter::ExecutionObserver +{ +public: + HessianObserver() = default; + + void postTensorWrite(const luci::CircleNode *node, + const luci_interpreter::Tensor *tensor) override; + + std::unique_ptr hessianData() { return _hessian_computer.getMap(); } + +private: + HessianComputer _hessian_computer; +}; + +} // namespace record_hessian + +#endif // __RECORD_HESSIAN_HESSIANOBSERVER_H__ diff --git a/compiler/record-hessian/src/RecordHessian.cpp b/compiler/record-hessian/src/RecordHessian.cpp index 4f54170bf34..95e8545403b 100644 --- a/compiler/record-hessian/src/RecordHessian.cpp +++ b/compiler/record-hessian/src/RecordHessian.cpp @@ -104,88 +104,3 @@ void verifyTypeShape(const luci::CircleInput *input_node, const DataType &dtype, } } // namespace - -namespace record_hessian -{ - -void RecordHessian::initialize(luci::Module *module) -{ - // Create and initialize interpreters and observers - - _module = module; - - auto interpreter = std::make_unique(module); - auto observer = std::make_unique(); - - interpreter->attachObserver(observer.get()); - - _observer = std::move(observer); - _interpreter = std::move(interpreter); -} - -std::unique_ptr RecordHessian::profileData(const std::string &input_data_path) -{ - try - { - dio::hdf5::HDF5Importer importer(input_data_path); - importer.importGroup("value"); - - bool is_raw_data = importer.isRawData(); - - const auto num_records = importer.numData(); - if (num_records == 0) - throw std::runtime_error("RecordHessian: The input data file does not contain any record."); - - const auto input_nodes = loco::input_nodes(_module->graph()); - const auto num_inputs = input_nodes.size(); - - for (int32_t record_idx = 0; record_idx < num_records; record_idx++) - { - if (num_inputs != static_cast(importer.numInputs(record_idx))) - throw std::runtime_error("RecordHessian: Wrong number of inputs."); - - std::cout << "Recording " << record_idx << "'th data for hessian." << std::endl; - - for (uint32_t input_idx = 0; input_idx < num_inputs; input_idx++) - { - const auto *input_node = loco::must_cast(input_nodes[input_idx]); - assert(input_node->index() == input_idx); - checkInputDimension(input_node); - std::vector input_data(getTensorSize(input_node)); - - if (!is_raw_data) - { - DataType dtype; - Shape shape; - importer.readTensor(record_idx, input_idx, &dtype, &shape, input_data.data(), - input_data.size()); - - // Check the type and the shape of the input data is valid - verifyTypeShape(input_node, dtype, shape); - } - else - { - // Skip type/shape check for raw data - importer.readTensor(record_idx, input_idx, input_data.data(), input_data.size()); - } - - // TODO: Input data is copied twice (file -> buffer (input_data) -> interpreter inputs) - // We can redcue the copy by directly writing data from file to interpreter inputs - getInterpreter()->writeInputTensor(input_node, input_data.data(), input_data.size()); - } - - getInterpreter()->interpret(); - } - - std::cout << "Recording finished. Number of recorded data: " << num_records << std::endl; - } - catch (const H5::Exception &e) - { - H5::Exception::printErrorStack(); - throw std::runtime_error("RecordHessian: HDF5 error occurred."); - } - - return getObserver()->hessianData(); -} - -} // namespace record_hessian diff --git a/compiler/record-hessian/src/RecordHessian.test.cpp b/compiler/record-hessian/src/RecordHessian.test.cpp deleted file mode 100644 index 66e5fe0a299..00000000000 --- a/compiler/record-hessian/src/RecordHessian.test.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "record-hessian/RecordHessian.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace record_hessian; - -TEST(RecordHessianTest, profileDataInvalidInputPath_NEG) -{ - // Create a module and a graph - auto m = luci::make_module(); - - // Initialize RecordHessian - RecordHessian rh; - rh.initialize(m.get()); - - // Provide an invalid input_data_path - std::string invalid_input_data_path = "invalid_h5_file"; - - // Call profileData and expect an exception - EXPECT_ANY_THROW( - { std::unique_ptr hessian_map = rh.profileData(invalid_input_data_path); }); -} - -TEST(RecordHessianTest, profileDataNonexistingFile_NEG) -{ - // Create a module and a graph - auto m = luci::make_module(); - - // Initialize RecordHessian - RecordHessian rh; - rh.initialize(m.get()); - - // // Provide an invalid input_data_path - std::string non_existing_h5 = "non_existing.h5"; - - // // Call profileData and expect an exception - EXPECT_ANY_THROW({ std::unique_ptr hessian_map = rh.profileData(non_existing_h5); }); -} From 85bcba8e564dc6f5bd8b12aaa128f1acfdd1cc8f Mon Sep 17 00:00:00 2001 From: BLEE <61487178+BLee-bot@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:15:13 +0900 Subject: [PATCH 3/5] [record-hessian] Introduce HessianObserver. (#14292) This commit introduce hessian observer. ONE-DCO-1.0-Signed-off-by: Banseok Lee --- .../include/record-hessian/HessianObserver.h | 46 +++++++++++++++++++ .../record-hessian/src/HessianObserver.cpp | 45 ++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 compiler/record-hessian/include/record-hessian/HessianObserver.h create mode 100644 compiler/record-hessian/src/HessianObserver.cpp diff --git a/compiler/record-hessian/include/record-hessian/HessianObserver.h b/compiler/record-hessian/include/record-hessian/HessianObserver.h new file mode 100644 index 00000000000..283d9e2a377 --- /dev/null +++ b/compiler/record-hessian/include/record-hessian/HessianObserver.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __RECORD_HESSIAN_HESSIANOBSERVER_H__ +#define __RECORD_HESSIAN_HESSIANOBSERVER_H__ + +#include "record-hessian/HessianComputer.h" + +#include +#include + +#include + +namespace record_hessian +{ + +class HessianObserver : public luci_interpreter::ExecutionObserver +{ +public: + HessianObserver() = default; + + void postTensorWrite(const luci::CircleNode *node, + const luci_interpreter::Tensor *tensor) override; + + std::unique_ptr hessianData() { return _hessian_computer.getMap(); } + +private: + HessianComputer _hessian_computer; +}; + +} // namespace record_hessian + +#endif // __RECORD_HESSIAN_HESSIANOBSERVER_H__ diff --git a/compiler/record-hessian/src/HessianObserver.cpp b/compiler/record-hessian/src/HessianObserver.cpp new file mode 100644 index 00000000000..aef981b0d51 --- /dev/null +++ b/compiler/record-hessian/src/HessianObserver.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "record-hessian/HessianObserver.h" + +namespace record_hessian +{ + +void HessianObserver::postTensorWrite(const luci::CircleNode *node, + const luci_interpreter::Tensor *tensor) +{ + assert(node != nullptr); + assert(tensor != nullptr); + + auto node_outputs = loco::succs(node); + for (auto node_output : node_outputs) + { + auto cur_node = dynamic_cast(node_output); + if (cur_node == nullptr) + { + throw std::runtime_error("Record Hessian: node output shouldn't be null."); + } + // TODO : ADD TCONV/DepthCONV cases + if (cur_node->opcode() == luci::CircleOpcode::FULLY_CONNECTED || + cur_node->opcode() == luci::CircleOpcode::CONV_2D) + { + _hessian_computer.recordHessian(cur_node, tensor); + } + } +} + +} // namespace record_hessian From 535e69778edfcc3a3ebb57a5f1deba88d1703c6e Mon Sep 17 00:00:00 2001 From: blee-bot <93bslee@gmail.com> Date: Mon, 4 Nov 2024 19:12:58 +0900 Subject: [PATCH 4/5] This commit introduce record hessian. This commit introduce record hessian. ONE-DCO-1.0-Signed-off-by: Banseok Lee --- .../include/record-hessian/RecordHessian.h | 53 +++++ compiler/record-hessian/src/RecordHessian.cpp | 191 ++++++++++++++++++ .../record-hessian/src/RecordHessian.test.cpp | 62 ++++++ 3 files changed, 306 insertions(+) create mode 100644 compiler/record-hessian/include/record-hessian/RecordHessian.h create mode 100644 compiler/record-hessian/src/RecordHessian.cpp create mode 100644 compiler/record-hessian/src/RecordHessian.test.cpp diff --git a/compiler/record-hessian/include/record-hessian/RecordHessian.h b/compiler/record-hessian/include/record-hessian/RecordHessian.h new file mode 100644 index 00000000000..8261c0e5042 --- /dev/null +++ b/compiler/record-hessian/include/record-hessian/RecordHessian.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __RECORD_HESSIAN_H__ +#define __RECORD_HESSIAN_H__ + +#include +#include + +#include "record-hessian/HessianObserver.h" + +namespace record_hessian +{ + +class RecordHessian +{ +public: + explicit RecordHessian() {} + + ~RecordHessian() = default; + + void initialize(luci::Module *module); + // TODO Refactor profile functions + std::unique_ptr profileData(const std::string &input_data_path); + +private: + luci_interpreter::Interpreter *getInterpreter() const { return _interpreter.get(); } + + // Never return nullptr + HessianObserver *getObserver() const { return _observer.get(); } + + luci::Module *_module; + + std::unique_ptr _interpreter; + std::unique_ptr _observer; +}; + +} // namespace record_hessian + +#endif // __RECORD_HESSIAN_H__ diff --git a/compiler/record-hessian/src/RecordHessian.cpp b/compiler/record-hessian/src/RecordHessian.cpp new file mode 100644 index 00000000000..4f54170bf34 --- /dev/null +++ b/compiler/record-hessian/src/RecordHessian.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "record-hessian/RecordHessian.h" +#include "record-hessian/HessianObserver.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using Shape = std::vector; +using DataType = loco::DataType; + +namespace +{ + +// Return a string with no whitespace from both ends +std::string trim(std::string s) +{ + // Trim left side + s.erase(s.begin(), + std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); })); + + // Trim right side + s.erase( + std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), + s.end()); + + return s; +} + +uint32_t numElements(const luci::CircleNode *node) +{ + uint32_t num_elements = 1; + for (uint32_t i = 0; i < node->rank(); i++) + num_elements *= node->dim(i).value(); + + return num_elements; +} + +void checkInputDimension(const luci::CircleInput *input) +{ + for (uint32_t i = 0; i < input->rank(); i++) + if (!input->dim(i).known()) + throw std::runtime_error("RecordHessian: " + input->name() + " has unknown dimension"); + + if (numElements(input) == 0) + throw std::runtime_error("RecordHessian: " + input->name() + " is a zero-sized input"); +} + +/** + * @brief getTensorSize will return size in bytes + */ +template size_t getTensorSize(const NodeT *node) +{ + uint32_t tensor_size = luci::size(node->dtype()); + for (uint32_t i = 0; i < node->rank(); ++i) + tensor_size *= node->dim(i).value(); + return tensor_size; +} + +/** + * @brief verifyTypeShape checks the type and the shape of CircleInput + * This throws an exception if type or shape does not match + */ +void verifyTypeShape(const luci::CircleInput *input_node, const DataType &dtype, const Shape &shape) +{ + // Type check + if (dtype != input_node->dtype()) + throw std::runtime_error("RecordHessian: Wrong input type."); + + if (shape.size() != input_node->rank()) + throw std::runtime_error("RecordHessian: Input rank mismatch."); + + for (uint32_t i = 0; i < shape.size(); i++) + { + if (not(shape.at(i) == input_node->dim(i))) + throw std::runtime_error("RecordHessian: Input shape mismatch."); + } +} + +} // namespace + +namespace record_hessian +{ + +void RecordHessian::initialize(luci::Module *module) +{ + // Create and initialize interpreters and observers + + _module = module; + + auto interpreter = std::make_unique(module); + auto observer = std::make_unique(); + + interpreter->attachObserver(observer.get()); + + _observer = std::move(observer); + _interpreter = std::move(interpreter); +} + +std::unique_ptr RecordHessian::profileData(const std::string &input_data_path) +{ + try + { + dio::hdf5::HDF5Importer importer(input_data_path); + importer.importGroup("value"); + + bool is_raw_data = importer.isRawData(); + + const auto num_records = importer.numData(); + if (num_records == 0) + throw std::runtime_error("RecordHessian: The input data file does not contain any record."); + + const auto input_nodes = loco::input_nodes(_module->graph()); + const auto num_inputs = input_nodes.size(); + + for (int32_t record_idx = 0; record_idx < num_records; record_idx++) + { + if (num_inputs != static_cast(importer.numInputs(record_idx))) + throw std::runtime_error("RecordHessian: Wrong number of inputs."); + + std::cout << "Recording " << record_idx << "'th data for hessian." << std::endl; + + for (uint32_t input_idx = 0; input_idx < num_inputs; input_idx++) + { + const auto *input_node = loco::must_cast(input_nodes[input_idx]); + assert(input_node->index() == input_idx); + checkInputDimension(input_node); + std::vector input_data(getTensorSize(input_node)); + + if (!is_raw_data) + { + DataType dtype; + Shape shape; + importer.readTensor(record_idx, input_idx, &dtype, &shape, input_data.data(), + input_data.size()); + + // Check the type and the shape of the input data is valid + verifyTypeShape(input_node, dtype, shape); + } + else + { + // Skip type/shape check for raw data + importer.readTensor(record_idx, input_idx, input_data.data(), input_data.size()); + } + + // TODO: Input data is copied twice (file -> buffer (input_data) -> interpreter inputs) + // We can redcue the copy by directly writing data from file to interpreter inputs + getInterpreter()->writeInputTensor(input_node, input_data.data(), input_data.size()); + } + + getInterpreter()->interpret(); + } + + std::cout << "Recording finished. Number of recorded data: " << num_records << std::endl; + } + catch (const H5::Exception &e) + { + H5::Exception::printErrorStack(); + throw std::runtime_error("RecordHessian: HDF5 error occurred."); + } + + return getObserver()->hessianData(); +} + +} // namespace record_hessian diff --git a/compiler/record-hessian/src/RecordHessian.test.cpp b/compiler/record-hessian/src/RecordHessian.test.cpp new file mode 100644 index 00000000000..66e5fe0a299 --- /dev/null +++ b/compiler/record-hessian/src/RecordHessian.test.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "record-hessian/RecordHessian.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace record_hessian; + +TEST(RecordHessianTest, profileDataInvalidInputPath_NEG) +{ + // Create a module and a graph + auto m = luci::make_module(); + + // Initialize RecordHessian + RecordHessian rh; + rh.initialize(m.get()); + + // Provide an invalid input_data_path + std::string invalid_input_data_path = "invalid_h5_file"; + + // Call profileData and expect an exception + EXPECT_ANY_THROW( + { std::unique_ptr hessian_map = rh.profileData(invalid_input_data_path); }); +} + +TEST(RecordHessianTest, profileDataNonexistingFile_NEG) +{ + // Create a module and a graph + auto m = luci::make_module(); + + // Initialize RecordHessian + RecordHessian rh; + rh.initialize(m.get()); + + // // Provide an invalid input_data_path + std::string non_existing_h5 = "non_existing.h5"; + + // // Call profileData and expect an exception + EXPECT_ANY_THROW({ std::unique_ptr hessian_map = rh.profileData(non_existing_h5); }); +} From 4eacb30fda3077735f26c4650e6ed48260b88922 Mon Sep 17 00:00:00 2001 From: blee-bot <93bslee@gmail.com> Date: Mon, 4 Nov 2024 10:06:19 +0900 Subject: [PATCH 5/5] Split codes into core snippets for small PR. Split codes into core snippets for small PR. ONE-DCO-1.0-Signed-off-by: Banseok Lee --- compiler/record-hessian/src/RecordHessian.cpp | 85 ------------------- .../record-hessian/src/RecordHessian.test.cpp | 62 -------------- 2 files changed, 147 deletions(-) delete mode 100644 compiler/record-hessian/src/RecordHessian.test.cpp diff --git a/compiler/record-hessian/src/RecordHessian.cpp b/compiler/record-hessian/src/RecordHessian.cpp index 4f54170bf34..95e8545403b 100644 --- a/compiler/record-hessian/src/RecordHessian.cpp +++ b/compiler/record-hessian/src/RecordHessian.cpp @@ -104,88 +104,3 @@ void verifyTypeShape(const luci::CircleInput *input_node, const DataType &dtype, } } // namespace - -namespace record_hessian -{ - -void RecordHessian::initialize(luci::Module *module) -{ - // Create and initialize interpreters and observers - - _module = module; - - auto interpreter = std::make_unique(module); - auto observer = std::make_unique(); - - interpreter->attachObserver(observer.get()); - - _observer = std::move(observer); - _interpreter = std::move(interpreter); -} - -std::unique_ptr RecordHessian::profileData(const std::string &input_data_path) -{ - try - { - dio::hdf5::HDF5Importer importer(input_data_path); - importer.importGroup("value"); - - bool is_raw_data = importer.isRawData(); - - const auto num_records = importer.numData(); - if (num_records == 0) - throw std::runtime_error("RecordHessian: The input data file does not contain any record."); - - const auto input_nodes = loco::input_nodes(_module->graph()); - const auto num_inputs = input_nodes.size(); - - for (int32_t record_idx = 0; record_idx < num_records; record_idx++) - { - if (num_inputs != static_cast(importer.numInputs(record_idx))) - throw std::runtime_error("RecordHessian: Wrong number of inputs."); - - std::cout << "Recording " << record_idx << "'th data for hessian." << std::endl; - - for (uint32_t input_idx = 0; input_idx < num_inputs; input_idx++) - { - const auto *input_node = loco::must_cast(input_nodes[input_idx]); - assert(input_node->index() == input_idx); - checkInputDimension(input_node); - std::vector input_data(getTensorSize(input_node)); - - if (!is_raw_data) - { - DataType dtype; - Shape shape; - importer.readTensor(record_idx, input_idx, &dtype, &shape, input_data.data(), - input_data.size()); - - // Check the type and the shape of the input data is valid - verifyTypeShape(input_node, dtype, shape); - } - else - { - // Skip type/shape check for raw data - importer.readTensor(record_idx, input_idx, input_data.data(), input_data.size()); - } - - // TODO: Input data is copied twice (file -> buffer (input_data) -> interpreter inputs) - // We can redcue the copy by directly writing data from file to interpreter inputs - getInterpreter()->writeInputTensor(input_node, input_data.data(), input_data.size()); - } - - getInterpreter()->interpret(); - } - - std::cout << "Recording finished. Number of recorded data: " << num_records << std::endl; - } - catch (const H5::Exception &e) - { - H5::Exception::printErrorStack(); - throw std::runtime_error("RecordHessian: HDF5 error occurred."); - } - - return getObserver()->hessianData(); -} - -} // namespace record_hessian diff --git a/compiler/record-hessian/src/RecordHessian.test.cpp b/compiler/record-hessian/src/RecordHessian.test.cpp deleted file mode 100644 index 66e5fe0a299..00000000000 --- a/compiler/record-hessian/src/RecordHessian.test.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2024 Samsung Electronics Co., Ltd. All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "record-hessian/RecordHessian.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace record_hessian; - -TEST(RecordHessianTest, profileDataInvalidInputPath_NEG) -{ - // Create a module and a graph - auto m = luci::make_module(); - - // Initialize RecordHessian - RecordHessian rh; - rh.initialize(m.get()); - - // Provide an invalid input_data_path - std::string invalid_input_data_path = "invalid_h5_file"; - - // Call profileData and expect an exception - EXPECT_ANY_THROW( - { std::unique_ptr hessian_map = rh.profileData(invalid_input_data_path); }); -} - -TEST(RecordHessianTest, profileDataNonexistingFile_NEG) -{ - // Create a module and a graph - auto m = luci::make_module(); - - // Initialize RecordHessian - RecordHessian rh; - rh.initialize(m.get()); - - // // Provide an invalid input_data_path - std::string non_existing_h5 = "non_existing.h5"; - - // // Call profileData and expect an exception - EXPECT_ANY_THROW({ std::unique_ptr hessian_map = rh.profileData(non_existing_h5); }); -}