diff --git a/src/simulation.cpp b/src/simulation.cpp index 3c9b7eb..df36ea7 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -11,20 +11,31 @@ #include #include -namespace simulation { -bool drawDebugInfo{ true }; - -static std::set living{}; -static std::vector overpopulated{}; -static std::vector underpopulated{}; -static std::vector born{}; - #ifdef __glibc_likely #define likely(cond_) __glibc_likely(cond_) #else #define likely(cond_) (cond_) #endif +#define SIM_MULTITHREADING 0 + +namespace simulation { +bool drawDebugInfo{ true }; + +struct ThreadWorkload { + size_t seg_idx{0}, seg_len{1}; + std::mutex mtx{}; + std::vector changes{}; +}; + +static std::set living{}; + +static std::vector> overpopulated{}; +static std::vector> underpopulated{}; +static std::vector> born{}; +static uint64_t generationStartTime{ 0 }; +static unsigned tasks{ 0 }; std::mutex tasksMutex{}; + CellIterator::CellIterator(Cell begin, Cell end) : state{ begin } , begin{ begin }, end{ end } {} @@ -56,16 +67,6 @@ bool CellIterator::at_end() const { return this->state.y == this->end.y; } -static std::vector NeighborSet(Cell cell) { - std::vector out{}; - for (Cell const &c : CellRange{ { cell.x - 1, cell.y - 1 }, { cell.x + 2, cell.y + 2 } }) { - if (likely(c != cell)) { - out.push_back(c); - } - } - return out; -} - static size_t CountNeighbors(Cell const &cell) { size_t count{ 0 }; for (Cell const &c : CellRange{ {cell.x - 1, cell.y - 1}, { cell.x + 2, cell.y + 2} }) { @@ -76,100 +77,125 @@ static size_t CountNeighbors(Cell const &cell) { return count; } -static std::mutex underpopulatedMutex, overpopulatedMutex, bornMutex; - -static void FindOverpopulated(size_t segment, size_t length) { - overpopulatedMutex.lock(); - std::set::iterator begin{ living.begin() }; - std::set::iterator end{ living.begin() }; - - std::advance(begin, segment * length); - std::advance(end, segment * length + length); - - std::copy_if(begin, end, std::back_inserter(overpopulated), - [&](Cell const &c) -> bool { - size_t const neighbors{ CountNeighbors(c) }; - return neighbors > 3; - }); - overpopulatedMutex.unlock(); -} - -static void FindUnderpopulated(size_t segment, size_t length) { - uint64_t ns{ SDL_GetTicksNS() }; - underpopulatedMutex.lock(); - std::set::iterator begin{ living.begin() }; - std::set::iterator end{ living.begin() }; - - std::advance(begin, segment * length); - std::advance(end, segment * length + length); - - std::copy_if(begin, end, std::back_inserter(underpopulated), - [&](Cell const &c) -> bool { - size_t const neighbors{ CountNeighbors(c) }; - return neighbors < 2; - }); - underpopulatedMutex.unlock(); -} - -static void FindBorn(size_t segment, size_t length) { - uint64_t ns{ SDL_GetTicksNS() }; - bornMutex.lock(); - std::set::iterator itr{ living.begin() }; - std::set::iterator end{ living.begin() }; - - std::advance(itr, segment * length); - std::advance(end, segment * length + length); - - for (;itr != end; ++itr) { - std::vector neighbors{ NeighborSet(*itr) }; - std::ranges::copy_if(neighbors, std::back_inserter(born), - [&](Cell const &c) -> bool { - size_t const neighbors{ CountNeighbors(c) }; - return !living.contains(c) && neighbors == 3; - }); +static void TaskComplete() { + std::scoped_lock lock{ tasksMutex }; + if (--tasks == 0) { + SDL_Log("Generation Complete %lfs", (double)(SDL_GetTicksNS() - generationStartTime) * 0.0000000001); } - bornMutex.unlock(); } -#define MULTITHREADING 1 -static uint64_t generationStartTime{ 0 }; -static bool logGenerationTime{ false }; +static void FindOverpopulated(std::shared_ptr wl) { + std::scoped_lock lock{ wl->mtx }; + wl->changes.clear(); + + std::set::iterator begin{ living.begin() }; + std::set::iterator end{ living.begin() }; + + std::advance(begin, wl->seg_idx * wl->seg_len); + std::advance(end, wl->seg_idx * wl->seg_len + wl->seg_len); + + std::copy_if(begin, end, std::back_inserter(wl->changes), + [&](Cell const &c) -> bool { + return CountNeighbors(c) > 3; + }); + TaskComplete(); +} + +static void FindUnderpopulated(std::shared_ptr wl) { + std::scoped_lock lock{ wl->mtx }; + wl->changes.clear(); + + std::set::iterator begin{ living.begin() }; + std::set::iterator end{ living.begin() }; + + std::advance(begin, wl->seg_idx * wl->seg_len); + std::advance(end, wl->seg_idx * wl->seg_len + wl->seg_len); + + std::copy_if(begin, end, std::back_inserter(wl->changes), + [&](Cell const &c) -> bool { + return CountNeighbors(c) < 2; + }); + TaskComplete(); +} + +static void FindBorn(std::shared_ptr wl) { + std::scoped_lock lock{ wl->mtx }; + wl->changes.clear(); + + std::set::iterator itr{ living.begin() }; + + std::advance(itr, wl->seg_idx * wl->seg_len); + + std::for_each_n(itr, wl->seg_len, [&wl](Cell const ¢er){ + CellRange range{{ center.x - 1, center.y - 1 }, {center.x + 2, center.y + 2 }}; + std::copy_if(range.begin(), range.end(), std::back_inserter(wl->changes), + [&](Cell const &c) -> bool { + return !living.contains(c) && CountNeighbors(c) == 3; + }); + }); + TaskComplete(); +} static void PopulateChanges() { -#if MULTITHREADING - constexpr size_t split{ 1 }; + static bool first_run{ true }; +#if SIM_MULTITHREADING + constexpr size_t split{ 4 }; + if (first_run) { + first_run = false; + born.resize(split); + underpopulated.resize(split); + overpopulated.resize(split); + } + SDL_Log("Multithreading ON"); - logGenerationTime = true; + { std::scoped_lock lock{ tasksMutex }; + tasks = 3 * split; } generationStartTime = SDL_GetTicksNS(); size_t const seg_length{ living.size() / split }; for (size_t i{ 0 }; i < split; ++i) { - threading::tasks.ScheduleTask(std::bind(FindOverpopulated, i, seg_length)); - threading::tasks.ScheduleTask(std::bind(FindUnderpopulated, i, seg_length)); - threading::tasks.ScheduleTask(std::bind(FindBorn, i, seg_length)); + if (overpopulated[i] == nullptr) + overpopulated[i] = std::make_shared(); + { std::scoped_lock lock{ overpopulated[i]->mtx }; + overpopulated[i]->seg_idx = i; overpopulated[i]->seg_len = seg_length; + threading::tasks.ScheduleTask(std::bind(FindOverpopulated, overpopulated[i])); + } + if (underpopulated[i] == nullptr) + underpopulated[i] = std::make_shared(); + { std::scoped_lock lock{ underpopulated[i]->mtx }; + underpopulated[i]->seg_idx = i; underpopulated[i]->seg_len = seg_length; + threading::tasks.ScheduleTask(std::bind(FindUnderpopulated, underpopulated[i])); + } + if (born[i] == nullptr) + born[i] = std::make_shared(); + { std::scoped_lock lock{ born[i]->mtx }; + born[i]->seg_idx = i; born[i]->seg_len = seg_length; + threading::tasks.ScheduleTask(std::bind(FindBorn, born[i])); + } } #else + if (first_run) { + first_run = false; + overpopulated.emplace_back(std::make_shared()); + overpopulated[0]->seg_idx = 0; + overpopulated[0]->seg_len = living.size(); + underpopulated.emplace_back(std::make_shared()); + underpopulated[0]->seg_idx = 0; + underpopulated[0]->seg_len = living.size(); + born.emplace_back(std::make_shared()); + born[0]->seg_idx = 0; + born[0]->seg_len = living.size(); + } SDL_Log("Multithreading OFF"); - logGenerationTime = true; + { std::scoped_lock lock{ tasksMutex }; + tasks = 3; } generationStartTime = SDL_GetTicksNS(); - FindOverpopulated(0, living.size()); - FindUnderpopulated(0, living.size()); - FindBorn(0, living.size()); + FindOverpopulated(overpopulated[0]); + FindUnderpopulated(underpopulated[0]); + FindBorn(born[0]); #endif } void InitializeRandom(size_t livingChance, int64_t fillArea) { - underpopulatedMutex.lock(); - underpopulated.clear(); - underpopulatedMutex.unlock(); - - overpopulatedMutex.lock(); - overpopulated.clear(); - overpopulatedMutex.unlock(); - - bornMutex.lock(); - born.clear(); - bornMutex.unlock(); - living.clear(); Cell itr{ 0, 0 }; @@ -187,28 +213,24 @@ void InitializeRandom(size_t livingChance, int64_t fillArea) { } void Step() { - underpopulatedMutex.lock(); - overpopulatedMutex.lock(); - bornMutex.lock(); - - for (Cell const &cell : underpopulated) { - living.erase(cell); + for (std::shared_ptr wl : underpopulated) { + if (wl == nullptr) continue; + std::scoped_lock l{ wl->mtx }; + for (Cell const &cell : wl->changes) + living.erase(cell); } - underpopulated.clear(); - - for (Cell const &cell : overpopulated) { - living.erase(cell); + for (std::shared_ptr wl : overpopulated) { + if (wl == nullptr) continue; + std::scoped_lock l{ wl->mtx }; + for (Cell const &cell : wl->changes) + living.erase(cell); } - overpopulated.clear(); - - for (Cell const &cell : born) { - living.insert(cell); + for (std::shared_ptr wl : born) { + if (wl == nullptr) continue; + std::scoped_lock l{ wl->mtx }; + for (Cell const &cell : wl->changes) + living.insert(cell); } - born.clear(); - - underpopulatedMutex.unlock(); - overpopulatedMutex.unlock(); - bornMutex.unlock(); PopulateChanges(); } @@ -236,34 +258,32 @@ 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); + for (std::shared_ptr wl : overpopulated) { + std::scoped_lock l{ wl->mtx }; + SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255); + for (Cell const &cell : wl->changes) { + 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); + for (std::shared_ptr wl : underpopulated) { + std::scoped_lock l{ wl->mtx }; + SDL_SetRenderDrawColor(renderer, 255, 0, 255, 255); + for (Cell const &cell : wl->changes) { + 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(); - - if (logGenerationTime) { - SDL_Log("End Generation %lf", double(SDL_GetTicksNS() - generationStartTime) * 0.0000000001); - logGenerationTime = false; + for (std::shared_ptr wl : born) { + std::scoped_lock l{ wl->mtx }; + SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); + for (Cell const &cell : wl->changes) { + cellRect.x = (viewOffset.x + cell.x) * cellRect.w; + cellRect.y = (viewOffset.y + cell.y) * cellRect.h; + SDL_RenderRect(renderer, &cellRect); + } } }