From 932ef9ace4d0172c0a78afdd694c27eb758efaf6 Mon Sep 17 00:00:00 2001 From: zhenyan121 <3367366583@qq.com> Date: Fri, 26 Jun 2026 21:52:57 +0800 Subject: [PATCH] perf(gameplay): optimize protobuf message allocation with Arena and limit thread pool size Switch to Arena allocation for protobuf messages to reduce heap allocations. Pass ChunkDataRsp by value to enable move semantics. Cap thread pool size to hardware concurrency with max 4 threads. --- include/Cubed/gameplay/client_world.hpp | 2 +- src/gameplay/client_world.cpp | 45 +++++++++------- src/gameplay/network_client.cpp | 36 +++++++------ src/gameplay/server_world.cpp | 70 ++++++++++++++----------- src/gameplay/session.cpp | 37 ++++++------- 5 files changed, 106 insertions(+), 84 deletions(-) diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index a691c88..8b744a3 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -61,7 +61,7 @@ public: const std::vector& render_snapshots() const; const std::vector& render_player_data() const; glm::vec3 sunlight_dir() const; - void receive_chunk(const ChunkDataRsp& data); + void receive_chunk(ChunkDataRsp data); void exit(); template void register_timer(std::string_view id, TickType threshold, Fn&& f) { diff --git a/src/gameplay/client_world.cpp b/src/gameplay/client_world.cpp index dd39f4f..8e07b6f 100644 --- a/src/gameplay/client_world.cpp +++ b/src/gameplay/client_world.cpp @@ -5,6 +5,7 @@ #include "Cubed/gameplay/packet.hpp" using namespace std::chrono; using namespace std::chrono_literals; +using namespace google::protobuf; namespace Cubed { namespace { @@ -170,14 +171,15 @@ void ClientWorld::push_delete_vao(GLuint vao) { void ClientWorld::report_block_change(const glm::ivec3& pos, unsigned id) const { - BlockChangeReq req; - req.set_uuid(m_player.get_uuid()); - req.set_block(id); - auto* p = req.mutable_pos(); + Arena arena; + auto* req = Arena::Create(&arena); + req->set_uuid(m_player.get_uuid()); + req->set_block(id); + auto* p = req->mutable_pos(); p->set_x(pos.x); p->set_y(pos.y); p->set_z(pos.z); - m_client->send(make_packet(req)); + m_client->send(make_packet(*req)); } void ClientWorld::receive_block_change(const BlockChangeRsp& rsp) { @@ -254,7 +256,11 @@ void ClientWorld::stop_client_thread() { } m_game_running = false; } -void ClientWorld::start_thread_pool() { change_pool_threads(1); } +void ClientWorld::start_thread_pool() { + int max_threads = std::thread::hardware_concurrency(); + int threads = std::min(max_threads, 4); + change_pool_threads(threads); +} void ClientWorld::stop_thread_pool() { auto pool_ptr = m_thread_pool.load(); if (pool_ptr) { @@ -301,14 +307,15 @@ void ClientWorld::report_player_pos() { if (!m_client) { return; } - PlayerPos pos; - pos.set_uuid(m_player.get_uuid()); + Arena arena; + auto* pos = Arena::Create(&arena); + pos->set_uuid(m_player.get_uuid()); glm::vec3 player_pos = m_player.get_player_pos(); - auto* v3 = pos.mutable_pos(); + auto* v3 = pos->mutable_pos(); v3->set_x(player_pos.x); v3->set_y(player_pos.y); v3->set_z(player_pos.z); - m_client->send(make_packet(pos)); + m_client->send(make_packet(*pos)); } void ClientWorld::request_chunk() { @@ -379,18 +386,19 @@ void ClientWorld::request_chunk() { } } auto uuid = m_player.get_uuid(); - ChunkDataReq req; + Arena arena; + auto* req = Arena::Create(&arena); for (const auto& pos : need_send_pos) { - req.set_uuid(uuid); - auto* p = req.mutable_pos(); + req->set_uuid(uuid); + auto* p = req->mutable_pos(); p->set_x(pos.x); p->set_z(pos.z); - m_client->send(make_packet(req)); + m_client->send(make_packet(*req)); } m_requesting_chunk = false; } -void ClientWorld::receive_chunk(const ChunkDataRsp& data) { +void ClientWorld::receive_chunk(ChunkDataRsp data) { { std::lock_guard lock(m_chunks_mutex); @@ -418,9 +426,10 @@ void ClientWorld::receive_chunk(const ChunkDataRsp& data) { } void ClientWorld::exit() { - LogoutReq req; - req.set_uuid(m_player.get_uuid()); - m_client->send(make_packet(req)); + Arena arena; + auto* req = Arena::Create(&arena); + req->set_uuid(m_player.get_uuid()); + m_client->send(make_packet(*req)); } void ClientWorld::update(float delta_time) { diff --git a/src/gameplay/network_client.cpp b/src/gameplay/network_client.cpp index c050ed0..b10f3bd 100644 --- a/src/gameplay/network_client.cpp +++ b/src/gameplay/network_client.cpp @@ -2,6 +2,7 @@ #include "Cubed/gameplay/client_world.hpp" #include "Cubed/tools/log.hpp" +using namespace google::protobuf; namespace Cubed { NetworkClient::NetworkClient(ClientWorld& world) : m_socket(m_io), m_strand(asio::make_strand(m_io)), m_world(world) {} @@ -62,13 +63,14 @@ asio::awaitable NetworkClient::read_loop() { } constexpr auto& to_num = std::to_underlying; + Arena arena; switch (header.cmd) { case to_num(PacketEnum::LOGIN_RSP): { - LoginRsp rsp; + auto* rsp = Arena::Create(&arena); Logger::info("Client: Receive Login rsp"); - if (decode_packet(rsp, body_data, header)) { - if (rsp.success()) { - m_world.start_client_thread(rsp.uuid()); + if (decode_packet(*rsp, body_data, header)) { + if (rsp->success()) { + m_world.start_client_thread(rsp->uuid()); } else { Logger::error("Connected Server Fail"); } @@ -79,32 +81,32 @@ asio::awaitable NetworkClient::read_loop() { Logger::info("Client: Receive Chunk Data rsp, size {}mb", body_data.size() / 1024.0f / 1024); if (decode_packet(rsp, body_data, header)) { - m_world.receive_chunk(rsp); + m_world.receive_chunk(std::move(rsp)); } } break; case to_num(PacketEnum::BLOCK_CHANGE_RSP): { - BlockChangeRsp rsp; + auto* rsp = Arena::Create(&arena); Logger::info("Client: Receive Block Change rsp"); - if (decode_packet(rsp, body_data, header)) { - m_world.receive_block_change(rsp); + if (decode_packet(*rsp, body_data, header)) { + m_world.receive_block_change(*rsp); } } break; case to_num(PacketEnum::UPDATE_TIME): { - UpdateTime rsp; - if (decode_packet(rsp, body_data, header)) { - m_world.receive_time(rsp); + auto* rsp = Arena::Create(&arena); + if (decode_packet(*rsp, body_data, header)) { + m_world.receive_time(*rsp); } } break; case to_num(PacketEnum::PLAYER_INFO_RSP): { - PlayerInfoRsp rsp; - if (decode_packet(rsp, body_data, header)) { - m_world.receive_other_player(rsp); + auto* rsp = Arena::Create(&arena); + if (decode_packet(*rsp, body_data, header)) { + m_world.receive_other_player(*rsp); } } break; case to_num(PacketEnum::LOGOUT_RSP): { - LogoutRsp rsp; - if (decode_packet(rsp, body_data, header)) { - m_world.receive_player_logout(rsp); + auto* rsp = Arena::Create(&arena); + if (decode_packet(*rsp, body_data, header)) { + m_world.receive_player_logout(*rsp); } } break; } diff --git a/src/gameplay/server_world.cpp b/src/gameplay/server_world.cpp index d286629..9b11da0 100644 --- a/src/gameplay/server_world.cpp +++ b/src/gameplay/server_world.cpp @@ -10,6 +10,7 @@ #include using namespace std::chrono; using namespace std::chrono_literals; +using namespace google::protobuf; namespace Cubed { ServerWorld::ServerWorld() {} @@ -36,12 +37,14 @@ void ServerWorld::wait_all_chunk_tasks() { } void ServerWorld::send_time() { - UpdateTime rsp; - rsp.set_day_tick(m_day_tick); - rsp.set_game_tick(m_game_ticks); + Arena arena; + auto* rsp = Arena::Create(&arena); + + rsp->set_day_tick(m_day_tick); + rsp->set_game_tick(m_game_ticks); for (auto& [uuid, player] : m_players) { - player.get_session()->send(make_packet(rsp)); + player.get_session()->send(make_packet(*rsp)); } } @@ -439,14 +442,15 @@ void ServerWorld::sync_player_pos(const std::string& uuid, float x, float y, } for (auto& session : other) { - PlayerInfoRsp rsp; - rsp.set_uuid(uuid); - rsp.set_name(name); - auto* pos = rsp.mutable_pos(); + Arena arena; + auto* rsp = Arena::Create(&arena); + rsp->set_uuid(uuid); + rsp->set_name(name); + auto* pos = rsp->mutable_pos(); pos->set_x(x); pos->set_y(y); pos->set_z(z); - session->send(make_packet(rsp)); + session->send(make_packet(*rsp)); } } @@ -461,10 +465,11 @@ void ServerWorld::handle_player_login(const std::string& name, std::forward_as_tuple(name, uuid, *this, session, m_game_ticks)); } m_uuid_to_name.emplace(uuid, name); - LoginRsp rsp; - rsp.set_success(true); - rsp.set_uuid(uuid); - session->send(make_packet(rsp)); + Arena arena; + auto* rsp = Arena::Create(&arena); + rsp->set_success(true); + rsp->set_uuid(uuid); + session->send(make_packet(*rsp)); } void ServerWorld::handle_player_exit(const std::string& uuid) { @@ -491,9 +496,10 @@ void ServerWorld::handle_player_exit(const std::string& uuid) { } for (auto& s : sessions) { - LogoutRsp rsp; - rsp.set_uuid(uuid); - s->send(make_packet(rsp)); + Arena arena; + auto* rsp = Arena::Create(&arena); + rsp->set_uuid(uuid); + s->send(make_packet(*rsp)); } } @@ -508,8 +514,10 @@ glm::vec3 ServerWorld::get_player_pos(const std::string& uuid) const { } void ServerWorld::handle_chunk_req(const std::string& uuid, ChunkPos pos) { - ChunkDataRsp rsq; - auto* rsq_pos = rsq.mutable_pos(); + + Arena arean; + ChunkDataRsp* rsp = Arena::Create(&arean); + auto* rsq_pos = rsp->mutable_pos(); rsq_pos->set_x(pos.x); rsq_pos->set_z(pos.z); { @@ -518,9 +526,9 @@ void ServerWorld::handle_chunk_req(const std::string& uuid, ChunkPos pos) { if (it == m_chunks.end()) { return; } - rsq.set_chunk_seed(it->second.seed()); - rsq.set_biome_type(std::to_underlying(it->second.biome())); - auto* blocks = rsq.mutable_chunk_blocks(); + rsp->set_chunk_seed(it->second.seed()); + rsp->set_biome_type(std::to_underlying(it->second.biome())); + auto* blocks = rsp->mutable_chunk_blocks(); auto& chunk_blocks = it->second.get_chunk_blocks(); blocks->Assign(chunk_blocks.begin(), chunk_blocks.end()); auto& neighbor_blocks = it->second.get_neightbor_blocks(); @@ -534,10 +542,10 @@ void ServerWorld::handle_chunk_req(const std::string& uuid, ChunkPos pos) { } nb->Assign(blocks->begin(), blocks->end()); }; - auto* nb1 = rsq.mutable_neighbor_blocks_1(); - auto* nb2 = rsq.mutable_neighbor_blocks_2(); - auto* nb3 = rsq.mutable_neighbor_blocks_3(); - auto* nb4 = rsq.mutable_neighbor_blocks_4(); + auto* nb1 = rsp->mutable_neighbor_blocks_1(); + auto* nb2 = rsp->mutable_neighbor_blocks_2(); + auto* nb3 = rsp->mutable_neighbor_blocks_3(); + auto* nb4 = rsp->mutable_neighbor_blocks_4(); assign(nb1, neighbor_blocks[0]); assign(nb2, neighbor_blocks[1]); assign(nb3, neighbor_blocks[2]); @@ -557,7 +565,7 @@ void ServerWorld::handle_chunk_req(const std::string& uuid, ChunkPos pos) { return; } - s->send(make_packet(rsq)); + s->send(make_packet(*rsp)); } void ServerWorld::handle_block_change(const BlockChangeReq& req) { @@ -567,18 +575,20 @@ void ServerWorld::handle_block_change(const BlockChangeReq& req) { if (!set_block(glm::ivec3(x, y, z), req.block())) { return; } - BlockChangeRsp rsp; - auto* pos = rsp.mutable_pos(); + + Arena arena; + BlockChangeRsp* rsp = Arena::Create(&arena); + auto* pos = rsp->mutable_pos(); pos->set_x(x); pos->set_y(y); pos->set_z(z); - rsp.set_block(req.block()); + rsp->set_block(req.block()); { std::shared_lock lock(m_player_mutex); for (auto& [uuid, player] : m_players) { auto session = player.get_session(); if (session) { - session->send(make_packet(rsp)); + session->send(make_packet(*rsp)); } } } diff --git a/src/gameplay/session.cpp b/src/gameplay/session.cpp index cdf3a22..aef3db9 100644 --- a/src/gameplay/session.cpp +++ b/src/gameplay/session.cpp @@ -4,7 +4,7 @@ #include "Cubed/tools/log.hpp" #include "Cubed/tools/uuid.hpp" using asio::ip::tcp; - +using namespace google::protobuf; namespace Cubed { Session::Session(tcp::socket socket, ServerWorld& server_world, asio::io_context& io) @@ -55,41 +55,42 @@ asio::awaitable Session::read_loop() { } constexpr auto& to_num = std::to_underlying; auto cmd_id = header.cmd; + Arena arena; if (cmd_id == to_num(PacketEnum::LOGIN_REQ)) { - LoginReq req; + auto* req = Arena::Create(&arena); Logger::info("Session: Receive Login req"); - if (decode_packet(req, body_data, header)) { - m_server_world.handle_player_login(req.name(), + if (decode_packet(*req, body_data, header)) { + m_server_world.handle_player_login(req->name(), shared_from_this()); } } if (cmd_id == to_num(PacketEnum::PLAYER_POS)) { - PlayerPos pos; - if (decode_packet(pos, body_data, header)) { - m_server_world.sync_player_pos(pos.uuid(), pos.pos().x(), - pos.pos().y(), - pos.pos().z()); + auto* pos = Arena::Create(&arena); + if (decode_packet(*pos, body_data, header)) { + m_server_world.sync_player_pos(pos->uuid(), pos->pos().x(), + pos->pos().y(), + pos->pos().z()); } } if (cmd_id == to_num(PacketEnum::CHUNK_DATA_REQ)) { - ChunkDataReq req; + auto* req = Arena::Create(&arena); // Logger::info("Session: Receive Chunk Data req"); - if (decode_packet(req, body_data, header)) { + if (decode_packet(*req, body_data, header)) { m_server_world.handle_chunk_req( - req.uuid(), ChunkPos(req.pos().x(), req.pos().z())); + req->uuid(), ChunkPos(req->pos().x(), req->pos().z())); } } if (cmd_id == to_num(PacketEnum::BLOCK_CHANGE_REQ)) { - BlockChangeReq req; + auto* req = Arena::Create(&arena); Logger::info("Session: Receive Block Change req"); - if (decode_packet(req, body_data, header)) { - m_server_world.handle_block_change(req); + if (decode_packet(*req, body_data, header)) { + m_server_world.handle_block_change(*req); } } if (cmd_id == to_num(PacketEnum::LOGOUT_REQ)) { - LogoutReq req; - if (decode_packet(req, body_data, header)) { - m_server_world.handle_player_exit(req.uuid()); + auto* req = Arena::Create(&arena); + if (decode_packet(*req, body_data, header)) { + m_server_world.handle_player_exit(req->uuid()); } } }