feat: implemented simulation multithreading
This commit is contained in:
parent
34a289d36b
commit
99ec92ba88
|
|
@ -1,10 +1,12 @@
|
|||
#include "simulation.h"
|
||||
#include "input.h"
|
||||
#include "thread_pool.h"
|
||||
#include <SDL3/SDL_log.h>
|
||||
#include <SDL3/SDL_render.h>
|
||||
#include <algorithm>
|
||||
#include <clay/clay.h>
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
|
|
@ -73,21 +75,33 @@ static size_t CountNeighbors(Cell const &cell) {
|
|||
return count;
|
||||
}
|
||||
|
||||
static void PopulateChanges() {
|
||||
static std::mutex underpopulatedMutex, overpopulatedMutex, bornMutex;
|
||||
|
||||
static void FindOverpopulated() {
|
||||
overpopulatedMutex.lock();
|
||||
overpopulated.clear();
|
||||
underpopulated.clear();
|
||||
born.clear();
|
||||
// TODO: consider multithreading, this is highly parallelisable
|
||||
std::ranges::copy_if(living, std::back_inserter(overpopulated),
|
||||
[&](Cell const &c) -> bool {
|
||||
size_t const neighbors{ CountNeighbors(c) };
|
||||
return neighbors > 3;
|
||||
});
|
||||
overpopulatedMutex.unlock();
|
||||
}
|
||||
|
||||
static void FindUnderpopulated() {
|
||||
underpopulatedMutex.lock();
|
||||
underpopulated.clear();
|
||||
std::ranges::copy_if(living, std::back_inserter(underpopulated),
|
||||
[&](Cell const &c) -> bool {
|
||||
size_t const neighbors{ CountNeighbors(c) };
|
||||
return neighbors < 2;
|
||||
});
|
||||
underpopulatedMutex.unlock();
|
||||
}
|
||||
|
||||
static void FindBorn() {
|
||||
bornMutex.lock();
|
||||
born.clear();
|
||||
for (Cell const &cell : living) {
|
||||
std::vector<Cell> neighbors{ NeighborSet(cell) };
|
||||
std::ranges::copy_if(neighbors, std::back_inserter(born),
|
||||
|
|
@ -96,6 +110,19 @@ static void PopulateChanges() {
|
|||
return !living.contains(c) && neighbors == 3;
|
||||
});
|
||||
}
|
||||
bornMutex.unlock();
|
||||
}
|
||||
|
||||
static void PopulateChanges() {
|
||||
#if 1
|
||||
threading::tasks.ScheduleTask(&FindOverpopulated);
|
||||
threading::tasks.ScheduleTask(&FindUnderpopulated);
|
||||
threading::tasks.ScheduleTask(&FindBorn);
|
||||
#else
|
||||
FindOverpopulated();
|
||||
FindUnderpopulated();
|
||||
FindBorn();
|
||||
#endif
|
||||
}
|
||||
|
||||
void InitializeRandom(size_t livingChance, int64_t fillArea) {
|
||||
|
|
@ -115,15 +142,21 @@ void InitializeRandom(size_t livingChance, int64_t fillArea) {
|
|||
}
|
||||
|
||||
void Step() {
|
||||
underpopulatedMutex.lock();
|
||||
for (Cell const &cell : underpopulated) {
|
||||
living.erase(cell);
|
||||
}
|
||||
underpopulatedMutex.unlock();
|
||||
overpopulatedMutex.lock();
|
||||
for (Cell const &cell : overpopulated) {
|
||||
living.erase(cell);
|
||||
}
|
||||
overpopulatedMutex.unlock();
|
||||
bornMutex.lock();
|
||||
for (Cell const &cell : born) {
|
||||
living.insert(cell);
|
||||
}
|
||||
bornMutex.unlock();
|
||||
PopulateChanges();
|
||||
}
|
||||
|
||||
|
|
@ -150,24 +183,30 @@ void Draw(SDL_Renderer *renderer, double cellSizePercent) {
|
|||
if (!drawDebugInfo) {
|
||||
return;
|
||||
}
|
||||
overpopulatedMutex.lock();
|
||||
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
|
||||
for (Cell const &cell : overpopulated) {
|
||||
cellRect.x = (viewOffset.x + cell.x) * cellRect.w;
|
||||
cellRect.y = (viewOffset.y + cell.y) * cellRect.h;
|
||||
SDL_RenderRect(renderer, &cellRect);
|
||||
}
|
||||
overpopulatedMutex.unlock();
|
||||
underpopulatedMutex.lock();
|
||||
SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255);
|
||||
for (Cell const &cell : underpopulated) {
|
||||
cellRect.x = (viewOffset.x + cell.x) * cellRect.w;
|
||||
cellRect.y = (viewOffset.y + cell.y) * cellRect.h;
|
||||
SDL_RenderRect(renderer, &cellRect);
|
||||
}
|
||||
underpopulatedMutex.unlock();
|
||||
bornMutex.lock();
|
||||
SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255);
|
||||
for (Cell const &cell : born) {
|
||||
cellRect.x = (viewOffset.x + cell.x) * cellRect.w;
|
||||
cellRect.y = (viewOffset.y + cell.y) * cellRect.h;
|
||||
SDL_RenderRect(renderer, &cellRect);
|
||||
}
|
||||
bornMutex.unlock();
|
||||
}
|
||||
|
||||
void SetSimulationHovered(bool value) {
|
||||
|
|
|
|||
56
src/thread_pool.cpp
Normal file
56
src/thread_pool.cpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#include "thread_pool.h"
|
||||
#include <algorithm>
|
||||
#include <thread>
|
||||
|
||||
namespace threading {
|
||||
ThreadPool::ThreadPool() {
|
||||
size_t const thread_count{ std::max(std::thread::hardware_concurrency() / 2, 1u) };
|
||||
this->threads.reserve(thread_count);
|
||||
for (size_t i{ 0 }; i < thread_count; ++i) {
|
||||
this->threads.emplace_back(std::bind(&ThreadPool::ThreadFn, this));
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool::~ThreadPool() {
|
||||
this->lock.lock();
|
||||
this->shutdown = true;
|
||||
this->threadNotifier.notify_all();
|
||||
this->lock.unlock();
|
||||
|
||||
for (std::thread &thread : this->threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
size_t ThreadPool::GetThreadCount() {
|
||||
return this->threads.size();
|
||||
}
|
||||
|
||||
void ThreadPool::ScheduleTask(TaskFunc fn) {
|
||||
this->lock.lock();
|
||||
this->taskQueue.emplace(std::move(fn));
|
||||
this->threadNotifier.notify_one();
|
||||
this->lock.unlock();
|
||||
}
|
||||
|
||||
void ThreadPool::ThreadFn() {
|
||||
TaskFunc function;
|
||||
for(;;) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock{ this->lock };
|
||||
while (!this->shutdown && this->taskQueue.empty()) {
|
||||
this->threadNotifier.wait(lock);
|
||||
}
|
||||
if (this->taskQueue.empty()) {
|
||||
return;
|
||||
}
|
||||
function = std::move(this->taskQueue.front());
|
||||
this->taskQueue.pop();
|
||||
}
|
||||
|
||||
function.operator()();
|
||||
}
|
||||
}
|
||||
|
||||
ThreadPool tasks{};
|
||||
}
|
||||
30
src/thread_pool.h
Normal file
30
src/thread_pool.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#ifndef THREAD_POOL_H
|
||||
#define THREAD_POOL_H
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
|
||||
namespace threading {
|
||||
typedef std::function<void()> TaskFunc;
|
||||
|
||||
class ThreadPool {
|
||||
public: ThreadPool();
|
||||
~ThreadPool();
|
||||
void ScheduleTask(TaskFunc jobs);
|
||||
size_t GetThreadCount();
|
||||
private:
|
||||
void ThreadFn();
|
||||
std::queue<TaskFunc> taskQueue{};
|
||||
std::vector<std::thread> threads{};
|
||||
std::mutex lock{};
|
||||
std::condition_variable threadNotifier{};
|
||||
bool shutdown{ false };
|
||||
};
|
||||
|
||||
extern ThreadPool tasks;
|
||||
}
|
||||
|
||||
#endif // !THREAD_POOL_H
|
||||
Loading…
Reference in a new issue