diff --git a/corsika/detail/framework/analytics/ClassTimer.inl b/corsika/detail/framework/analytics/ClassTimer.inl new file mode 100644 index 0000000000000000000000000000000000000000..c025efe56a9d540de5605395be406794c4f6ab8f --- /dev/null +++ b/corsika/detail/framework/analytics/ClassTimer.inl @@ -0,0 +1,107 @@ +/* + * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +// Another possibility: +// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Execute-Around_Pointer +// for a more global approach +// +// In this case here only a single function is measured via member function pointer. + +#pragma once + +#include <chrono> +#include <utility> + +namespace corsika { + + // Common + + template <typename TClass, typename TRet, typename... TArgs, + TRet (TClass::*TFuncPtr)(TArgs...)> + ClassTimer<TRet (TClass::*)(TArgs...), TFuncPtr>::ClassTimer(TClass& obj) + : obj_(obj) {} + + template <typename TClass, typename TRet, typename... TArgs, + TRet (TClass::*TFuncPtr)(TArgs...)> + TRet ClassTimer<TRet (TClass::*)(TArgs...), TFuncPtr>::call(TArgs... args) { + start_ = TClock::now(); + auto tmp = (obj_.*TFuncPtr)(std::forward<TArgs>(args)...); + timeDiff_ = std::chrono::duration_cast<TDuration>(TClock::now() - start_); + return tmp; + } + + template <typename TClass, typename TRet, typename... TArgs, + TRet (TClass::*TFuncPtr)(TArgs...)> + inline typename ClassTimer<TRet (TClass::*)(TArgs...), TFuncPtr>::TDuration + ClassTimer<TRet (TClass::*)(TArgs...), TFuncPtr>::getTime() const { + return timeDiff_; + } + + // Specialisation 1 + + template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...)> + ClassTimer<void (TClass::*)(TArgs...), TFuncPtr>::ClassTimer(TClass& obj) + : obj_(obj) {} + + template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...)> + void ClassTimer<void (TClass::*)(TArgs...), TFuncPtr>::call(TArgs... args) { + start_ = TClock::now(); + (obj_.*TFuncPtr)(std::forward<TArgs>(args)...); + timeDiff_ = std::chrono::duration_cast<TDuration>(TClock::now() - start_); + return; + } + + template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...)> + inline typename ClassTimer<void (TClass::*)(TArgs...), TFuncPtr>::TDuration + ClassTimer<void (TClass::*)(TArgs...), TFuncPtr>::getTime() const { + return timeDiff_; + } + + /// Specialisation 2 + + template <typename TClass, typename TRet, typename... TArgs, + TRet (TClass::*TFuncPtr)(TArgs...) const> + ClassTimer<TRet (TClass::*)(TArgs...) const, TFuncPtr>::ClassTimer(TClass& obj) + : obj_(obj) {} + + template <typename TClass, typename TRet, typename... TArgs, + TRet (TClass::*TFuncPtr)(TArgs...) const> + TRet ClassTimer<TRet (TClass::*)(TArgs...) const, TFuncPtr>::call(TArgs... args) { + start_ = TClock::now(); + auto tmp = (obj_.*TFuncPtr)(std::forward<TArgs>(args)...); + timeDiff_ = std::chrono::duration_cast<TDuration>(TClock::now() - start_); + return tmp; + } + + template <typename TClass, typename TRet, typename... TArgs, + TRet (TClass::*TFuncPtr)(TArgs...) const> + inline typename ClassTimer<TRet (TClass::*)(TArgs...) const, TFuncPtr>::TDuration + ClassTimer<TRet (TClass::*)(TArgs...) const, TFuncPtr>::getTime() const { + return timeDiff_; + } + + /// Specialisation 3 + template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...) const> + ClassTimer<void (TClass::*)(TArgs...) const, TFuncPtr>::ClassTimer(TClass& obj) + : obj_(obj) {} + + template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...) const> + void ClassTimer<void (TClass::*)(TArgs...) const, TFuncPtr>::call(TArgs... args) { + start_ = TClock::now(); + (obj_.*TFuncPtr)(std::forward<TArgs>(args)...); + timeDiff_ = std::chrono::duration_cast<TDuration>(TClock::now() - start_); + return; + } + + template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...) const> + inline typename ClassTimer<void (TClass::*)(TArgs...) const, TFuncPtr>::TDuration + ClassTimer<void (TClass::*)(TArgs...) const, TFuncPtr>::getTime() const { + return timeDiff_; + } + +} // namespace corsika \ No newline at end of file diff --git a/corsika/detail/framework/analytics/FunctionTimer.inl b/corsika/detail/framework/analytics/FunctionTimer.inl new file mode 100644 index 0000000000000000000000000000000000000000..a40a4a838272d1b153623d93d5f70330c70b0cbd --- /dev/null +++ b/corsika/detail/framework/analytics/FunctionTimer.inl @@ -0,0 +1,34 @@ +/* + * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ +#pragma once + +#include <chrono> +#include <utility> + +namespace corsika { + + template <typename TFunc, typename TClock, typename TDuration> + FunctionTimer<TFunc, TClock, TDuration>::FunctionTimer(TFunc f) + : function_(f) {} + + template <typename TFunc, typename TClock, typename TDuration> + template <typename... TArgs> + auto FunctionTimer<TFunc, TClock, TDuration>::operator()(TArgs&&... args) + -> std::invoke_result_t<TFunc, TArgs...> { + start_ = TClock::now(); + auto tmp = function_(std::forward<TArgs>(args)...); + timeDiff_ = std::chrono::duration_cast<TDuration>(TClock::now() - start_); + return tmp; + } + + template <typename TFunc, typename TClock, typename TDuration> + inline TDuration FunctionTimer<TFunc, TClock, TDuration>::getTime() const { + return timeDiff_; + } + +} // namespace corsika diff --git a/corsika/framework/analytics/ClassTimer.hpp b/corsika/framework/analytics/ClassTimer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f0bc63914bc894523db7f3c9cd849c8635d5f103 --- /dev/null +++ b/corsika/framework/analytics/ClassTimer.hpp @@ -0,0 +1,123 @@ +/* + * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +// Another possibility: +// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Execute-Around_Pointer +// for a more global approach +// +// In this case here only a single function is measured via member function pointer. + +#pragma once + +#include <chrono> +#include <utility> + +namespace corsika { + + template <class TClass> + class ClassTimerImpl { + protected: + /// Default clock used for time measurement + using TClock = std::chrono::high_resolution_clock; + + /// Internal resolution of the time measurement + using TDuration = std::chrono::microseconds; + + /// Reference to the class object on which the function should be called + TClass& obj_; + + /// Startpoint of time measurement + typename TClock::time_point start_; + + /// Measured runtime of the function + TDuration timeDiff_; + }; + + /// Measure the runtime of a single class function + /** + * @tparam TClassFunc Type of the member function pointer that should be wrapped + * @tparam TFunc Actual function of the type defined in TClass + */ + template <typename TClassFunc, TClassFunc TFunc> + class ClassTimer; + + /// Measure the runtime of a single class function + /** Specialisation to capture exact information about the composition of the member + * function pointer used. + * + * This class wrapes a single function and allowes the measureing of its runtime if it + * called via the "call(...)" function + * + * @tparam TClass Class of the function that should be wrapped + * @tparam TRet Return value of the wrapped function + * @tparam TArgs Arguments passed to the wrapped function + * @tparam TFuncPtr Actual function of the type defined by TRet + * TClass::TFuncPtr(TArgs...) + */ + template <typename TClass, typename TRet, typename... TArgs, + TRet (TClass::*TFuncPtr)(TArgs...)> + class ClassTimer<TRet (TClass::*)(TArgs...), TFuncPtr> : ClassTimerImpl<TClass>{ + private: + public: + ClassTimer(TClass& obj); + + /// Executes the wrapped function + /** This function executes and measure the runtime of the wrapped function with the + * highest precision available (high_resolution_clock). + * + * @param args Arguments are perfect forwarded to the wrapped function. + * @return Returns the return value of the wrapped function. This value get copied + * during the process and therefore must be copy constructible! + */ + TRet call(TArgs... args); + + /// returns the last runtime of the wraped function accessed via call + inline TDuration getTime() const; + }; + + /// Specialisation for member functions without return value + template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...)> + class ClassTimer<void (TClass::*)(TArgs...), TFuncPtr> : ClassTimerImpl<TClass> { + private: + using TClock = std::chrono::high_resolution_clock; + using TDuration = std::chrono::microseconds; + + public: + ClassTimer(TClass& obj); + + void call(TArgs... args); + + inline TDuration getTime() const; + }; + + /// Specialisation for const member functions + template <typename TClass, typename TRet, typename... TArgs, + TRet (TClass::*TFuncPtr)(TArgs...) const> + class ClassTimer<TRet (TClass::*)(TArgs...) const, TFuncPtr> : ClassTimerImpl<TClass>{ + public: + ClassTimer(TClass& obj); + + TRet call(TArgs... args); + + inline TDuration getTime() const; + }; + + /// Specialisation for const member functions without return value + template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...) const> + class ClassTimer<void (TClass::*)(TArgs...) const, TFuncPtr> : ClassTimerImpl<TClass> { + public: + ClassTimer(TClass& obj); + + void call(TArgs... args); + + inline TDuration getTime() const; + }; + +} // namespace corsika + +#include <corsika/detail/framework/analytics/ClassTimer.inl> \ No newline at end of file diff --git a/corsika/framework/analytics/FunctionTimer.hpp b/corsika/framework/analytics/FunctionTimer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0d19bdc28a474cdda33bad0e2cfc6b2bc3e7c361 --- /dev/null +++ b/corsika/framework/analytics/FunctionTimer.hpp @@ -0,0 +1,63 @@ +/* + * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ +#pragma once + +#include <chrono> +#include <utility> + +namespace corsika { + + /// Wraps and measures the runtime of a single function type object + /** + * + * @tparam TFunc funtion pointer that should be wrapped + * @tparam TClock type of the clock that should be used for measurements, default is + * high_resolution_clock + * @tparam TDuration type of std::duration to measure the elapsed time, default is + * microseconds + */ + template <typename TFunc, typename TClock = std::chrono::high_resolution_clock, + typename TDuration = std::chrono::microseconds> + class FunctionTimer { + + public: + /** Constructs the wrapper with the given functionpointer + * @param f Function or functor whose runtime should be measured + **/ + FunctionTimer(TFunc f); + + /** Functor for calling the wrapped function + * This functor calls the wrapped function and measures the elapsed time between call + *and return. The return value needs to be copy constructible. + * @tparam TArgs Parameter types that are forwarded to the function. The use of + *correct types is the responsibility of the user, no checks are done. + * @param args Arguments are forwarded to the wrapped function. This method does not + *support overloaded function resolution. + * @return The return value of the wrapped function is temporarily copied and then + *returned by value + **/ + template <typename... TArgs> + auto operator()(TArgs&&... args) -> std::invoke_result_t<TFunc, TArgs...>; + + /** + * Returns the runtime of the last call to the wrapped function + * @return Returns the measured runtime of the wrapped function/functor in the unit + *given by TDuration + **/ + inline TDuration getTime() const; + + private: + typename TClock::time_point start_; + TDuration timeDiff_; + + TFunc function_; + }; + +} // namespace corsika + +#include <corsika/detail/framework/analytics/FunctionTimer.inl> \ No newline at end of file diff --git a/tests/framework/CMakeLists.txt b/tests/framework/CMakeLists.txt index 296c37b18bda747cfc44adc676da92905e6c85dc..97450901cf97eb298ba0a4ff192e02a0d17f7991 100644 --- a/tests/framework/CMakeLists.txt +++ b/tests/framework/CMakeLists.txt @@ -1,10 +1,12 @@ set (test_framework_sources # testCascade.cpp this is most important, but whole content of former Processes folder missing yet + testClassTimer.cpp testCombinedStack.cpp testCOMBoost.cpp #testCorsikaFenv.cpp # does not work because of use of exceptions in catch2 testFourVector.cpp + testFunctionTimer.cpp testGeometry.cpp testLogging.cpp TestMain.cpp diff --git a/tests/framework/testClassTimer.cpp b/tests/framework/testClassTimer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ffbd79429448046f36fecdf1acd3172309a843cf --- /dev/null +++ b/tests/framework/testClassTimer.cpp @@ -0,0 +1,147 @@ +/* + * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +#include <corsika/framework/analytics/ClassTimer.hpp> + +#include <catch2/catch.hpp> + +#include <chrono> +#include <iostream> +#include <thread> + +using namespace corsika; + +class _foo2 { +public: + int inside(int) { return 123; } + + int inside(char) { return 312; } +}; + +class _foo1 : public _foo2 { +public: + int inside(int) { return 123; } +}; + +class foo : public _foo1 { +public: + int bar() { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return 31415; + } + + void bar2(int) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return; + } + + inline void bar_const() const { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return; + } + + int inside() { + auto tc = corsika::ClassTimer<int (_foo1::*)(int), &_foo1::inside>(*this); + + auto r = tc.call(1); + + return r; + } +}; + +template <typename TType, TType> +class timeMin; + +template <typename TType, typename TRet, typename... TArgs, + TRet (TType::*TFuncPtr)(TArgs...)> +class timeMin<TRet (TType::*)(TArgs...), TFuncPtr> { +private: + TType& obj_; + +public: + timeMin(TType& obj) + : obj_(obj) {} + + TRet call(TArgs... args) { return (obj_.*TFuncPtr)(std::forward<TArgs>(args)...); } +}; + +// quasi processor +class fooT1 { +public: + template <typename T1, typename T2> + int inside_t(T1 a, T2 b, T2 c) { + return 123; + } +}; + +// exec_time_impl +template <typename T> +class fooT2 : public T { +public: + using _T = T; +}; + +// exec_time_impl +template <typename T> +class fooT3 : public fooT2<T> { +public: + template <typename T1, typename T2> + int inside_t(T1 a, T2 b, T2 c) { + auto tc = + timeMin<int (fooT2<T>::_T::*)(T1, T2, T2), + &fooT2<T>::_T::template inside_t<T1, T2>>(*this); // <- dependent template + + auto r = tc.call(a, b, c); + + return r; + } +}; + +TEST_CASE("ClassTimer", "[Timer]") { + SECTION("Measure runtime of a function without arguments") { + + auto test = foo(); + auto tc = corsika::ClassTimer<decltype(&foo::bar), &foo::bar>(test); + + tc.call(); + + CHECK(tc.getTime().count() == Approx(100000).margin(10000)); + } + + SECTION("Measure runtime of a function with arguments") { + + auto test = foo(); + auto tc = corsika::ClassTimer<decltype(&foo::bar2), &foo::bar2>(test); + + tc.call(1); + + CHECK(tc.getTime().count() == Approx(100000).margin(10000)); + } + + SECTION("Measure runtime of a const function without arguments") { + + auto test = foo(); + auto tc = + corsika::ClassTimer<decltype(&foo::bar_const), &foo::bar_const>(test); + + tc.call(); + + CHECK(tc.getTime().count() == Approx(100000).margin(10000)); + } + + SECTION("Measure runtime of function inside class") { + + auto test = foo(); + CHECK(test.inside() == 123); + } + + SECTION("Measure runtime of function inside class") { + auto test = fooT3<fooT1>(); + CHECK(test.inside_t(1, 'a', 'b') == 123); + } +} diff --git a/tests/framework/testFunctionTimer.cpp b/tests/framework/testFunctionTimer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c825f3efbd2f6a82427f5c7e33fe2896e0594208 --- /dev/null +++ b/tests/framework/testFunctionTimer.cpp @@ -0,0 +1,48 @@ +/* + * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +#include <corsika/framework/analytics/FunctionTimer.hpp> + +#include <catch2/catch.hpp> + +#include <chrono> +#include <iostream> +#include <thread> + +using namespace corsika; + +int testFunc() { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return 31415; +} + +class TestClass { +public: + int operator()() { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + return 31415; + } +}; + +TEST_CASE("FunctionTimer", "[Timer]") { + SECTION("Measure runtime of a free function") { + + auto test = corsika::FunctionTimer(testFunc); + + std::cout << test() << std::endl; + std::cout << test.getTime().count() << std::endl; + } + + SECTION("Measure runtime of a class functor") { + TestClass testC; + auto test = corsika::FunctionTimer(testC); + + std::cout << test() << std::endl; + std::cout << test.getTime().count() << std::endl; + } +}