feat/fix: ScanQuorums improvements (#4945)

* feat: Revive quorum scan caching

* refactor: split quorumsCacheCs mutex into two

* fix: Avoid extra work in quorum scanning

Non-rotation quorums do not become rotation ones (anymore), use `useRotation` to pick the right method only. This brings CPU load for `d-isman` thread (while being idle) from ~5% down to ~1% on testnet for me.

* apply suggestions
This commit is contained in:
UdjinM6 2022-08-08 19:09:21 +03:00 committed by GitHub
parent 63d9c6abf7
commit d47af2c4bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 26 deletions

View File

@ -357,7 +357,7 @@ void CQuorumManager::CheckQuorumConnections(const Consensus::LLMQParams& llmqPar
CQuorumPtr CQuorumManager::BuildQuorumFromCommitment(const Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex) const
{
AssertLockHeld(quorumsCacheCs);
AssertLockHeld(cs_map_quorums);
assert(pQuorumBaseBlockIndex);
const uint256& quorumHash{pQuorumBaseBlockIndex->GetBlockHash()};
@ -495,21 +495,36 @@ std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqTyp
size_t nScanCommitments{nCountRequested};
std::vector<CQuorumCPtr> vecResultQuorums;
// TODO implement caching, see [pre-rotation caching](https://github.com/dashpay/dash/blob/e84bf45cefbcf9ee89ca3d706fd8abffdbcc8a84/src/llmq/quorums.cpp#L424-L448)
// for inspiration
{
LOCK(cs_scan_quorums);
auto& cache = scanQuorumsCache[llmqType];
bool fCacheExists = cache.get(pindexStart->GetBlockHash(), vecResultQuorums);
if (fCacheExists) {
// We have exactly what requested so just return it
if (vecResultQuorums.size() == nCountRequested) {
return vecResultQuorums;
}
// If we have more cached than requested return only a subvector
if (vecResultQuorums.size() > nCountRequested) {
return {vecResultQuorums.begin(), vecResultQuorums.begin() + nCountRequested};
}
// If we have cached quorums but not enough, subtract what we have from the count and the set correct index where to start
// scanning for the rests
if (!vecResultQuorums.empty()) {
nScanCommitments -= vecResultQuorums.size();
pIndexScanCommitments = vecResultQuorums.back()->m_quorum_base_block_index->pprev;
}
} else {
// If there is nothing in cache request at least cache.max_size() because this gets cached then later
nScanCommitments = std::max(nCountRequested, cache.max_size());
}
}
// Get the block indexes of the mined commitments to build the required quorums from
auto pQuorumBaseBlockIndexes = quorumBlockProcessor->GetMinedCommitmentsIndexedUntilBlock(llmqType, static_cast<const CBlockIndex*>(pIndexScanCommitments), nScanCommitments);
if (pQuorumBaseBlockIndexes.size() < nScanCommitments) {
if (!pQuorumBaseBlockIndexes.empty()) {
nScanCommitments -= pQuorumBaseBlockIndexes.size();
if (pQuorumBaseBlockIndexes.back()->pprev) {
pIndexScanCommitments = pQuorumBaseBlockIndexes.back()->pprev;
}
}
auto pQuorumBaseBlockIndexesLegacy = quorumBlockProcessor->GetMinedCommitmentsUntilBlock(llmqType, static_cast<const CBlockIndex*>(pIndexScanCommitments), nScanCommitments);
pQuorumBaseBlockIndexes.insert(pQuorumBaseBlockIndexes.end(), pQuorumBaseBlockIndexesLegacy.begin(), pQuorumBaseBlockIndexesLegacy.end());
}
std::vector<const CBlockIndex*> pQuorumBaseBlockIndexes{ GetLLMQParams(llmqType).useRotation ?
quorumBlockProcessor->GetMinedCommitmentsIndexedUntilBlock(llmqType, pIndexScanCommitments, nScanCommitments) :
quorumBlockProcessor->GetMinedCommitmentsUntilBlock(llmqType, pIndexScanCommitments, nScanCommitments)
};
vecResultQuorums.reserve(vecResultQuorums.size() + pQuorumBaseBlockIndexes.size());
for (auto& pQuorumBaseBlockIndex : pQuorumBaseBlockIndexes) {
@ -519,18 +534,17 @@ std::vector<CQuorumCPtr> CQuorumManager::ScanQuorums(Consensus::LLMQType llmqTyp
vecResultQuorums.emplace_back(quorum);
}
size_t nCountResult{vecResultQuorums.size()};
const size_t nCountResult{vecResultQuorums.size()};
if (nCountResult > 0) {
LOCK(quorumsCacheCs);
LOCK(cs_scan_quorums);
// Don't cache more than cache.max_size() elements
auto& cache = scanQuorumsCache[llmqType];
size_t nCacheEndIndex = std::min(nCountResult, cache.max_size());
const size_t nCacheEndIndex = std::min(nCountResult, cache.max_size());
cache.emplace(pindexStart->GetBlockHash(), {vecResultQuorums.begin(), vecResultQuorums.begin() + nCacheEndIndex});
}
// Don't return more than nCountRequested elements
size_t nResultEndIndex = std::min(nCountResult, nCountRequested);
const std::vector<CQuorumCPtr>& ret = {vecResultQuorums.begin(), vecResultQuorums.begin() + nResultEndIndex};
return ret;
const size_t nResultEndIndex = std::min(nCountResult, nCountRequested);
return {vecResultQuorums.begin(), vecResultQuorums.begin() + nResultEndIndex};
}
CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const
@ -555,7 +569,7 @@ CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const CBlock
return nullptr;
}
LOCK(quorumsCacheCs);
LOCK(cs_map_quorums);
CQuorumPtr pQuorum;
if (mapQuorumsCache[llmqType].get(quorumHash, pQuorum)) {
return pQuorum;
@ -722,7 +736,7 @@ void CQuorumManager::ProcessMessage(CNode* pFrom, const std::string& msg_type, C
CQuorumPtr pQuorum;
{
LOCK(quorumsCacheCs);
LOCK(cs_map_quorums);
if (!mapQuorumsCache[request.GetLLMQType()].get(request.GetQuorumHash(), pQuorum)) {
errorHandler("Quorum not found", 0); // Don't bump score because we asked for it
return;

View File

@ -210,9 +210,10 @@ private:
CBLSWorker& blsWorker;
CDKGSessionManager& dkgManager;
mutable CCriticalSection quorumsCacheCs;
mutable std::map<Consensus::LLMQType, unordered_lru_cache<uint256, CQuorumPtr, StaticSaltedHasher>> mapQuorumsCache GUARDED_BY(quorumsCacheCs);
mutable std::map<Consensus::LLMQType, unordered_lru_cache<uint256, std::vector<CQuorumCPtr>, StaticSaltedHasher>> scanQuorumsCache GUARDED_BY(quorumsCacheCs);
mutable CCriticalSection cs_map_quorums;
mutable std::map<Consensus::LLMQType, unordered_lru_cache<uint256, CQuorumPtr, StaticSaltedHasher>> mapQuorumsCache GUARDED_BY(cs_map_quorums);
mutable CCriticalSection cs_scan_quorums;
mutable std::map<Consensus::LLMQType, unordered_lru_cache<uint256, std::vector<CQuorumCPtr>, StaticSaltedHasher>> scanQuorumsCache GUARDED_BY(cs_scan_quorums);
mutable ctpl::thread_pool workerPool;
mutable CThreadInterrupt quorumThreadInterrupt;
@ -245,7 +246,7 @@ private:
// all private methods here are cs_main-free
void CheckQuorumConnections(const Consensus::LLMQParams& llmqParams, const CBlockIndex *pindexNew) const;
CQuorumPtr BuildQuorumFromCommitment(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex) const EXCLUSIVE_LOCKS_REQUIRED(quorumsCacheCs);
CQuorumPtr BuildQuorumFromCommitment(Consensus::LLMQType llmqType, const CBlockIndex* pQuorumBaseBlockIndex) const EXCLUSIVE_LOCKS_REQUIRED(cs_map_quorums);
bool BuildQuorumContributions(const CFinalCommitmentPtr& fqc, const std::shared_ptr<CQuorum>& quorum) const;
CQuorumCPtr GetQuorum(Consensus::LLMQType llmqType, const CBlockIndex* pindex) const;