From 73aab0b5ba2c7aa198161fe195ee6ad00c7b86c9 Mon Sep 17 00:00:00 2001 From: Gent Semaj Date: Mon, 28 Oct 2024 12:56:20 -0700 Subject: [PATCH] Optimize performance Introduces a generic, fixed-size `Bucket` type in place of the inner vector. This does add size generics but I think it's a very good tradeoff considering how performance-sensitive this codepath is. --- Makefile | 1 + src/Bucket.hpp | 39 ++++++++++++++++ src/Chunking.cpp | 114 +++++++++++++++++++++++------------------------ 3 files changed, 97 insertions(+), 57 deletions(-) create mode 100644 src/Bucket.hpp diff --git a/Makefile b/Makefile index f94e9af..b6541dc 100644 --- a/Makefile +++ b/Makefile @@ -115,6 +115,7 @@ CXXHDR=\ src/settings.hpp\ src/Transport.hpp\ src/TableData.hpp\ + src/Bucket.hpp\ src/Chunking.hpp\ src/Buddies.hpp\ src/Groups.hpp\ diff --git a/src/Bucket.hpp b/src/Bucket.hpp new file mode 100644 index 0000000..fc3f52b --- /dev/null +++ b/src/Bucket.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include +#include + +template +class Bucket { + std::array buf; + size_t sz; +public: + Bucket() { + sz = 0; + } + + void add(const T& item) { + if (sz < N) { + buf[sz++] = item; + } + } + + std::optional get(size_t idx) const { + if (idx < sz) { + return buf[idx]; + } + return std::nullopt; + } + + size_t size() const { + return sz; + } + + bool isFull() const { + return sz == N; + } + + void clear() { + sz = 0; + } +}; diff --git a/src/Chunking.cpp b/src/Chunking.cpp index 06ba53f..fec1cdc 100644 --- a/src/Chunking.cpp +++ b/src/Chunking.cpp @@ -3,6 +3,7 @@ #include "Player.hpp" #include "MobAI.hpp" #include "NPCManager.hpp" +#include "Bucket.hpp" #include @@ -12,11 +13,11 @@ using namespace Chunking; * The initial chunkPos value before a player is placed into the world. */ const ChunkPos Chunking::INVALID_CHUNK = {}; -constexpr size_t MAX_PC_PER_AROUND = (CN_PACKET_BUFFER_SIZE - 4) / sizeof(sPCAppearanceData); -constexpr size_t MAX_NPC_PER_AROUND = (CN_PACKET_BUFFER_SIZE - 4) / sizeof(sNPCAppearanceData); -constexpr size_t MAX_SHINY_PER_AROUND = (CN_PACKET_BUFFER_SIZE - 4) / sizeof(sShinyAppearanceData); -constexpr size_t MAX_TRANSPORTATION_PER_AROUND = (CN_PACKET_BUFFER_SIZE - 4) / sizeof(sTransportationAppearanceData); -constexpr size_t MAX_IDS_PER_AROUND_DEL = (CN_PACKET_BUFFER_SIZE - 4) / sizeof(int32_t); +constexpr size_t MAX_PC_PER_AROUND = (CN_PACKET_BUFFER_SIZE - sizeof(int32_t)) / sizeof(sPCAppearanceData); +constexpr size_t MAX_NPC_PER_AROUND = (CN_PACKET_BUFFER_SIZE - sizeof(int32_t)) / sizeof(sNPCAppearanceData); +constexpr size_t MAX_SHINY_PER_AROUND = (CN_PACKET_BUFFER_SIZE - sizeof(int32_t)) / sizeof(sShinyAppearanceData); +constexpr size_t MAX_TRANSPORTATION_PER_AROUND = (CN_PACKET_BUFFER_SIZE - sizeof(int32_t)) / sizeof(sTransportationAppearanceData); +constexpr size_t MAX_IDS_PER_AROUND_DEL = (CN_PACKET_BUFFER_SIZE - sizeof(int32_t)) / sizeof(int32_t); std::map Chunking::chunks; @@ -81,36 +82,32 @@ void Chunking::untrackEntity(ChunkPos chunkPos, const EntityRef ref) { deleteChunk(chunkPos); } -template -static void sendAroundPacket(const EntityRef recipient, std::vector>& slices, size_t maxCnt, uint32_t packetId) { +template +static void sendAroundPacket(const EntityRef recipient, std::vector>& buckets, uint32_t packetId) { assert(recipient.kind == EntityKind::PLAYER); uint8_t pktBuf[CN_PACKET_BUFFER_SIZE]; - for (const auto& slice : slices) { + for (const auto& bucket : buckets) { memset(pktBuf, 0, CN_PACKET_BUFFER_SIZE); - int count = slice.size(); - assert(count <= maxCnt); + int count = bucket.size(); *((int32_t*)pktBuf) = count; T* data = (T*)(pktBuf + sizeof(int32_t)); for (size_t i = 0; i < count; i++) { - data[i] = slice[i]; + data[i] = bucket.get(i).value(); } recipient.sock->sendPacket(pktBuf, packetId, sizeof(int32_t) + (count * sizeof(T))); } } -static void sendAroundDelPacket(const EntityRef recipient, std::vector>& slices, bool isTransportation, uint32_t packetId) { +template +static void sendAroundDelPacket(const EntityRef recipient, std::vector>& buckets, bool isTransportation, uint32_t packetId) { assert(recipient.kind == EntityKind::PLAYER); - size_t maxCnt = MAX_IDS_PER_AROUND_DEL; - if (isTransportation) - maxCnt -= 1; // account for eTT. sad. - uint8_t pktBuf[CN_PACKET_BUFFER_SIZE]; - for (const auto& slice : slices) { + for (const auto& bucket : buckets) { memset(pktBuf, 0, CN_PACKET_BUFFER_SIZE); - int count = slice.size(); - assert(count <= maxCnt); + int count = bucket.size(); + assert(count <= N); size_t baseSize; if (isTransportation) { @@ -125,39 +122,42 @@ static void sendAroundDelPacket(const EntityRef recipient, std::vectorsendPacket(pktBuf, packetId, baseSize + (count * sizeof(int32_t))); } } -template -static void bufferAppearanceData(std::vector>& slices, const T& data, size_t maxCnt) { - if (slices.empty()) - slices.push_back(std::vector()); - std::vector& slice = slices[slices.size() - 1]; - slice.push_back(data); - if (slice.size() == maxCnt) - slices.push_back(std::vector()); +template +static void bufferAppearanceData(std::vector>& buckets, const T& data) { + if (buckets.empty()) + buckets.push_back(Bucket()); + Bucket& bucket = buckets[buckets.size() - 1]; + assert(!bucket.isFull()); + bucket.add(data); + if (bucket.isFull()) + buckets.push_back(Bucket()); } -static void bufferIdForDisappearance(std::vector>& slices, int32_t id, size_t maxCnt) { - if (slices.empty()) - slices.push_back(std::vector()); - std::vector& slice = slices[slices.size() - 1]; - slice.push_back(id); - if (slice.size() == maxCnt) - slices.push_back(std::vector()); +template +static void bufferIdForDisappearance(std::vector>& buckets, int32_t id) { + if (buckets.empty()) + buckets.push_back(Bucket()); + Bucket& bucket = buckets[buckets.size() - 1]; + assert(!bucket.isFull()); + bucket.add(id); + if (bucket.isFull()) + buckets.push_back(Bucket()); } void Chunking::addEntityToChunks(std::set chnks, const EntityRef ref) { Entity *ent = ref.getEntity(); bool alive = ent->isExtant(); - std::vector> pcAppearances; - std::vector> npcAppearances; - std::vector> shinyAppearances; - std::vector> transportationAppearances; + std::vector> pcAppearances; + std::vector> npcAppearances; + std::vector> shinyAppearances; + std::vector> transportationAppearances; for (Chunk *chunk : chnks) { for (const EntityRef otherRef : chunk->entities) { // skip oneself @@ -181,27 +181,27 @@ void Chunking::addEntityToChunks(std::set chnks, const EntityRef ref) { { case EntityKind::PLAYER: pcData = dynamic_cast(other)->getAppearanceData(); - bufferAppearanceData(pcAppearances, pcData, MAX_PC_PER_AROUND); + bufferAppearanceData(pcAppearances, pcData); break; case EntityKind::SIMPLE_NPC: npcData = dynamic_cast(other)->getAppearanceData(); - bufferAppearanceData(npcAppearances, npcData, MAX_NPC_PER_AROUND); + bufferAppearanceData(npcAppearances, npcData); break; case EntityKind::COMBAT_NPC: npcData = dynamic_cast(other)->getAppearanceData(); - bufferAppearanceData(npcAppearances, npcData, MAX_NPC_PER_AROUND); + bufferAppearanceData(npcAppearances, npcData); break; case EntityKind::MOB: npcData = dynamic_cast(other)->getAppearanceData(); - bufferAppearanceData(npcAppearances, npcData, MAX_NPC_PER_AROUND); + bufferAppearanceData(npcAppearances, npcData); break; case EntityKind::EGG: eggData = dynamic_cast(other)->getShinyAppearanceData(); - bufferAppearanceData(shinyAppearances, eggData, MAX_SHINY_PER_AROUND); + bufferAppearanceData(shinyAppearances, eggData); break; case EntityKind::BUS: busData = dynamic_cast(other)->getTransportationAppearanceData(); - bufferAppearanceData(transportationAppearances, busData, MAX_TRANSPORTATION_PER_AROUND); + bufferAppearanceData(transportationAppearances, busData); break; default: break; @@ -220,23 +220,23 @@ void Chunking::addEntityToChunks(std::set chnks, const EntityRef ref) { return; // nothing to send if (!pcAppearances.empty()) - sendAroundPacket(ref, pcAppearances, MAX_PC_PER_AROUND, P_FE2CL_PC_AROUND); + sendAroundPacket(ref, pcAppearances, P_FE2CL_PC_AROUND); if (!npcAppearances.empty()) - sendAroundPacket(ref, npcAppearances, MAX_NPC_PER_AROUND, P_FE2CL_NPC_AROUND); + sendAroundPacket(ref, npcAppearances, P_FE2CL_NPC_AROUND); if (!shinyAppearances.empty()) - sendAroundPacket(ref, shinyAppearances, MAX_SHINY_PER_AROUND, P_FE2CL_SHINY_AROUND); + sendAroundPacket(ref, shinyAppearances, P_FE2CL_SHINY_AROUND); if (!transportationAppearances.empty()) - sendAroundPacket(ref, transportationAppearances, MAX_TRANSPORTATION_PER_AROUND, P_FE2CL_TRANSPORTATION_AROUND); + sendAroundPacket(ref, transportationAppearances, P_FE2CL_TRANSPORTATION_AROUND); } void Chunking::removeEntityFromChunks(std::set chnks, const EntityRef ref) { Entity *ent = ref.getEntity(); bool alive = ent->isExtant(); - std::vector> pcDisappearances; - std::vector> npcDisappearances; - std::vector> shinyDisappearances; - std::vector> transportationDisappearances; + std::vector> pcDisappearances; + std::vector> npcDisappearances; + std::vector> shinyDisappearances; + std::vector> transportationDisappearances; for (Chunk *chunk : chnks) { for (const EntityRef otherRef : chunk->entities) { // skip oneself @@ -257,21 +257,21 @@ void Chunking::removeEntityFromChunks(std::set chnks, const EntityRef re { case EntityKind::PLAYER: id = dynamic_cast(other)->iID; - bufferIdForDisappearance(pcDisappearances, id, MAX_IDS_PER_AROUND_DEL); + bufferIdForDisappearance(pcDisappearances, id); break; case EntityKind::SIMPLE_NPC: case EntityKind::COMBAT_NPC: case EntityKind::MOB: id = dynamic_cast(other)->id; - bufferIdForDisappearance(npcDisappearances, id, MAX_IDS_PER_AROUND_DEL); + bufferIdForDisappearance(npcDisappearances, id); break; case EntityKind::EGG: id = dynamic_cast(other)->id; - bufferIdForDisappearance(shinyDisappearances, id, MAX_IDS_PER_AROUND_DEL); + bufferIdForDisappearance(shinyDisappearances, id); break; case EntityKind::BUS: id = dynamic_cast(other)->id; - bufferIdForDisappearance(transportationDisappearances, id, MAX_IDS_PER_AROUND_DEL - 1); + bufferIdForDisappearance(transportationDisappearances, id); break; default: break;