From: Inki Dae Date: Wed, 13 Sep 2023 07:21:58 +0000 (+0900) Subject: mv_machine_learning: embed AsyncManager into each task group X-Git-Tag: accepted/tizen/unified/20231025.093310^2~14 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=fb9e3aef4c273814b5808f142eccc65f070e5ecd;p=platform%2Fcore%2Fapi%2Fmediavision.git mv_machine_learning: embed AsyncManager into each task group [Issue type] : code cleanup Embed AsyncManager into eash task group by moving all implementations from cpp file to header one because keeping the implementations in cpp file propergates template type declarations every time each task group uses the AsyncManager for async API support. Wit this patch, mv_ml_common.so library file isn't created anymore so drop the relevant code. Change-Id: I4fbabcfbe5cb29053858549fd77400ae9ae3ec2d Signed-off-by: Inki Dae --- diff --git a/mv_machine_learning/common/CMakeLists.txt b/mv_machine_learning/common/CMakeLists.txt index a305c5a..01305a0 100644 --- a/mv_machine_learning/common/CMakeLists.txt +++ b/mv_machine_learning/common/CMakeLists.txt @@ -1,14 +1,6 @@ project("mv_ml_common") cmake_minimum_required(VERSION 2.6...3.13) -pkg_check_modules(${PROJECT_NAME}_DEP REQUIRED inference-engine-interface-common) -file(GLOB MV_ML_COMMON_SOURCE_LIST "${PROJECT_SOURCE_DIR}/src/*.cpp") - -add_library(${PROJECT_NAME} SHARED ${MV_ML_COMMON_SOURCE_LIST}) - -target_link_libraries(${PROJECT_NAME} ${MV_COMMON_LIB_NAME} ${OpenCV_LIBS} ${${PROJECT_NAME}_DEP_LIBRARIES}) -target_include_directories(${PROJECT_NAME} PRIVATE include ../../common/include ../object_detection/include) -install(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR}) install( DIRECTORY ${PROJECT_SOURCE_DIR}/include/ DESTINATION include/media FILES_MATCHING diff --git a/mv_machine_learning/common/include/async_manager.h b/mv_machine_learning/common/include/async_manager.h index 605190a..c1e8965 100644 --- a/mv_machine_learning/common/include/async_manager.h +++ b/mv_machine_learning/common/include/async_manager.h @@ -23,9 +23,12 @@ #include #include #include +#include +#include #include #include +#include "machine_learning_exception.h" namespace mediavision { @@ -52,25 +55,179 @@ private: unsigned long _input_frame_number {}; CallbackType _callback; - template void pushToInput(AsyncInputQueue &inputQueue); - template bool isInputQueueEmpty(); - void waitforInputQueue(); - R popFromOutput(); - bool isOutputQueueEmpty(); - void waitforOutputQueue(); - template void inferenceThreadLoop(); - template void invoke(); + template void pushToInput(AsyncInputQueue &inputQueue) + { + std::lock_guard lock(_incoming_queue_mutex); + AsyncInputQueue dstQueue; + + dstQueue.frame_number = inputQueue.frame_number; + + for (auto &elms : inputQueue.inputs) { + std::vector dst_vector; + + for (auto &elm : elms) { + unsigned char *bytes = reinterpret_cast(&elm); + + std::copy_n(bytes, sizeof(T), back_inserter(dst_vector)); + } + + dstQueue.inputs.push_back(dst_vector); + } + + _incoming_queue.push(dstQueue); + _incoming_queue_event.notify_one(); + } + template bool isInputQueueEmpty() + { + std::lock_guard lock(_incoming_queue_mutex); + + return _incoming_queue.empty(); + } + R popFromOutput() + { + std::lock_guard lock(_outgoing_queue_mutex); + R output = _outgoing_queue.front(); + + _outgoing_queue.pop(); + + return output; + } + template void inferenceThreadLoop() + { + // If user called destroy API then this thread loop will be terminated. + while (!_exit_thread) { + // Wait for input data. + waitforInputQueue(); + + // Exit this thread loop if user called destroy API while in thread loop. + if (_exit_thread) + break; + + // What the callback function has to do is to, + // 1. Get input data from input queue. + // 2. Run inference. + // 3. Put output data to output queue. + _callback(); + } + + // waitforOutputQueue function could wait for the notify event after while loop is exited. + // So make sure to call notify_one() here. + _outgoing_queue_event.notify_one(); + } + template void invoke() + { + if (!_thread_handle) + _thread_handle = std::make_unique(&AsyncManager::inferenceThreadLoop, this); + } + void waitforInputQueue() + { + std::unique_lock lock(_incoming_queue_mutex); + + if (!_incoming_queue_event.wait_for(lock, std::chrono::seconds(1), + [this] { return !_incoming_queue.empty(); })) { + LOGD("Waiting for input queue has been timed out."); + } + } + bool isOutputQueueEmpty() + { + std::lock_guard lock(_outgoing_queue_mutex); + + return _outgoing_queue.empty(); + } + void waitforOutputQueue() + { + std::unique_lock lock(_outgoing_queue_mutex); + + if (!_outgoing_queue_event.wait_for(lock, std::chrono::seconds(10), [this] { + if (_exit_thread) + throw exception::InvalidOperation("thread is exited already."); + + return !_outgoing_queue.empty(); + })) { + throw exception::InvalidOperation("Waiting for output queue has been timed out."); + } + } public: - AsyncManager(const CallbackType &cb); + AsyncManager(const CallbackType &cb) : _callback(cb) + {} virtual ~AsyncManager() = default; - bool isWorking(); - void stop(); - template AsyncInputQueue popFromInput(); - void pushToOutput(R &output); - template void push(std::vector > &inputs); - R pop(); + bool isWorking() + { + if (_exit_thread) + return false; + + return true; + } + void stop() + { + // Make sure to wait for the completion of all inference requests in the incoming queue. + _exit_thread = true; + + _thread_handle->join(); + _thread_handle = nullptr; + + std::lock_guard lock(_outgoing_queue_mutex); + std::queue empty; + + swap(_outgoing_queue, empty); + } + template AsyncInputQueue popFromInput() + { + std::lock_guard lock(_incoming_queue_mutex); + AsyncInputQueue inputQueue = _incoming_queue.front(); + + _incoming_queue.pop(); + AsyncInputQueue dstQueue; + + dstQueue.frame_number = inputQueue.frame_number; + + for (auto &elms : inputQueue.inputs) { + std::vector dst_vector; + + for (size_t idx = 0; idx < elms.size(); idx += sizeof(T)) { + T dst_data; + + std::copy_n(elms.begin() + idx, sizeof(T), reinterpret_cast(&dst_data)); + dst_vector.push_back(dst_data); + } + + dstQueue.inputs.push_back(dst_vector); + } + + return dstQueue; + } + void pushToOutput(R &output) + { + std::lock_guard lock(_outgoing_queue_mutex); + + _outgoing_queue.push(output); + _outgoing_queue_event.notify_one(); + } + template void push(std::vector > &inputs) + { + _input_frame_number++; + + if (!isInputQueueEmpty()) { + LOGD("input frame number(%ld) has been skipped.", _input_frame_number); + return; + } + + AsyncInputQueue in_queue = { _input_frame_number, inputs }; + + pushToInput(in_queue); + + LOGD("Pushed : input frame number = %lu", in_queue.frame_number); + + invoke(); + } + R pop() + { + waitforOutputQueue(); + + return popFromOutput(); + } }; } // machine_learning diff --git a/mv_machine_learning/common/src/async_manager.cpp b/mv_machine_learning/common/src/async_manager.cpp deleted file mode 100644 index 784992d..0000000 --- a/mv_machine_learning/common/src/async_manager.cpp +++ /dev/null @@ -1,253 +0,0 @@ -/** - * Copyright (c) 2023 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 - -#include "mv_private.h" -#include "machine_learning_exception.h" -#include "async_manager.h" -#include "object_detection_type.h" - -#define IS_INPUT_QUEUE_EMPTY(result_type) \ - template bool AsyncManager::isInputQueueEmpty(); \ - template bool AsyncManager::isInputQueueEmpty() -#define INVOKE(result_type) \ - template void AsyncManager::invoke(); \ - template void AsyncManager::invoke() -#define POP_FROM_OUTPUT(result_type) template result_type AsyncManager::popFromOutput() -#define PUSH_TO_INPUT(result_type) \ - template void AsyncManager::pushToInput(AsyncInputQueue & inputQueue); \ - template void AsyncManager::pushToInput(AsyncInputQueue & inputQueue) -#define STOP(result_type) template void AsyncManager::stop() -#define ASYNC_MANAGER(result_type) template AsyncManager::AsyncManager(const CallbackType &cb) -#define IS_WORKING(result_type) template bool AsyncManager::isWorking() -#define POP_FROM_INPUT(result_type) \ - template AsyncInputQueue AsyncManager::popFromInput(); \ - template AsyncInputQueue AsyncManager::popFromInput() -#define PUSH_TO_OUTPUT(result_type) template void AsyncManager::pushToOutput(result_type &output) -#define INFERENCE_THREAD_LOOP(result_type) \ - template void AsyncManager::inferenceThreadLoop(); \ - template void AsyncManager::inferenceThreadLoop() -#define PUSH(result_type) \ - template void AsyncManager::push(std::vector > &inputs); \ - template void AsyncManager::push(std::vector > &inputs) -#define POP(result_type) template result_type AsyncManager::pop() - -using namespace std; -using namespace mediavision::machine_learning::exception; - -namespace mediavision -{ -namespace machine_learning -{ -template AsyncManager::AsyncManager(const CallbackType &cb) : _callback(cb) -{} - -template bool AsyncManager::isWorking() -{ - if (_exit_thread) - return false; - - return true; -} - -template template void AsyncManager::pushToInput(AsyncInputQueue &inputQueue) -{ - lock_guard lock(_incoming_queue_mutex); - AsyncInputQueue dstQueue; - - dstQueue.frame_number = inputQueue.frame_number; - - for (auto &elms : inputQueue.inputs) { - vector dst_vector; - - for (auto &elm : elms) { - unsigned char *bytes = reinterpret_cast(&elm); - - copy_n(bytes, sizeof(T), back_inserter(dst_vector)); - } - - dstQueue.inputs.push_back(dst_vector); - } - - _incoming_queue.push(dstQueue); - _incoming_queue_event.notify_one(); -} - -template template AsyncInputQueue AsyncManager::popFromInput() -{ - lock_guard lock(_incoming_queue_mutex); - AsyncInputQueue inputQueue = _incoming_queue.front(); - - _incoming_queue.pop(); - AsyncInputQueue dstQueue; - - dstQueue.frame_number = inputQueue.frame_number; - - for (auto &elms : inputQueue.inputs) { - vector dst_vector; - - for (size_t idx = 0; idx < elms.size(); idx += sizeof(T)) { - T dst_data; - - copy_n(elms.begin() + idx, sizeof(T), reinterpret_cast(&dst_data)); - dst_vector.push_back(dst_data); - } - - dstQueue.inputs.push_back(dst_vector); - } - - return dstQueue; -} - -template template bool AsyncManager::isInputQueueEmpty() -{ - lock_guard lock(_incoming_queue_mutex); - - return _incoming_queue.empty(); -} - -template void AsyncManager::waitforInputQueue() -{ - unique_lock lock(_incoming_queue_mutex); - - if (!_incoming_queue_event.wait_for(lock, 1s, [this] { return !_incoming_queue.empty(); })) { - LOGD("Waiting for input queue has been timed out."); - } -} - -template R AsyncManager::popFromOutput() -{ - lock_guard lock(_outgoing_queue_mutex); - R output = _outgoing_queue.front(); - - _outgoing_queue.pop(); - - return output; -} - -template bool AsyncManager::isOutputQueueEmpty() -{ - lock_guard lock(_outgoing_queue_mutex); - - return _outgoing_queue.empty(); -} - -template void AsyncManager::waitforOutputQueue() -{ - unique_lock lock(_outgoing_queue_mutex); - - if (!_outgoing_queue_event.wait_for(lock, 10s, [this] { - if (_exit_thread) - throw InvalidOperation("thread is exited already."); - - return !_outgoing_queue.empty(); - })) { - throw InvalidOperation("Waiting for output queue has been timed out."); - } -} - -template void AsyncManager::pushToOutput(R &output) -{ - lock_guard lock(_outgoing_queue_mutex); - - _outgoing_queue.push(output); - _outgoing_queue_event.notify_one(); -} - -template template void AsyncManager::inferenceThreadLoop() -{ - // If user called destroy API then this thread loop will be terminated. - while (!_exit_thread) { - // Wait for input data. - waitforInputQueue(); - - // Exit this thread loop if user called destroy API while in thread loop. - if (_exit_thread) - break; - - // What the callback function has to do is to, - // 1. Get input data from input queue. - // 2. Run inference. - // 3. Put output data to output queue. - _callback(); - } - - // waitforOutputQueue function could wait for the notify event after while loop is exited. - // So make sure to call notify_one() here. - _outgoing_queue_event.notify_one(); -} - -template template void AsyncManager::invoke() -{ - if (!_thread_handle) - _thread_handle = make_unique(&AsyncManager::inferenceThreadLoop, this); -} - -template void AsyncManager::stop() -{ - // Make sure to wait for the completion of all inference requests in the incoming queue. - _exit_thread = true; - - _thread_handle->join(); - _thread_handle = nullptr; - - lock_guard lock(_outgoing_queue_mutex); - queue empty; - - swap(_outgoing_queue, empty); -} - -template template void AsyncManager::push(std::vector > &inputs) -{ - _input_frame_number++; - - if (!isInputQueueEmpty()) { - LOGD("input frame number(%ld) has been skipped.", _input_frame_number); - return; - } - - AsyncInputQueue in_queue = { _input_frame_number, inputs }; - - pushToInput(in_queue); - - LOGD("Pushed : input frame number = %lu", in_queue.frame_number); - - invoke(); -} - -template R AsyncManager::pop() -{ - waitforOutputQueue(); - - return popFromOutput(); -} - -IS_INPUT_QUEUE_EMPTY(ObjectDetectionResult); -INVOKE(ObjectDetectionResult); -POP_FROM_OUTPUT(ObjectDetectionResult); -PUSH_TO_INPUT(ObjectDetectionResult); -STOP(ObjectDetectionResult); -ASYNC_MANAGER(ObjectDetectionResult); -IS_WORKING(ObjectDetectionResult); -POP_FROM_INPUT(ObjectDetectionResult); -PUSH_TO_OUTPUT(ObjectDetectionResult); -INFERENCE_THREAD_LOOP(ObjectDetectionResult); -PUSH(ObjectDetectionResult); -POP(ObjectDetectionResult); - -} -} \ No newline at end of file diff --git a/mv_machine_learning/object_detection/CMakeLists.txt b/mv_machine_learning/object_detection/CMakeLists.txt index 2a1c083..1489b8e 100644 --- a/mv_machine_learning/object_detection/CMakeLists.txt +++ b/mv_machine_learning/object_detection/CMakeLists.txt @@ -11,7 +11,7 @@ if(NOT OpenCV_FOUND) endif() add_library(${PROJECT_NAME} SHARED ${MV_OBJECT_DETECTION_SOURCE_LIST}) -target_link_libraries(${PROJECT_NAME} ${MV_COMMON_LIB_NAME} ${OpenCV_LIBS} ${${PROJECT_NAME}_DEP_LIBRARIES} mv_inference mv_ml_common) +target_link_libraries(${PROJECT_NAME} ${MV_COMMON_LIB_NAME} ${OpenCV_LIBS} ${${PROJECT_NAME}_DEP_LIBRARIES} mv_inference) target_include_directories(${PROJECT_NAME} PRIVATE include ../inference/include ../common/include ../meta/include) install(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR}) install( diff --git a/mv_machine_learning/object_detection/include/object_detection.h b/mv_machine_learning/object_detection/include/object_detection.h index 6846b85..93d46d0 100644 --- a/mv_machine_learning/object_detection/include/object_detection.h +++ b/mv_machine_learning/object_detection/include/object_detection.h @@ -20,7 +20,6 @@ #include #include #include -#include #include #include diff --git a/mv_machine_learning/object_detection/src/object_detection.cpp b/mv_machine_learning/object_detection/src/object_detection.cpp index 6b7e074..e0b379d 100644 --- a/mv_machine_learning/object_detection/src/object_detection.cpp +++ b/mv_machine_learning/object_detection/src/object_detection.cpp @@ -18,7 +18,6 @@ #include #include #include -#include #include "machine_learning_exception.h" #include "mv_machine_learning_common.h" @@ -26,7 +25,6 @@ #include "object_detection.h" using namespace std; -using namespace std::chrono_literals; using namespace mediavision::inference; using namespace MediaVision::Common; using namespace mediavision::common; @@ -354,19 +352,16 @@ void ObjectDetection::perform(mv_source_h &mv_src) template void ObjectDetection::performAsync(ObjectDetectionInput &input, shared_ptr metaInfo) { if (!_async_manager) { - _async_manager = make_unique >( - [this]() - { - AsyncInputQueue inputQueue = _async_manager->popFromInput(); + _async_manager = make_unique >([this]() { + AsyncInputQueue inputQueue = _async_manager->popFromInput(); - inference(inputQueue.inputs); + inference(inputQueue.inputs); - ObjectDetectionResult &resultQueue = result(); + ObjectDetectionResult &resultQueue = result(); - resultQueue.frame_number = inputQueue.frame_number; - _async_manager->pushToOutput(resultQueue); - } - ); + resultQueue.frame_number = inputQueue.frame_number; + _async_manager->pushToOutput(resultQueue); + }); } vector inputVector; diff --git a/packaging/capi-media-vision.spec b/packaging/capi-media-vision.spec index 1043fce..8da3d41 100644 --- a/packaging/capi-media-vision.spec +++ b/packaging/capi-media-vision.spec @@ -394,7 +394,6 @@ find . -name '*.gcno' -not -path "./test/*" -not -path "./mv_machine_learning/*" %manifest %{name}.manifest %license LICENSE.APLv2 %{_libdir}/libmv_inference*.so -%{_libdir}/libmv_ml_common.so %if "%{enable_ml_face_recognition}" == "1" %{_datadir}/%{name}/face_recognition.json %{_libdir}/libmv_training.so