From 15f2f54a14dbe46007b247367f053ef97b68a013 Mon Sep 17 00:00:00 2001 From: JuiceyDev Date: Fri, 3 Apr 2026 21:49:34 +0200 Subject: [PATCH] fix: huge performance fix with chunk storage. insane code by 4j devs, this is objectively the WORST code i've seen this year, not even yanderedev can compare. --- .../chunk/storage/McRegionChunkStorage.cpp | 128 ++++++------------ .../chunk/storage/McRegionChunkStorage.h | 3 + 2 files changed, 44 insertions(+), 87 deletions(-) diff --git a/targets/minecraft/world/level/chunk/storage/McRegionChunkStorage.cpp b/targets/minecraft/world/level/chunk/storage/McRegionChunkStorage.cpp index 3406282aa..d15a07e37 100644 --- a/targets/minecraft/world/level/chunk/storage/McRegionChunkStorage.cpp +++ b/targets/minecraft/world/level/chunk/storage/McRegionChunkStorage.cpp @@ -35,6 +35,8 @@ class DataInput; std::mutex McRegionChunkStorage::cs_memory; +std::condition_variable McRegionChunkStorage::s_queueCondition; +std::condition_variable McRegionChunkStorage::s_waitCondition; std::deque McRegionChunkStorage::s_chunkDataQueue; int McRegionChunkStorage::s_runningThreadCount = 0; @@ -204,14 +206,15 @@ void McRegionChunkStorage::save(Level* level, LevelChunk* levelChunk) { DataOutputStream* output = RegionFileCache::getChunkDataOutputStream( m_saveFile, m_prefix, levelChunk->x, levelChunk->z); - if (m_saveFile->getOriginalSaveVersion() >= - SAVE_FILE_VERSION_COMPRESSED_CHUNK_STORAGE) { - OldChunkStorage::save(levelChunk, level, output); + if (m_saveFile->getOriginalSaveVersion() >= SAVE_FILE_VERSION_COMPRESSED_CHUNK_STORAGE) { + OldChunkStorage::save(levelChunk, level, output); - { - std::lock_guard lock(cs_memory); - s_chunkDataQueue.push_back(output); - } + { + std::lock_guard lock(cs_memory); + s_chunkDataQueue.push_back(output); + } + // 4jcraft: WAKE UP, WAKE THE FUCK.. UP + s_queueCondition.notify_one(); } else { CompoundTag* tag; @@ -337,52 +340,38 @@ void McRegionChunkStorage::staticCtor() { } } +// 4jcraft: removed the wasting 100ms chunk loading part. int McRegionChunkStorage::runSaveThreadProc(void* lpParam) { Compression::CreateNewThreadStorage(); bool running = true; - size_t lastQueueSize = 0; - DataOutputStream* dos = nullptr; while (running) { { - std::unique_lock lock(cs_memory, std::try_to_lock); - if (lock.owns_lock()) { - lastQueueSize = s_chunkDataQueue.size(); - if (lastQueueSize > 0) { - dos = s_chunkDataQueue.front(); - s_chunkDataQueue.pop_front(); - } - s_runningThreadCount++; - lock.unlock(); + std::unique_lock lock(cs_memory); + s_queueCondition.wait(lock, [] { return !s_chunkDataQueue.empty(); }); + dos = s_chunkDataQueue.front(); + s_chunkDataQueue.pop_front(); + s_runningThreadCount++; + } // Unlock so the main thread can keep working - if (dos) { - // app.DebugPrintf("Compressing chunk data (%d left)\n", - // lastQueueSize - 1); - dos->close(); - dos->deleteChildStream(); - } - delete dos; - dos = nullptr; - - { - std::lock_guard lock2(cs_memory); - s_runningThreadCount--; - } - } + if (dos) { + dos->close(); + dos->deleteChildStream(); + delete dos; + dos = nullptr; } - // If there was more than one thing in the queue last time we checked, - // then we want to spin round again soon Otherwise wait a bit longer - if ((lastQueueSize - 1) > 0) - std::this_thread::sleep_for( - std::chrono::milliseconds(1)); // Sleep 1 to yield - else - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + { + std::lock_guard lock(cs_memory); + s_runningThreadCount--; + } + + // Tell the main thread we finished a chunk + s_waitCondition.notify_all(); } Compression::ReleaseThreadStorage(); - return 0; } @@ -391,39 +380,13 @@ void McRegionChunkStorage::WaitForAll() { WaitForAllSaves(); } void McRegionChunkStorage::WaitIfTooManyQueuedChunks() { WaitForSaves(); } // Static +// 4jcraft: Better waiting system void McRegionChunkStorage::WaitForAllSaves() { - // Wait for there to be no more tasks to be processed... - size_t queueSize; - { - std::lock_guard lock(cs_memory); - queueSize = s_chunkDataQueue.size(); - } - - while (queueSize > 0) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - - { - std::lock_guard lock(cs_memory); - queueSize = s_chunkDataQueue.size(); - } - } - - // And then wait for there to be no running threads that are processing - // these tasks - int runningThreadCount; - { - std::lock_guard lock(cs_memory); - runningThreadCount = s_runningThreadCount; - } - - while (runningThreadCount > 0) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - - { - std::lock_guard lock(cs_memory); - runningThreadCount = s_runningThreadCount; - } - } + std::unique_lock lock(cs_memory); + // Pause the main thread instantly until queue is 0 AND workers are done + s_waitCondition.wait(lock, [] { + return s_chunkDataQueue.empty() && s_runningThreadCount == 0; + }); } // Static @@ -431,21 +394,12 @@ void McRegionChunkStorage::WaitForSaves() { static const int MAX_QUEUE_SIZE = 12; static const int DESIRED_QUEUE_SIZE = 6; - // Wait for the queue to reduce to a level where we should add more elements - size_t queueSize; - { - std::lock_guard lock(cs_memory); - queueSize = s_chunkDataQueue.size(); - } - if (queueSize > MAX_QUEUE_SIZE) { - while (queueSize > DESIRED_QUEUE_SIZE) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - - { - std::lock_guard lock(cs_memory); - queueSize = s_chunkDataQueue.size(); - } - } + std::unique_lock lock(cs_memory); + if (s_chunkDataQueue.size() > MAX_QUEUE_SIZE) { + // Pause until the queue drains down to the desired size + s_waitCondition.wait(lock, [] { + return s_chunkDataQueue.size() <= DESIRED_QUEUE_SIZE; + }); } } diff --git a/targets/minecraft/world/level/chunk/storage/McRegionChunkStorage.h b/targets/minecraft/world/level/chunk/storage/McRegionChunkStorage.h index 60c13c4e1..7cfec8d33 100644 --- a/targets/minecraft/world/level/chunk/storage/McRegionChunkStorage.h +++ b/targets/minecraft/world/level/chunk/storage/McRegionChunkStorage.h @@ -14,6 +14,7 @@ #include "RegionFileCache.h" #include "minecraft/world/level/chunk/LevelChunk.h" #include "nbt/NbtIo.h" +#include // 4jcraft: im pretty sure there's a better alternative to this. class ConsoleSaveFile; class C4JThread; @@ -38,6 +39,8 @@ public: ~McRegionChunkStorage(); static void staticCtor(); + static std::condition_variable s_queueCondition; + static std::condition_variable s_waitCondition; virtual LevelChunk* load(Level* level, int x, int z); virtual void save(Level* level, LevelChunk* levelChunk); virtual void saveEntities(Level* level, LevelChunk* levelChunk);