diff --git a/CMakeLists.txt b/CMakeLists.txt index ad20c18..4f2b4b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,4 +46,7 @@ add_executable(block_choice experiments/block_choice.cpp) target_link_libraries(block_choice ${PIP_LIBRARY}) add_executable(smbusdata_crash_test experiments/smbusdata_crash_test.cpp) -target_link_libraries(smbusdata_crash_test SMBricks_shared ${PIP_LIBRARY} ${PIP_CONCURRENT_LIBRARY} ${PIP_CRYPT_LIBRARY}) \ No newline at end of file +target_link_libraries(smbusdata_crash_test SMBricks_shared ${PIP_LIBRARY} ${PIP_CONCURRENT_LIBRARY} ${PIP_CRYPT_LIBRARY}) + +add_executable(packaged_task experiments/packaged_task.cpp) +target_link_libraries(packaged_task SMBricks_shared ${PIP_LIBRARY} ${PIP_CONCURRENT_LIBRARY} ${PIP_CRYPT_LIBRARY}) \ No newline at end of file diff --git a/experiments/packaged_task.cpp b/experiments/packaged_task.cpp new file mode 100644 index 0000000..269e4fa --- /dev/null +++ b/experiments/packaged_task.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include + +/** + * @brief Wrapper for custom invoke operator available function types. + * @note Source from: "Энтони Уильямс, Параллельное программирование на С++ в действии. Практика разработки многопоточных + * программ. Пер. с англ. Слинкин А. А. - M.: ДМК Пресс, 2012 - 672c.: ил." (page 387) + */ +class FunctionWrapper { + struct ImplBase { + virtual void call() = 0; + virtual ~ImplBase() = default; + }; + + std::unique_ptr impl; + + template + struct ImplType: ImplBase { + F f; + explicit ImplType(F&& f): f(std::forward(f)) {} + void call() final { f(); } + }; +public: + template::value> > + explicit FunctionWrapper(F&& f): impl(new ImplType(std::forward(f))) {} + + void operator()() { impl->call(); } + + FunctionWrapper() = default; + FunctionWrapper(FunctionWrapper&& other) noexcept : impl(std::move(other.impl)) {} + FunctionWrapper& operator=(FunctionWrapper&& other) noexcept { + impl = std::move(other.impl); + return *this; + } + + FunctionWrapper(const FunctionWrapper&) = delete; + FunctionWrapper& operator=(const FunctionWrapper&) = delete; +}; + +template +float check_performance(Func test_function) { + auto start = std::chrono::high_resolution_clock::now(); + for (int i = 0; i < 5 * 1000 * 1000; ++i) { + test_function(); + } + auto end = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(end - start).count() / 1000.f; +} + +int main() { + std::queue queue; + queue.push(std::move(FunctionWrapper())); + FunctionWrapper functionWrapper = std::move(queue.front()); + queue.pop(); + + auto test_lambda = [](){ int i = 0; while(i < 100) i++; }; + + float direct_call_performance = check_performance([&test_lambda](){ + std::unique_ptr > test_function_p(new std::function(test_lambda)); + test_function_p->operator()(); + }); + std::cout << "Direct call by pointer: " << direct_call_performance << " ms" << std::endl; + + float lambda_wrapper_performance = check_performance([&test_lambda](){ + auto copy_lambda = test_lambda; + FunctionWrapper wrapper(std::move(copy_lambda)); + wrapper(); + }); + std::cout << "Lambda with function wrapper: " << lambda_wrapper_performance << " ms" << std::endl; + + float std_function_wrapper_performance = check_performance([&test_lambda](){ + std::function test_function(test_lambda); + FunctionWrapper wrapper(std::move(test_function)); + wrapper(); + }); + std::cout << "std function with function wrapper: " << std_function_wrapper_performance << " ms" << std::endl; + + float packaged_task_wrapper_performance = check_performance([&test_lambda](){ + std::packaged_task test_packaged_task(test_lambda); + FunctionWrapper wrapper(test_packaged_task); + wrapper(); + }); + std::cout << "Packaged task with function wrapper: " << packaged_task_wrapper_performance << " ms" << std::endl; + + return 0; +} diff --git a/readme.md b/readme.md index dc0571a..a533e95 100644 --- a/readme.md +++ b/readme.md @@ -31,6 +31,18 @@ stdMutex: 25.5425 ms stdAtomic: 16.6967 ms ``` +### Эксперименты с std::packaged_task и др. способами хранения функций + +В тестах все способы кроме `std::packaged_task` показали приблизительно одинаковый результат. Вероятно, много ресурсов +расходуется на создание пары future-promise. + +```cmd +Direct call by pointer: 261.328 ms +Lambda with function wrapper: 241.38 ms +std function with function wrapper: 266.289 ms +Packaged task with function wrapper: 2476.41 ms +``` + ## Результаты на Linux Измерения производительности проводились с `-O3` оптимизацией.