From c1e6bbddfee1b5a480907a19c393410f03bb117c Mon Sep 17 00:00:00 2001 From: K Date: Sun, 14 Jul 2024 16:56:58 +0200 Subject: [PATCH] Reduce size of Bonus struct from 320 bytes to 296 bytes. - Internal enums were resized to occupy single byte. - Duration bitmask uses 16 bit integer directly instead of std::bitset<11> which consumed 8 bytes. - Fields shuffled to minimise padding and keep the most useful data on first 2 cache lines. --- lib/bonuses/Bonus.cpp | 4 +-- lib/bonuses/Bonus.h | 41 +++++++++++++------------- lib/bonuses/BonusEnum.cpp | 7 +++-- lib/bonuses/BonusEnum.h | 38 ++++++++++++++---------- server/battles/BattleActionProcessor.h | 2 +- 5 files changed, 50 insertions(+), 42 deletions(-) diff --git a/lib/bonuses/Bonus.cpp b/lib/bonuses/Bonus.cpp index e82b6eb13..ed9d04cce 100644 --- a/lib/bonuses/Bonus.cpp +++ b/lib/bonuses/Bonus.cpp @@ -119,7 +119,7 @@ std::string Bonus::Description(std::optional customValue) const if(descriptionHelper.empty()) { // still no description - try to generate one based on duration - if ((duration & BonusDuration::ONE_BATTLE).any()) + if ((duration & BonusDuration::ONE_BATTLE) != 0) { if (val > 0) descriptionHelper.appendTextID("core.arraytxt.110"); //+%d Temporary until next battle" @@ -248,7 +248,7 @@ DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus) #define printField(field) out << "\t" #field ": " << (int)bonus.field << "\n" printField(val); out << "\tSubtype: " << bonus.subtype.toString() << "\n"; - printField(duration.to_ulong()); + printField(duration); printField(source); out << "\tSource ID: " << bonus.sid.toString() << "\n"; if(bonus.additionalInfo != CAddInfo::NONE) diff --git a/lib/bonuses/Bonus.h b/lib/bonuses/Bonus.h index a03e193b0..787f0cfa4 100644 --- a/lib/bonuses/Bonus.h +++ b/lib/bonuses/Bonus.h @@ -58,21 +58,22 @@ public: /// Struct for handling bonuses of several types. Can be transferred to any hero struct DLL_LINKAGE Bonus : public std::enable_shared_from_this, public Serializeable { - BonusDuration::Type duration = BonusDuration::PERMANENT; //uses BonusDuration values + BonusDuration::Type duration = BonusDuration::PERMANENT; //uses BonusDuration values - 2 bytes si16 turnsRemain = 0; //used if duration is N_TURNS, N_DAYS or ONE_WEEK - - BonusType type = BonusType::NONE; //uses BonusType values - says to what is this bonus - 1 byte - BonusSubtypeID subtype; - - BonusSource source = BonusSource::OTHER; //source type" uses BonusSource values - what gave that bonus - BonusSource targetSourceType = BonusSource::OTHER;//Bonuses of what origin this amplifies, uses BonusSource values. Needed for PERCENT_TO_TARGET_TYPE. si32 val = 0; + + BonusValueType valType = BonusValueType::ADDITIVE_VALUE; // 1 byte + BonusSource source = BonusSource::OTHER; //source type" uses BonusSource values - what gave that bonus - 1 byte + BonusSource targetSourceType = BonusSource::OTHER;//Bonuses of what origin this amplifies, uses BonusSource values. Needed for PERCENT_TO_TARGET_TYPE. - 1 byte + BonusType type = BonusType::NONE; //uses BonusType values - says to what is this bonus - 1 byte + BonusLimitEffect effectRange = BonusLimitEffect::NO_LIMIT; // 1 byte + // 3 bytes padding + + BonusSubtypeID subtype; BonusSourceID sid; //source id: id of object/artifact/spell - BonusValueType valType = BonusValueType::ADDITIVE_VALUE; std::string stacking; // bonuses with the same stacking value don't stack (e.g. Angel/Archangel morale bonus) CAddInfo additionalInfo; - BonusLimitEffect effectRange = BonusLimitEffect::NO_LIMIT; TLimiterPtr limiter; TPropagatorPtr propagator; @@ -128,57 +129,57 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this, public Se static bool NDays(const Bonus *hb) { auto set = hb->duration & BonusDuration::N_DAYS; - return set.any(); + return set != 0; } static bool NTurns(const Bonus *hb) { auto set = hb->duration & BonusDuration::N_TURNS; - return set.any(); + return set != 0; } static bool OneDay(const Bonus *hb) { auto set = hb->duration & BonusDuration::ONE_DAY; - return set.any(); + return set != 0; } static bool OneWeek(const Bonus *hb) { auto set = hb->duration & BonusDuration::ONE_WEEK; - return set.any(); + return set != 0; } static bool OneBattle(const Bonus *hb) { auto set = hb->duration & BonusDuration::ONE_BATTLE; - return set.any(); + return set != 0; } static bool Permanent(const Bonus *hb) { auto set = hb->duration & BonusDuration::PERMANENT; - return set.any(); + return set != 0; } static bool UntilGetsTurn(const Bonus *hb) { auto set = hb->duration & BonusDuration::STACK_GETS_TURN; - return set.any(); + return set != 0; } static bool UntilAttack(const Bonus *hb) { auto set = hb->duration & BonusDuration::UNTIL_ATTACK; - return set.any(); + return set != 0; } static bool UntilBeingAttacked(const Bonus *hb) { auto set = hb->duration & BonusDuration::UNTIL_BEING_ATTACKED; - return set.any(); + return set != 0; } static bool UntilCommanderKilled(const Bonus *hb) { auto set = hb->duration & BonusDuration::COMMANDER_KILLED; - return set.any(); + return set != 0; } static bool UntilOwnAttack(const Bonus *hb) { auto set = hb->duration & BonusDuration::UNTIL_OWN_ATTACK; - return set.any(); + return set != 0; } inline bool operator == (const BonusType & cf) const { diff --git a/lib/bonuses/BonusEnum.cpp b/lib/bonuses/BonusEnum.cpp index e48d15dcf..1408a4aee 100644 --- a/lib/bonuses/BonusEnum.cpp +++ b/lib/bonuses/BonusEnum.cpp @@ -60,10 +60,11 @@ namespace BonusDuration JsonNode toJson(const Type & duration) { std::vector durationNames; - for(auto durBit = 0; durBit < duration.size(); durBit++) + for(size_t durBit = 0; durBit < Size; durBit++) { - if(duration[durBit]) - durationNames.push_back(vstd::findKey(bonusDurationMap, duration & Type().set(durBit))); + Type value = duration & (1 << durBit); + if(value) + durationNames.push_back(vstd::findKey(bonusDurationMap, value)); } if(durationNames.size() == 1) { diff --git a/lib/bonuses/BonusEnum.h b/lib/bonuses/BonusEnum.h index a55b0b06b..1edbdd4ee 100644 --- a/lib/bonuses/BonusEnum.h +++ b/lib/bonuses/BonusEnum.h @@ -212,7 +212,7 @@ class JsonNode; BONUS_VALUE(INDEPENDENT_MIN) //used for SECONDARY_SKILL_PREMY bonus -enum class BonusType +enum class BonusType : uint8_t { #define BONUS_NAME(x) x, BONUS_LIST @@ -220,21 +220,27 @@ enum class BonusType }; namespace BonusDuration //when bonus is automatically removed { - using Type = std::bitset<11>; + // We use uint16_t directly because std::bitset<11> eats whole 8 byte word. + using Type = uint16_t; + constexpr static size_t Size = 11; + + enum BonusDuration : Type { + PERMANENT = 1 << 0, + ONE_BATTLE = 1 << 1, //at the end of battle + ONE_DAY = 1 << 2, //at the end of day + ONE_WEEK = 1 << 3, //at the end of week (bonus lasts till the end of week, thats NOT 7 days + N_TURNS = 1 << 4, //used during battles, after battle bonus is always removed + N_DAYS = 1 << 5, + UNTIL_BEING_ATTACKED = 1 << 6, /*removed after attack and counterattacks are performed*/ + UNTIL_ATTACK = 1 << 7, /*removed after attack and counterattacks are performed*/ + STACK_GETS_TURN = 1 << 8, /*removed when stack gets its turn - used for defensive stance*/ + COMMANDER_KILLED = 1 << 9, + UNTIL_OWN_ATTACK = 1 << 10 /*removed after attack is performed (not counterattack)*/, + }; + extern JsonNode toJson(const Type & duration); - constexpr Type PERMANENT = 1 << 0; - constexpr Type ONE_BATTLE = 1 << 1; //at the end of battle - constexpr Type ONE_DAY = 1 << 2; //at the end of day - constexpr Type ONE_WEEK = 1 << 3; //at the end of week (bonus lasts till the end of week, thats NOT 7 days - constexpr Type N_TURNS = 1 << 4; //used during battles, after battle bonus is always removed - constexpr Type N_DAYS = 1 << 5; - constexpr Type UNTIL_BEING_ATTACKED = 1 << 6; /*removed after attack and counterattacks are performed*/ - constexpr Type UNTIL_ATTACK = 1 << 7; /*removed after attack and counterattacks are performed*/ - constexpr Type STACK_GETS_TURN = 1 << 8; /*removed when stack gets its turn - used for defensive stance*/ - constexpr Type COMMANDER_KILLED = 1 << 9; - constexpr Type UNTIL_OWN_ATTACK = 1 << 10; /*removed after attack is performed (not counterattack)*/; }; -enum class BonusSource +enum class BonusSource : uint8_t { #define BONUS_SOURCE(x) x, BONUS_SOURCE_LIST @@ -242,13 +248,13 @@ enum class BonusSource NUM_BONUS_SOURCE /*This is a dummy value, which will be always last*/ }; -enum class BonusLimitEffect +enum class BonusLimitEffect : uint8_t { NO_LIMIT = 0, ONLY_DISTANCE_FIGHT=1, ONLY_MELEE_FIGHT, //used to mark bonuses for attack/defense primary skills from spells like Precision (distance only) }; -enum class BonusValueType +enum class BonusValueType : uint8_t { #define BONUS_VALUE(x) x, BONUS_VALUE_LIST diff --git a/server/battles/BattleActionProcessor.h b/server/battles/BattleActionProcessor.h index e7eaa7e78..72744dc96 100644 --- a/server/battles/BattleActionProcessor.h +++ b/server/battles/BattleActionProcessor.h @@ -19,7 +19,7 @@ class CBattleInfoCallback; struct BattleHex; class CStack; class PlayerColor; -enum class BonusType; +enum class BonusType : uint8_t; namespace battle {