From b2187bf0ad6144ea965efe65d1c866fd934c6321 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Wed, 5 Aug 2026 20:26:57 +0800 Subject: [PATCH] perf(server): parallelize entity update loop Use parallel_do to process entities concurrently via the compute pool. Switch get_all_session to a thread-safe tbb::concurrent_vector and make ChunkEntity's ref_count atomic to avoid data races. Also remove the debug pig spawn from world init. --- .../Cubed/gameplay/server_entity_manager.hpp | 6 ++- include/Cubed/gameplay/server_world.hpp | 25 +++++++++++- src/gameplay/server_entity_manager.cpp | 38 +++++++++++++------ src/gameplay/server_world.cpp | 8 ++-- 4 files changed, 57 insertions(+), 20 deletions(-) diff --git a/include/Cubed/gameplay/server_entity_manager.hpp b/include/Cubed/gameplay/server_entity_manager.hpp index 15010ba..41a8e4a 100644 --- a/include/Cubed/gameplay/server_entity_manager.hpp +++ b/include/Cubed/gameplay/server_entity_manager.hpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace Cubed { class ServerWorld; class Session; @@ -46,8 +47,9 @@ private: void send_all_entities(std::shared_ptr& session); void update_ai(entt::entity e); void update_move(entt::entity e); - void update_send(entt::entity e, - std::span> sessions); + void + update_send(entt::entity e, + tbb::concurrent_vector>& sessions); template EntityID create_entity_in_factory(Args&&... args) { auto entity = m_registry.create(); diff --git a/include/Cubed/gameplay/server_world.hpp b/include/Cubed/gameplay/server_world.hpp index 84b3263..5fa12c2 100644 --- a/include/Cubed/gameplay/server_world.hpp +++ b/include/Cubed/gameplay/server_world.hpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -92,7 +93,7 @@ public: int chunk_size() const; - std::vector> get_all_session() const; + tbb::concurrent_vector> get_all_session() const; uint32_t get_chunk_ref_count(const glm::vec3& pos) const; ServerEntityManager& entity_manager(); @@ -114,7 +115,27 @@ private: struct ChunkEntity { ChunkState state; std::shared_ptr chunk; - uint32_t ref_count = 0; + std::atomic ref_count = 0; + ChunkEntity() = default; + ChunkEntity(ChunkState s, std::shared_ptr c = {}) + : state(s), chunk(std::move(c)) {} + + ChunkEntity& operator=(ChunkEntity&& o) noexcept { + if (this == &o) { + return *this; + } + + state = std::exchange(o.state, ServerWorld::ChunkState::NONE); + chunk = std::move(o.chunk); + ref_count = o.ref_count.exchange(0); + return *this; + } + + ChunkEntity(ChunkEntity&& o) noexcept + : state(std::exchange(o.state, ServerWorld::ChunkState::NONE)), + chunk(std::move(o.chunk)), ref_count(o.ref_count.exchange(0)) {} + ChunkEntity(const ChunkEntity&) = delete; + ChunkEntity& operator=(const ChunkEntity&) = delete; }; enum class ChunkLoadStyle { RANDOM, CENTER }; diff --git a/src/gameplay/server_entity_manager.cpp b/src/gameplay/server_entity_manager.cpp index 9b3fcc9..604625b 100644 --- a/src/gameplay/server_entity_manager.cpp +++ b/src/gameplay/server_entity_manager.cpp @@ -33,18 +33,31 @@ void ServerEntityManager::update() { handle_task(); auto view = m_registry.view(); - auto sessions = m_world.get_all_session(); - for (auto e : view) { - const auto& c = view.get(e); - if (!m_world.get_chunk_ref_count(c.transform.position.value)) { - const auto& entity = m_registry.get(e); - destory(entity.id); - continue; - } - update_ai(e); - update_move(e); - update_send(e, sessions); + + auto pool = m_world.get_compute_pool(); + if (!pool) { + return; } + auto sessions = m_world.get_all_session(); + std::vector entities; + for (auto e : view) { + entities.push_back(e); + } + // parallel block touches disjoint entities only; + // structural registry changes stay on the server thread via m_tasks. + parallel_do( + *pool, entities.begin(), entities.end(), pool->thread_sum(), + [this, &sessions](entt::entity e) { + const auto& c = m_registry.get(e); + if (!m_world.get_chunk_ref_count(c.transform.position.value)) { + const auto& entity = m_registry.get(e); + destory(entity.id); + return; + } + update_ai(e); + update_move(e); + update_send(e, sessions); + }); } void ServerEntityManager::update_ai(entt::entity e) { @@ -59,7 +72,8 @@ void ServerEntityManager::update_move(entt::entity e) { } void ServerEntityManager::update_send( - entt::entity e, std::span> sessions) { + entt::entity e, + tbb::concurrent_vector>& sessions) { if (!m_registry.all_of(e)) { return; diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index 90778a1..48e10f8 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -182,7 +182,6 @@ void ServerWorld::send_chunk(int task_id, const std::string& uuid, void ServerWorld::init_world(RunMode mode) { m_runmode = mode; m_entity_manager.init(); - m_entity_manager.add_entity("cubed:pig", {0, 225, 0}); register_timer("player disconnect", 5, [this]() { std::vector disconnect; { @@ -319,7 +318,7 @@ void ServerWorld::sync_and_collect_missing_chunks( chunk_acc acc; if (m_chunks.insert(acc, pos)) { need_gen_chunks_pos.push_back(pos); - acc->second = ChunkEntity{ChunkState::GENERATING, nullptr, 0}; + acc->second = ChunkEntity{ChunkState::GENERATING}; } } } @@ -1028,9 +1027,10 @@ void ServerWorld::set_chunk_load_style(int id) { int ServerWorld::chunk_size() const { return m_chunks.size(); } -std::vector> ServerWorld::get_all_session() const { +tbb::concurrent_vector> +ServerWorld::get_all_session() const { std::shared_lock lock(m_players_mutex); - std::vector> sessions; + tbb::concurrent_vector> sessions; for (const auto& [_, player] : m_players) { sessions.emplace_back(player.get_session()); }