From: Андрей Шедько/AI Tools Lab /SRR/Engineer/삼성전자 Date: Tue, 21 May 2019 09:36:13 +0000 (+0300) Subject: Introduce Execution Time structure (#5162) X-Git-Tag: submit/tizen/20190809.050447~781 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9d25737da96cc381a7a0668779ee194fcfe37ca8;p=platform%2Fcore%2Fml%2Fnnfw.git Introduce Execution Time structure (#5162) This adds a structure to store/update/persist runtime information for different ops Signed-off-by: Andrei Shedko --- diff --git a/runtimes/neurun/core/include/backend/ExecTime.h b/runtimes/neurun/core/include/backend/ExecTime.h new file mode 100644 index 0000000..90c641b --- /dev/null +++ b/runtimes/neurun/core/include/backend/ExecTime.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2019 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 __NEURUN_BACKEND_EXEC_TIME_H__ +#define __NEURUN_BACKEND_EXEC_TIME_H__ + +#include "backend/Backend.h" +#include "backend/IConfig.h" +#include "JSONExecTime.h" +#include +#include +#include +#include +#include + +namespace neurun +{ +namespace backend +{ +class ExecTime +{ +public: + explicit ExecTime(const std::vector &backends) : _json(backends, _measurements) {} + +public: + /** + * @brief Get exec time of an operation with input size + * or linearly interpolated value based on size if there is no record for given size + * + * @param[in] backend id of a backend + * @param[in] operation name of an operation + * @param[in] quant if input type quantized + * @param[in] op_size sum of operation's flattened sizes of inputs and outputs + * @return execution time for given input sizes + * -1 if there are no records for given parameters (backend, op, quantization). + */ + int64_t getOperationExecTime(Backend *backend, const std::string &operation, bool quant, + uint32_t op_size) const; + /** + * @brief Update exec time of the operation on a backend with given input size or + * add new entity if there is no one. + * + * @param[in] backend id of a backend + * @param[in] operation name of an operation + * @param[in] quant if input type quantized + * @param[in] op_size sum of operation's flattened sizes of inputs and outputs + * @param[in] time real measured value + */ + void updateOperationExecTime(Backend *backend, const std::string &operation, bool quant, + uint32_t op_size, int64_t time); + /** + * @brief Get the permute time from one backend to another + * + * @param[in] from_backend + * @param[in] to_backend + * @param[in] quant if input type quantized + * @param[in] op_size sum of operation's flattened sizes of inputs and outputs + * @return permutation time for operation size + */ + int64_t getPermuteTime(Backend *from_backend, Backend *to_backend, bool quant, + uint32_t op_size) const; + /** + * @brief Update permute time from one backend to another + * + * @param[in] from_backend + * @param[in] to_backend + * @param[in] quant if input type quantized + * @param[in] time measured permutation time + * @param[in] op_size sum of operation's flattened sizes of inputs and outputs + */ + void updatePermuteTime(Backend *from_backend, Backend *to_backend, bool quant, uint32_t op_size, + int64_t time); + /** + * @brief Get the max value of int32_t in int64_t + * @return max value + */ + static int64_t getMax() { return _MAX; } + +private: + /// @brief Measurement data, which is shared with serializer + MeasurementData _measurements; + // int64_t::max may cause integer overflow + static const int64_t _MAX = std::numeric_limits::max(); + /// @brief Serializer + JSON _json; +}; + +} // namespace backend +} // namespace neurun + +#endif // __NEURUN_BACKEND_EXEC_TIME_H__ diff --git a/runtimes/neurun/core/src/backend/ExecTime.cc b/runtimes/neurun/core/src/backend/ExecTime.cc new file mode 100644 index 0000000..fdb5edf --- /dev/null +++ b/runtimes/neurun/core/src/backend/ExecTime.cc @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2019 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 "backend/ExecTime.h" + +#include +#include +#include + +namespace neurun +{ +namespace backend +{ + +int64_t ExecTime::getOperationExecTime(Backend *backend, const std::string &operation, bool quant, + uint32_t op_size) const +{ + auto found_backend = _measurements.find(backend); + if (found_backend == _measurements.end()) + return -1; // no execution time for this backend + + auto found_operation_with_type = found_backend->second.find(operation); + if (found_operation_with_type == found_backend->second.end()) + // no execution time for this operation + return -1; + + auto found_operation = found_operation_with_type->second.find(quant); + if (found_operation == found_operation_with_type->second.end()) + // no execution time for this operation + return -1; + + auto found_size = found_operation->second.find(op_size); + if (found_size != found_operation->second.end()) + return found_size->second; // found execution time + + // Try to interpolate + if (found_operation->second.size() < 2) + // not possible to do linear interpolation + return found_operation->second.begin()->second; + + // if we reach here, then this means, that there is no record, that is equal to op_size + auto upper_bound = found_operation->second.upper_bound(op_size); // > op_size + auto lower_bound = upper_bound; + + if (upper_bound == found_operation->second.end()) // all values <= op_size + { + upper_bound--; + lower_bound = upper_bound; + lower_bound--; + } + else if (upper_bound == found_operation->second.begin()) // all values > op_size + { + upper_bound++; + } + else // op_size between + { + lower_bound--; + } + + // Linear interpolation + const auto x0 = static_cast(lower_bound->first); // size + const auto x1 = static_cast(upper_bound->first); // size + const int64_t y0 = lower_bound->second; // time + const int64_t y1 = upper_bound->second; // time + const auto x = static_cast(op_size); + + int64_t interpolated_value = y0 + (x - x0) * (y1 - y0) / (x1 - x0); + + // execution time must be non-negative + return std::max(interpolated_value, 1); +} + +void ExecTime::updateOperationExecTime(Backend *backend, const std::string &operation, bool quant, + uint32_t op_size, int64_t time) +{ + _measurements[backend][operation][quant][op_size] = time; +} + +void ExecTime::updatePermuteTime(Backend *from_backend, Backend *to_backend, bool quant, + uint32_t op_size, int64_t time) +{ + updateOperationExecTime(from_backend, to_backend->config()->id(), quant, op_size, time); +} + +int64_t ExecTime::getPermuteTime(Backend *from_backend, Backend *to_backend, bool quant, + uint32_t op_size) const +{ + return getOperationExecTime(from_backend, to_backend->config()->id(), quant, op_size); +} + +} // namespace backend +} // namespace neurun diff --git a/runtimes/neurun/test/core/backend/ExecTime.test.cc b/runtimes/neurun/test/core/backend/ExecTime.test.cc new file mode 100644 index 0000000..5b67c42 --- /dev/null +++ b/runtimes/neurun/test/core/backend/ExecTime.test.cc @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2019 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 "backend/ExecTime.h" +#include "backend/IConfig.h" +#include "backend/IStageGenerator.h" +#include "backend/Backend.h" +#include +#include +#include + +namespace +{ +using namespace neurun; +using namespace backend; + +struct MockConfig : public IConfig +{ + std::string id() override { return "b1"; } + void initialize() override{}; + bool SupportSubTensorAlloc() override { return false; } +}; + +struct MockStageGenerator : IStageGenerator +{ + std::shared_ptr tensor_builder() final { return nullptr; } +}; + +struct MockBackend : public ::neurun::backend::Backend +{ + /** @brief Stub backend for testing. + * Required because we use pointers to backends instead of string identifiers. + */ + MockBackend() + : Backend{std::make_shared(), std::make_shared()} {}; +}; + +TEST(ExecTime, roundtrip_ok) +{ + auto b = new MockBackend(); + std::vector bs = {b}; + { + ExecTime et(bs); + et.updateOperationExecTime(b, "op1", true, 100, 100); + et.updateOperationExecTime(b, "op1", true, 200, 200); + et.updateOperationExecTime(b, "op1", false, 100, 888); + } + { + ExecTime et(bs); + auto time = et.getOperationExecTime(b, "op1", true, 100); + ASSERT_EQ(time, 100); + // Check interpolation + time = et.getOperationExecTime(b, "op1", true, 150); + ASSERT_EQ(time, 150); + time = et.getOperationExecTime(b, "op1", false, 100); + ASSERT_EQ(time, 888); + } + // clean up + remove("execution_time.json"); +} + +TEST(ExecTime, structure) +{ + + auto b = new MockBackend(); + std::vector bs = {b}; + { + ExecTime et(bs); + et.updateOperationExecTime(b, "op1", true, 100, 100); + et.updateOperationExecTime(b, "op1", true, 200, 200); + } + { + ExecTime et(bs); + auto time = et.getOperationExecTime(b, "op1", true, 100); + ASSERT_EQ(time, 100); + // Check interpolation + time = et.getOperationExecTime(b, "op1", true, 200); + ASSERT_EQ(time, 200); + } + // clean up + remove("execution_time.json"); +} +} // unnamed namespace