Browse Source

Nullkiller: async hero chain calculation

Andrii Danylchenko 4 years ago
parent
commit
66843b22d3

+ 48 - 7
AI/Nullkiller/Pathfinding/AINodeStorage.cpp

@@ -401,11 +401,12 @@ private:
 	int heroChainTurn;
 	std::vector<CGPathNode *> heroChain;
 	const std::vector<int3> & tiles;
+	std::vector<DelayedWork> delayedWork;
 
 public:
 	HeroChainCalculationTask(
 		AINodeStorage & storage, AISharedStorage & nodes, const std::vector<int3> & tiles, uint64_t chainMask, int heroChainTurn)
-		:existingChains(), newChains(), nodes(nodes), storage(storage), chainMask(chainMask), heroChainTurn(heroChainTurn), heroChain(), tiles(tiles)
+		:existingChains(), newChains(), delayedWork(), nodes(nodes), storage(storage), chainMask(chainMask), heroChainTurn(heroChainTurn), heroChain(), tiles(tiles)
 	{
 		existingChains.reserve(NUM_CHAINS);
 		newChains.reserve(NUM_CHAINS);
@@ -444,6 +445,22 @@ public:
 					}
 				}
 
+				for(auto delayed = delayedWork.begin(); delayed != delayedWork.end();)
+				{
+					auto newActor = delayed->carrier->actor->tryExchangeNoLock(delayed->other->actor);
+
+					if(!newActor.lockAcquired) continue;
+					
+					if(newActor.actor)
+					{
+						newChains.push_back(calculateExchange(newActor.actor, delayed->carrier, delayed->other));
+					}
+					
+					delayed++;
+				}
+
+				delayedWork.clear();
+
 				cleanupInefectiveChains(newChains);
 				addHeroChain(newChains);
 			}
@@ -483,11 +500,34 @@ bool AINodeStorage::calculateHeroChain()
 
 	CCreature::DisableChildLinkage = true;
 
-	auto r = blocked_range<size_t>(0, data.size());
-	HeroChainCalculationTask task(*this, nodes, data, chainMask, heroChainTurn);
+	if(data.size() > 100)
+	{
+		std::mutex resultMutex;
+
+	std::random_shuffle(data.begin(), data.end());
+
+		parallel_for(blocked_range<size_t>(0, data.size()), [&](const blocked_range<size_t>& r)
+		{
+			//auto r = blocked_range<size_t>(0, data.size());
+			HeroChainCalculationTask task(*this, nodes, data, chainMask, heroChainTurn);
+
+			task.execute(r);
+
+			{
+				std::lock_guard<std::mutex> resultLock(resultMutex);
+
+				task.flushResult(heroChain);
+			}
+		});
+	}
+	else
+	{
+		auto r = blocked_range<size_t>(0, data.size());
+		HeroChainCalculationTask task(*this, nodes, data, chainMask, heroChainTurn);
 
-	task.execute(r);
-	task.flushResult(heroChain);
+		task.execute(r);
+		task.flushResult(heroChain);\
+	}
 
 	CCreature::DisableChildLinkage = false;
 
@@ -666,9 +706,10 @@ void HeroChainCalculationTask::calculateHeroChain(
 			}
 		}
 
-		auto newActor = carrier->actor->tryExchange(other->actor);
+		auto newActor = carrier->actor->tryExchangeNoLock(other->actor);
 		
-		if(newActor) result.push_back(calculateExchange(newActor, carrier, other));
+		if(!newActor.lockAcquired) delayedWork.push_back(DelayedWork(carrier, other));
+		if(newActor.actor) result.push_back(calculateExchange(newActor.actor, carrier, other));
 	}
 }
 

+ 87 - 56
AI/Nullkiller/Pathfinding/Actors.cpp

@@ -145,11 +145,11 @@ void HeroActor::setupSpecialActors()
 	}
 }
 
-ChainActor * ChainActor::tryExchange(const ChainActor * specialActor, const ChainActor * other) const
+ExchangeResult ChainActor::tryExchangeNoLock(const ChainActor * specialActor, const ChainActor * other) const
 {
-	if(!isMovable) return nullptr;
+	if(!isMovable) return ExchangeResult();
 
-	return baseActor->tryExchange(specialActor, other);
+	return baseActor->tryExchangeNoLock(specialActor, other);
 }
 
 namespace vstd
@@ -169,12 +169,12 @@ namespace vstd
 	}
 }
 
-ChainActor * HeroActor::tryExchange(const ChainActor * specialActor, const ChainActor * other) const
+ExchangeResult HeroActor::tryExchangeNoLock(const ChainActor * specialActor, const ChainActor * other) const
 {
 	const ChainActor * otherBase = other->baseActor;
-	HeroActor * result = exchangeMap->tryExchange(otherBase);
+	ExchangeResult result = exchangeMap->tryExchangeNoLock(otherBase);
 
-	if(!result) return nullptr;
+	if(!result.actor || !result.lockAcquired) return result;
 
 	if(specialActor == this)
 		return result;
@@ -184,7 +184,9 @@ ChainActor * HeroActor::tryExchange(const ChainActor * specialActor, const Chain
 		return &actor == specialActor;
 	});
 
-	return &result->specialActors[index];
+	result.actor = &(dynamic_cast<HeroActor *>(result.actor)->specialActors[index]);
+
+	return result;
 }
 
 HeroExchangeMap::HeroExchangeMap(const HeroActor * actor, const Nullkiller * ai)
@@ -205,40 +207,67 @@ HeroExchangeMap::~HeroExchangeMap()
 	exchangeMap.clear();
 }
 
-HeroActor * HeroExchangeMap::tryExchange(const ChainActor * other)
+ExchangeResult HeroExchangeMap::tryExchangeNoLock(const ChainActor * other)
 {
-	auto position = exchangeMap.find(other);
+	ExchangeResult result;
 
-	if(position != exchangeMap.end())
 	{
-		return position->second;
-	}
+		boost::shared_lock<boost::shared_mutex> lock(sync, boost::try_to_lock);
 
-	auto inserted = exchangeMap.insert(std::pair<const ChainActor *, HeroActor *>(other, nullptr));
+		if(!lock.owns_lock())
+		{
+			result.lockAcquired = false;
 
-	if(!inserted.second)
-	{
-		return inserted.first->second; // already inserted
+			return result;
+		}
+
+		auto position = exchangeMap.find(other);
+
+		if(position != exchangeMap.end())
+		{
+			result.actor = position->second;
+
+			return result;
+		}
 	}
 
-	position = inserted.first;
+	{
+		boost::unique_lock<boost::shared_mutex> uniqueLock(sync, boost::try_to_lock);
 
-	auto differentMasks = (actor->chainMask & other->chainMask) == 0;
+		if(!uniqueLock.owns_lock())
+		{
+			result.lockAcquired = false;
 
-	if(!differentMasks) return nullptr;
+			return result;
+		}
 
-	TResources resources = ai->cb->getResourceAmount();
+		auto inserted = exchangeMap.insert(std::pair<const ChainActor *, HeroActor *>(other, nullptr));
 
-	if(!resources.canAfford(actor->armyCost + other->armyCost))
-	{
+		if(!inserted.second)
+		{
+			result.actor = inserted.first->second;
+
+			return result; // already inserted
+		}
+
+		auto position = inserted.first;
+
+		auto differentMasks = (actor->chainMask & other->chainMask) == 0;
+
+		if(!differentMasks) return result;
+
+		TResources resources = ai->cb->getResourceAmount();
+
+		if(!resources.canAfford(actor->armyCost + other->armyCost))
+		{
 #if PATHFINDER_TRACE_LEVEL >= 2
-		logAi->trace(
-			"Can not afford exchange because of total cost %s but we have %s",
-			(actor->armyCost + other->armyCost).toString(),
-			resources.toString());
+			logAi->trace(
+				"Can not afford exchange because of total cost %s but we have %s",
+				(actor->armyCost + other->armyCost).toString(),
+				resources.toString());
 #endif
-		return nullptr;
-	}
+			return result;
+		}
 
 	if(other->isMovable && other->armyValue <= actor->armyValue / 10 && other->armyValue < MIN_ARMY_STRENGTH_FOR_CHAIN)
 		return nullptr;
@@ -247,52 +276,54 @@ HeroActor * HeroExchangeMap::tryExchange(const ChainActor * other)
 	HeroExchangeArmy * upgradedInitialArmy = tryUpgrade(actor->creatureSet, other->getActorObject(), availableResources);
 	HeroExchangeArmy * newArmy;
 
-	if(other->creatureSet->Slots().size())
-	{
-		if(upgradedInitialArmy)
+		if(other->creatureSet->Slots().size())
 		{
-			newArmy = pickBestCreatures(upgradedInitialArmy, other->creatureSet);
-			newArmy->armyCost = upgradedInitialArmy->armyCost;
-			newArmy->requireBuyArmy = upgradedInitialArmy->requireBuyArmy;
-
-			delete upgradedInitialArmy;
+			if(upgradedInitialArmy)
+			{
+				newArmy = pickBestCreatures(upgradedInitialArmy, other->creatureSet);
+				newArmy->armyCost = upgradedInitialArmy->armyCost;
+				newArmy->requireBuyArmy = upgradedInitialArmy->requireBuyArmy;
+
+				delete upgradedInitialArmy;
+			}
+			else
+			{
+				newArmy = pickBestCreatures(actor->creatureSet, other->creatureSet);
+			}
 		}
 		else
 		{
-			newArmy = pickBestCreatures(actor->creatureSet, other->creatureSet);
+			newArmy = upgradedInitialArmy;
 		}
-	}
-	else
-	{
-		newArmy = upgradedInitialArmy;
-	}
 
-	if(!newArmy) return nullptr;
+		if(!newArmy) return result;
 
-	auto reinforcement = newArmy->getArmyStrength() - actor->creatureSet->getArmyStrength();
+		auto reinforcement = newArmy->getArmyStrength() - actor->creatureSet->getArmyStrength();
 
 #if PATHFINDER_TRACE_LEVEL >= 2
-	logAi->trace(
-		"Exchange %s->%s reinforcement: %d, %f%%",
-		actor->toString(),
-		other->toString(),
-		reinforcement,
-		100.0f * reinforcement / actor->armyValue);
+		logAi->trace(
+			"Exchange %s->%s reinforcement: %d, %f%%",
+			actor->toString(),
+			other->toString(),
+			reinforcement,
+			100.0f * reinforcement / actor->armyValue);
 #endif
 
 	if(reinforcement <= actor->armyValue / 10 && reinforcement < MIN_ARMY_STRENGTH_FOR_CHAIN)
 	{
 		delete newArmy;
 
-		return nullptr;
-	}
+			return result;
+		}
 
-	HeroActor * exchanged = new HeroActor(actor, other, newArmy, ai);
+		HeroActor * exchanged = new HeroActor(actor, other, newArmy, ai);
 
-	exchanged->armyCost += newArmy->armyCost;
-	position->second = exchanged;
+		exchanged->armyCost += newArmy->armyCost;
+		result.actor = exchanged;
+		exchangeMap[other] = exchanged;
 
-	return exchanged;
+		return result;
+	}
 }
 
 HeroExchangeArmy * HeroExchangeMap::tryUpgrade(

+ 13 - 4
AI/Nullkiller/Pathfinding/Actors.h

@@ -17,6 +17,7 @@
 
 extern const uint64_t MIN_ARMY_STRENGTH_FOR_CHAIN;
 
+class ChainActor;
 class HeroActor;
 class Nullkiller;
 
@@ -33,6 +34,14 @@ public:
 	}
 };
 
+struct ExchangeResult
+{
+	bool lockAcquired;
+	ChainActor * actor;
+
+	ExchangeResult() : lockAcquired(true), actor(nullptr) {}
+};
+
 class ChainActor
 {
 protected:
@@ -68,12 +77,12 @@ public:
 	ChainActor(){}
 
 	virtual std::string toString() const;
-	ChainActor * tryExchange(const ChainActor * other) const { return tryExchange(this, other); }
+	ExchangeResult tryExchangeNoLock(const ChainActor * other) const { return tryExchangeNoLock(this, other); }
 	void setBaseActor(HeroActor * base);
 	virtual const CGObjectInstance * getActorObject() const	{ return hero; }
 
 protected:
-	virtual ChainActor * tryExchange(const ChainActor * specialActor, const ChainActor * other) const;
+	virtual ExchangeResult tryExchangeNoLock(const ChainActor * specialActor, const ChainActor * other) const;
 };
 
 class HeroExchangeMap
@@ -88,7 +97,7 @@ public:
 	HeroExchangeMap(const HeroActor * actor, const Nullkiller * ai);
 	~HeroExchangeMap();
 
-	HeroActor * tryExchange(const ChainActor * other);
+	ExchangeResult tryExchangeNoLock(const ChainActor * other);
 
 private:
 	HeroExchangeArmy * pickBestCreatures(const CCreatureSet * army1, const CCreatureSet * army2) const;
@@ -114,7 +123,7 @@ public:
 	HeroActor(const ChainActor * carrier, const ChainActor * other, const HeroExchangeArmy * army, const Nullkiller * ai);
 
 protected:
-	virtual ChainActor * tryExchange(const ChainActor * specialActor, const ChainActor * other) const override;
+	virtual ExchangeResult tryExchangeNoLock(const ChainActor * specialActor, const ChainActor * other) const override;
 };
 
 class ObjectActor : public ChainActor