// ===================================================================================================================== // fennec, a free and open source game engine // Copyright © 2025 Medusa Slockbower // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // ===================================================================================================================== /// /// \file test_threading.h /// \brief /// /// /// \details /// \author Medusa Slockbower /// /// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html)) /// /// #ifndef FENNEC_TEST_THREADING_H #define FENNEC_TEST_THREADING_H #include #include #include #include #include #include namespace fennec::test { inline void fennec_test_threading_test_atomic(atomic* test_atomic, size_t N = 1000) { for (size_t i = 0; i < N; ++i) { ++*test_atomic; } } template inline void fennec_test_threading_run_test_atomic(array& threads, size_t N = 1000) { atomic test = 0; for (thread& t : threads) { t = thread(fennec_test_threading_test_atomic, &test, N); } for (thread& t : threads) { t.join(); } fennec_test_run(test.load(), N * ThreadsV); } inline void fennec_test_threading_test_mutex(size_t* var, mutex* m, size_t N = 1000) { for (size_t i = 0; i < N; ++i) { lock_guard guard(*m); ++*var; } } template inline void fennec_test_threading_run_test_mutex(array& threads, size_t N = 1000) { mutex m; size_t test = 0; for (thread& t : threads) { t = thread(fennec_test_threading_test_mutex, &test, &m, N); } for (thread& t : threads) { t.join(); } fennec_test_run(test, N * ThreadsV); } inline void fennec_test_threading_test_mpscq_producer(mpscq* queue, size_t N = 1000) { for (size_t i = 0; i < N; ++i) { queue->emplace(1); } } inline void fennec_test_threading_test_mpscq_consumer(mpscq* queue, atomic* res, atomic* done) { while (not done->load() or not queue->is_empty()) { unique_ptr ptr = queue->pop(); if (ptr) { *res += *ptr; } } } template inline void fennec_test_threading_run_test_mpscq(array& threads, size_t N = 1000) { mpscq queue(N * ThreadsV); for (size_t i = 1; i < ThreadsV; ++i) { threads[i] = thread(fennec_test_threading_test_mpscq_producer, &queue, N); } atomic test; atomic done; threads[0] = thread(fennec_test_threading_test_mpscq_consumer, &queue, &test, &done); for (size_t i = 1; i < ThreadsV; ++i) { threads[i].join(); } done.store(true); threads[0].join(); fennec_test_run(test.load(), N * (ThreadsV - 1)); } template inline double fennec_test_threading_timed(ReturnT (func)(ParamsT...), ArgsT&&...args) { auto start = std::chrono::high_resolution_clock::now(); func(fennec::forward(args)...); return std::chrono::duration(std::chrono::high_resolution_clock::now() - start).count(); } inline void fennec_test_threading() { static constexpr size_t N = 100000, threads = 8; array arr; std::cout << fennec_test_threading_timed(fennec_test_threading_run_test_atomic, arr, N) << "s\n"; std::cout << fennec_test_threading_timed(fennec_test_threading_run_test_mutex, arr, N) << "s\n"; std::cout << fennec_test_threading_timed(fennec_test_threading_run_test_mpscq, arr, N) << "s\n"; } } #endif // FENNEC_TEST_THREADING_H