Index: source/ps/tests/test_TaskManager.h =================================================================== --- source/ps/tests/test_TaskManager.h +++ source/ps/tests/test_TaskManager.h @@ -21,8 +21,15 @@ #include "ps/TaskManager.h" #include +#include +#include #include #include +#include +#include +#include + +using namespace std::literals; class TestTaskManager : public CxxTest::TestSuite { @@ -115,4 +122,89 @@ TS_ASSERT_EQUALS(futures[i].Get(), 5); #undef ITERATIONS } + + /** + * Measures the performance cost all Future have to pay. Even if they return nothing and never have + * to wait for the result. + */ + void test_perf_mandatory_cost_DISABLED() + { + constexpr size_t iterations{50000}; + Threading::TaskManager& manager{Threading::TaskManager::Instance()}; + + // Use class over lambda so it is readable in the debug-symbols. + class Noop + { + public: + void operator()() noexcept {}; + }; + + // Use std::optional so we can measure destruction. + auto futures{std::make_optional, iterations>>()}; + const auto beginning{std::chrono::steady_clock::now()}; + + for (Future& future : *futures) + future = manager.PushTask(Noop{}); + + const auto enqueued{std::chrono::steady_clock::now()}; + + for (Future& future : *futures) + future.Wait(); + + const auto joined{std::chrono::steady_clock::now()}; + + futures.reset(); + + const auto destructed{std::chrono::steady_clock::now()}; + + std::cout << "async cost:\n"; + std::cout << "enqueueing:\t" + << std::chrono::duration_cast(enqueued - beginning).count() + << "ms\n"; + std::cout << "work done:\t" + << std::chrono::duration_cast(joined - beginning).count() + << "ms\n"; + std::cout << "total:\t" + << std::chrono::duration_cast(destructed - beginning).count() + << "ms\n"; + } + + /** + * A more realistic measurement: Enqueue one task and wait for it. + */ + void test_perf_round_trip_DISABLED() + { + constexpr size_t iterations{50000}; + Threading::TaskManager& manager{Threading::TaskManager::Instance()}; + + // Use class over lambda so it is readable in the debug-symbols. + class EmulateWork + { + public: + auto operator()() + { + // We have to time this since it might sleep longer than the provided duration. + const auto beginSleep{std::chrono::steady_clock::now()}; + std::this_thread::sleep_for(1us); + return std::chrono::steady_clock::now() - beginSleep; + } + }; + + auto accumulator{std::chrono::steady_clock::duration::zero()}; + for (size_t i{0}; i != iterations; ++i) + { + const auto beginning{std::chrono::steady_clock::now()}; + Future f{manager.PushTask(EmulateWork{})}; + const auto sleepTime{f.Get()}; + const auto joined{std::chrono::steady_clock::now()}; + + static_assert( + std::is_same_v>); + accumulator += joined - beginning - sleepTime; + } + + std::cout << "async round-trip:\n"; + std::cout << "total:\t" << + std::chrono::duration_cast(accumulator).count() << "ms\n"; + } };