浏览代码

Caching of bonuses that are requested with different durations

Ivan Savenko 10 月之前
父节点
当前提交
05397e2aaf
共有 5 个文件被更改,包括 108 次插入7 次删除
  1. 2 2
      include/vcmi/Creature.h
  2. 15 4
      lib/battle/CUnitState.cpp
  3. 5 0
      lib/battle/CUnitState.h
  4. 56 0
      lib/bonuses/BonusCache.cpp
  5. 30 1
      lib/bonuses/BonusCache.h

+ 2 - 2
include/vcmi/Creature.h

@@ -23,8 +23,8 @@ class DLL_LINKAGE ACreature: public AFactionMember
 {
 public:
 	bool isLiving() const; //non-undead, non-non living or alive
-	ui32 getMovementRange(int turn) const; //get speed (in moving tiles) of creature with all modificators
-	ui32 getMovementRange() const; //get speed (in moving tiles) of creature with all modificators
+	virtual ui32 getMovementRange(int turn) const; //get speed (in moving tiles) of creature with all modificators
+	virtual ui32 getMovementRange() const; //get speed (in moving tiles) of creature with all modificators
 	virtual ui32 getMaxHealth() const; //get max HP of stack with all modifiers
 };
 

+ 15 - 4
lib/battle/CUnitState.cpp

@@ -333,6 +333,8 @@ CUnitState::CUnitState():
 	counterAttacks(this),
 	health(this),
 	shots(this),
+	stackSpeedPerTurn(this, Selector::type()(BonusType::STACKS_SPEED), BonusCacheMode::VALUE),
+	immobilizedPerTurn(this, Selector::type()(BonusType::SIEGE_WEAPON).Or(Selector::type()(BonusType::BIND_EFFECT)), BonusCacheMode::PRESENCE),
 	bonusCache(this, generateBonusSelectors()),
 	cloneLifetimeMarker(this, Selector::type()(BonusType::NONE).And(Selector::source(BonusSource::SPELL_EFFECT, BonusSourceID(SpellID(SpellID::CLONE)))), "CUnitState::cloneLifetimeMarker"),
 	cloneID(-1)
@@ -573,11 +575,20 @@ void CUnitState::setPosition(BattleHex hex)
 
 int32_t CUnitState::getInitiative(int turn) const
 {
-	if (turn == 0)
-		return valOfBonuses(BonusType::STACKS_SPEED);
+	return stackSpeedPerTurn.getValue(turn);
+}
+
+ui32 CUnitState::getMovementRange(int turn) const
+{
+	if (immobilizedPerTurn.getValue(0) != 0)
+		return 0;
+
+	return stackSpeedPerTurn.getValue(0);
+}
 
-	std::string cachingStr = "type_STACKS_SPEED_turns_" + std::to_string(turn);
-	return valOfBonuses(Selector::type()(BonusType::STACKS_SPEED).And(Selector::turns(turn)), cachingStr);
+ui32 CUnitState::getMovementRange() const
+{
+	return getMovementRange(0);
 }
 
 uint8_t CUnitState::getRangedFullDamageDistance() const

+ 5 - 0
lib/battle/CUnitState.h

@@ -248,6 +248,9 @@ public:
 	uint8_t getRangedFullDamageDistance() const;
 	uint8_t getShootingRangeDistance() const;
 
+	ui32 getMovementRange(int turn) const override;
+	ui32 getMovementRange() const override;
+
 	bool canMove(int turn = 0) const override;
 	bool defended(int turn = 0) const override;
 	bool moved(int turn = 0) const override;
@@ -293,6 +296,8 @@ private:
 
 	const IUnitEnvironment * env;
 
+	BonusCachePerTurn immobilizedPerTurn;
+	BonusCachePerTurn stackSpeedPerTurn;
 	UnitBonusValuesProxy bonusCache;
 	CCheckProxy cloneLifetimeMarker;
 

+ 56 - 0
lib/bonuses/BonusCache.cpp

@@ -79,3 +79,59 @@ const std::array<std::atomic<int32_t>, 4> & PrimarySkillsCache::getSkills() cons
 		update();
 	return skills;
 }
+
+int BonusCachePerTurn::getValueUncached(int turns) const
+{
+	std::lock_guard lock(bonusListMutex);
+
+	int nodeTreeVersion = target->getTreeVersion();
+
+	if (bonusListVersion != nodeTreeVersion)
+	{
+		bonusList = target->getBonuses(selector);
+		bonusListVersion = nodeTreeVersion;
+	}
+
+	if (mode == BonusCacheMode::VALUE)
+	{
+		if (turns != 0)
+			return bonusList->valOfBonuses(Selector::turns(turns));
+		else
+			return bonusList->totalValue();
+	}
+	else
+	{
+		if (turns != 0)
+			return bonusList->getFirst(Selector::turns(turns)) != nullptr;
+		else
+			return !bonusList->empty();
+	}
+}
+
+int BonusCachePerTurn::getValue(int turns) const
+{
+	int nodeTreeVersion = target->getTreeVersion();
+
+	if (turns < cachedTurns)
+	{
+		auto & entry = cache[turns];
+		if (entry.version == nodeTreeVersion)
+		{
+			// best case: value is in cache and up-to-date
+			return entry.value;
+		}
+		else
+		{
+			// else - compute value and update it in the cache
+			int newValue = getValueUncached(turns);
+			entry.value = newValue;
+			entry.version = nodeTreeVersion;
+			return newValue;
+		}
+	}
+	else
+	{
+		// non-cacheable value - compute and return (should be 0 / close to 0 calls)
+		return getValueUncached(turns);
+	}
+}

+ 30 - 1
lib/bonuses/BonusCache.h

@@ -12,12 +12,18 @@
 
 #include "BonusSelector.h"
 
+enum class BonusCacheMode
+{
+	VALUE, // total value of bonus will be cached
+	PRESENCE, // presence of bonus will be cached
+};
+
 /// Internal base class with no own cache
 class BonusCacheBase
 {
+protected:
 	const IBonusBearer * target;
 
-protected:
 	explicit BonusCacheBase(const IBonusBearer * target):
 		target(target)
 	{}
@@ -79,3 +85,26 @@ public:
 
 	const std::array<std::atomic<int32_t>, 4> & getSkills() const;
 };
+
+/// Cache that tracks values for different values of bonus duration
+class BonusCachePerTurn : public BonusCacheBase
+{
+	static constexpr int cachedTurns = 8;
+
+	const CSelector selector;
+	mutable TConstBonusListPtr bonusList;
+	mutable std::mutex bonusListMutex;
+	mutable std::atomic<int64_t> bonusListVersion = 0;
+	mutable std::array<BonusCacheEntry, cachedTurns> cache;
+	const BonusCacheMode mode;
+
+	int getValueUncached(int turns) const;
+public:
+	BonusCachePerTurn(const IBonusBearer * target, const CSelector & selector, BonusCacheMode mode)
+		: BonusCacheBase(target)
+		, selector(selector)
+		, mode(mode)
+	{}
+
+	int getValue(int turns) const;
+};