From e2eed966b0ecb6445027e6a023b48d702c5f4832 Mon Sep 17 00:00:00 2001 From: Nik Bougalis Date: Wed, 25 May 2022 11:22:47 -0700 Subject: [PATCH] Improve AccountID string conversion caching: Caching the base58check encoded version of an `AccountID` has performance advantages, because because of the computationally heavy cost associated with the conversion, which requires the application of SHA-256 twice. This commit makes the cache significantly more efficient in terms of memory used: it eliminates the map, using a vector with a size that is determined by the configured size of the node, and a hash function to directly map any given `AccountID` to a specific slot in the cache; the eviction policy is simple: in case of collision the existing entry is removed and replaced with the new data. Previously, use of the cache was optional and required additional effort by the programmer. Now the cache is automatic and does not require any additional work or information. The new cache also utilizes a 64-way spinlock, to help reduce any contention that the pressure on the cache would impose. --- src/ripple/app/ledger/AcceptedLedger.cpp | 4 +- src/ripple/app/ledger/AcceptedLedgerTx.cpp | 5 +- src/ripple/app/ledger/AcceptedLedgerTx.h | 3 +- src/ripple/app/main/Application.cpp | 11 +- src/ripple/app/main/Application.h | 3 - src/ripple/app/paths/PathRequest.cpp | 19 ++- src/ripple/app/rdb/backend/detail/Node.h | 4 - .../app/rdb/backend/detail/impl/Node.cpp | 21 +-- .../app/rdb/backend/impl/SQLiteDatabase.cpp | 60 +++------ src/ripple/core/Config.h | 3 +- src/ripple/core/impl/Config.cpp | 29 ++-- src/ripple/protocol/AccountID.h | 43 ++---- src/ripple/protocol/impl/AccountID.cpp | 126 +++++++++++------- src/ripple/rpc/handlers/AccountChannels.cpp | 2 +- src/ripple/rpc/handlers/AccountInfo.cpp | 2 +- src/ripple/rpc/handlers/AccountLines.cpp | 2 +- src/ripple/rpc/handlers/AccountObjects.cpp | 4 +- src/ripple/rpc/handlers/AccountOffers.cpp | 2 +- src/ripple/rpc/handlers/GatewayBalances.cpp | 2 +- src/ripple/rpc/handlers/NFTOffers.cpp | 6 +- src/ripple/rpc/handlers/NoRippleCheck.cpp | 2 +- 21 files changed, 157 insertions(+), 196 deletions(-) diff --git a/src/ripple/app/ledger/AcceptedLedger.cpp b/src/ripple/app/ledger/AcceptedLedger.cpp index 526704d188..4f308653dc 100644 --- a/src/ripple/app/ledger/AcceptedLedger.cpp +++ b/src/ripple/app/ledger/AcceptedLedger.cpp @@ -31,11 +31,9 @@ AcceptedLedger::AcceptedLedger( transactions_.reserve(256); auto insertAll = [&](auto const& txns) { - auto const& idcache = app.accountIDCache(); - for (auto const& item : txns) transactions_.emplace_back(std::make_unique( - ledger, item.first, item.second, idcache)); + ledger, item.first, item.second)); }; if (app.config().reporting()) diff --git a/src/ripple/app/ledger/AcceptedLedgerTx.cpp b/src/ripple/app/ledger/AcceptedLedgerTx.cpp index f0408b0c04..613a91e437 100644 --- a/src/ripple/app/ledger/AcceptedLedgerTx.cpp +++ b/src/ripple/app/ledger/AcceptedLedgerTx.cpp @@ -28,8 +28,7 @@ namespace ripple { AcceptedLedgerTx::AcceptedLedgerTx( std::shared_ptr const& ledger, std::shared_ptr const& txn, - std::shared_ptr const& met, - AccountIDCache const& accountCache) + std::shared_ptr const& met) : mTxn(txn) , mMeta(txn->getTransactionID(), ledger->seq(), *met) , mAffected(mMeta.getAffectedAccounts()) @@ -52,7 +51,7 @@ AcceptedLedgerTx::AcceptedLedgerTx( { Json::Value& affected = (mJson[jss::affected] = Json::arrayValue); for (auto const& account : mAffected) - affected.append(accountCache.toBase58(account)); + affected.append(toBase58(account)); } if (mTxn->getTxnType() == ttOFFER_CREATE) diff --git a/src/ripple/app/ledger/AcceptedLedgerTx.h b/src/ripple/app/ledger/AcceptedLedgerTx.h index 7d68978571..2995d447bb 100644 --- a/src/ripple/app/ledger/AcceptedLedgerTx.h +++ b/src/ripple/app/ledger/AcceptedLedgerTx.h @@ -46,8 +46,7 @@ public: AcceptedLedgerTx( std::shared_ptr const& ledger, std::shared_ptr const&, - std::shared_ptr const&, - AccountIDCache const&); + std::shared_ptr const&); std::shared_ptr const& getTxn() const diff --git a/src/ripple/app/main/Application.cpp b/src/ripple/app/main/Application.cpp index 99f3b060b9..dce11bc38f 100644 --- a/src/ripple/app/main/Application.cpp +++ b/src/ripple/app/main/Application.cpp @@ -181,7 +181,6 @@ public: NodeStoreScheduler m_nodeStoreScheduler; std::unique_ptr m_shaMapStore; PendingSaves pendingSaves_; - AccountIDCache accountIDCache_; std::optional openLedger_; NodeCache m_tempNodeCache; @@ -336,8 +335,6 @@ public: m_nodeStoreScheduler, logs_->journal("SHAMapStore"))) - , accountIDCache_(128000) - , m_tempNodeCache( "NodeCache", 16384, @@ -494,6 +491,8 @@ public: config_->reporting() ? std::make_unique(*this) : nullptr) { + initAccountIdCache(config_->getValueFor(SizedItem::accountIdCacheSize)); + add(m_resourceManager.get()); // @@ -856,12 +855,6 @@ public: return pendingSaves_; } - AccountIDCache const& - accountIDCache() const override - { - return accountIDCache_; - } - OpenLedger& openLedger() override { diff --git a/src/ripple/app/main/Application.h b/src/ripple/app/main/Application.h index 3b357deef3..d8cb7d3181 100644 --- a/src/ripple/app/main/Application.h +++ b/src/ripple/app/main/Application.h @@ -90,7 +90,6 @@ class PathRequests; class PendingSaves; class PublicKey; class SecretKey; -class AccountIDCache; class STLedgerEntry; class TimeKeeper; class TransactionMaster; @@ -251,8 +250,6 @@ public: getSHAMapStore() = 0; virtual PendingSaves& pendingSaves() = 0; - virtual AccountIDCache const& - accountIDCache() const = 0; virtual OpenLedger& openLedger() = 0; virtual OpenLedger const& diff --git a/src/ripple/app/paths/PathRequest.cpp b/src/ripple/app/paths/PathRequest.cpp index d1acb3ac1f..02b46c81e9 100644 --- a/src/ripple/app/paths/PathRequest.cpp +++ b/src/ripple/app/paths/PathRequest.cpp @@ -552,9 +552,16 @@ PathRequest::findPaths( continueCallback); mContext[issue] = ps; - auto& sourceAccount = !isXRP(issue.account) - ? issue.account - : isXRP(issue.currency) ? xrpAccount() : *raSrcAccount; + auto const& sourceAccount = [&] { + if (!isXRP(issue.account)) + return issue.account; + + if (isXRP(issue.currency)) + return xrpAccount(); + + return *raSrcAccount; + }(); + STAmount saMaxAmount = saSendMax.value_or( STAmount({issue.currency, sourceAccount}, 1u, 0, true)); @@ -675,10 +682,8 @@ PathRequest::doUpdate( destCurrencies.append(to_string(c)); } - newStatus[jss::source_account] = - app_.accountIDCache().toBase58(*raSrcAccount); - newStatus[jss::destination_account] = - app_.accountIDCache().toBase58(*raDstAccount); + newStatus[jss::source_account] = toBase58(*raSrcAccount); + newStatus[jss::destination_account] = toBase58(*raDstAccount); newStatus[jss::destination_amount] = saDstAmount.getJson(JsonOptions::none); newStatus[jss::full_reply] = !fast; diff --git a/src/ripple/app/rdb/backend/detail/Node.h b/src/ripple/app/rdb/backend/detail/Node.h index fa7e39c832..0ebae76dc2 100644 --- a/src/ripple/app/rdb/backend/detail/Node.h +++ b/src/ripple/app/rdb/backend/detail/Node.h @@ -389,7 +389,6 @@ getNewestAccountTxsB( * account which match given criteria starting from given marker * and calls callback for each found transaction. * @param session Session with database. - * @param idCache Account ID cache. * @param onUnsavedLedger Callback function to call on each found unsaved * ledger within given range. * @param onTransaction Callback function to call on each found transaction. @@ -408,7 +407,6 @@ getNewestAccountTxsB( std::pair, int> oldestAccountTxPage( soci::session& session, - AccountIDCache const& idCache, std::function const& onUnsavedLedger, std::function< void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& @@ -422,7 +420,6 @@ oldestAccountTxPage( * account which match given criteria starting from given marker * and calls callback for each found transaction. * @param session Session with database. - * @param idCache Account ID cache. * @param onUnsavedLedger Callback function to call on each found unsaved * ledger within given range. * @param onTransaction Callback function to call on each found transaction. @@ -441,7 +438,6 @@ oldestAccountTxPage( std::pair, int> newestAccountTxPage( soci::session& session, - AccountIDCache const& idCache, std::function const& onUnsavedLedger, std::function< void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& diff --git a/src/ripple/app/rdb/backend/detail/impl/Node.cpp b/src/ripple/app/rdb/backend/detail/impl/Node.cpp index 0c9f3b0171..b3b354ebe7 100644 --- a/src/ripple/app/rdb/backend/detail/impl/Node.cpp +++ b/src/ripple/app/rdb/backend/detail/impl/Node.cpp @@ -307,7 +307,7 @@ saveValidatedLedger( sql += txnId; sql += "','"; - sql += app.accountIDCache().toBase58(account); + sql += toBase58(account); sql += "',"; sql += ledgerSeq; sql += ","; @@ -760,8 +760,7 @@ transactionsSQL( sql = boost::str( boost::format("SELECT %s FROM AccountTransactions " "WHERE Account = '%s' %s %s LIMIT %u, %u;") % - selection % app.accountIDCache().toBase58(options.account) % - maxClause % minClause % + selection % toBase58(options.account) % maxClause % minClause % beast::lexicalCastThrow(options.offset) % beast::lexicalCastThrow(numberOfResults)); else @@ -774,9 +773,9 @@ transactionsSQL( "ORDER BY AccountTransactions.LedgerSeq %s, " "AccountTransactions.TxnSeq %s, AccountTransactions.TransID %s " "LIMIT %u, %u;") % - selection % app.accountIDCache().toBase58(options.account) % - maxClause % minClause % (descending ? "DESC" : "ASC") % + selection % toBase58(options.account) % maxClause % minClause % (descending ? "DESC" : "ASC") % (descending ? "DESC" : "ASC") % + (descending ? "DESC" : "ASC") % beast::lexicalCastThrow(options.offset) % beast::lexicalCastThrow(numberOfResults)); JLOG(j.trace()) << "txSQL query: " << sql; @@ -1049,7 +1048,6 @@ getNewestAccountTxsB( * account that matches the given criteria starting from the provided * marker and invokes the callback parameter for each found transaction. * @param session Session with the database. - * @param idCache Account ID cache. * @param onUnsavedLedger Callback function to call on each found unsaved * ledger within the given range. * @param onTransaction Callback function to call on each found transaction. @@ -1069,7 +1067,6 @@ getNewestAccountTxsB( static std::pair, int> accountTxPage( soci::session& session, - AccountIDCache const& idCache, std::function const& onUnsavedLedger, std::function< void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& @@ -1135,8 +1132,8 @@ accountTxPage( ORDER BY AccountTransactions.LedgerSeq %s, AccountTransactions.TxnSeq %s LIMIT %u;)")) % - idCache.toBase58(options.account) % options.minLedger % - options.maxLedger % order % order % queryLimit); + toBase58(options.account) % options.minLedger % options.maxLedger % + order % order % queryLimit); } else { @@ -1146,7 +1143,7 @@ accountTxPage( const std::uint32_t maxLedger = forward ? options.maxLedger : findLedger - 1; - auto b58acct = idCache.toBase58(options.account); + auto b58acct = toBase58(options.account); sql = boost::str( boost::format(( R"(SELECT AccountTransactions.LedgerSeq,AccountTransactions.TxnSeq, @@ -1250,7 +1247,6 @@ accountTxPage( std::pair, int> oldestAccountTxPage( soci::session& session, - AccountIDCache const& idCache, std::function const& onUnsavedLedger, std::function< void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& @@ -1261,7 +1257,6 @@ oldestAccountTxPage( { return accountTxPage( session, - idCache, onUnsavedLedger, onTransaction, options, @@ -1273,7 +1268,6 @@ oldestAccountTxPage( std::pair, int> newestAccountTxPage( soci::session& session, - AccountIDCache const& idCache, std::function const& onUnsavedLedger, std::function< void(std::uint32_t, std::string const&, Blob&&, Blob&&)> const& @@ -1284,7 +1278,6 @@ newestAccountTxPage( { return accountTxPage( session, - idCache, onUnsavedLedger, onTransaction, options, diff --git a/src/ripple/app/rdb/backend/impl/SQLiteDatabase.cpp b/src/ripple/app/rdb/backend/impl/SQLiteDatabase.cpp index e6ec44399a..547ab843b3 100644 --- a/src/ripple/app/rdb/backend/impl/SQLiteDatabase.cpp +++ b/src/ripple/app/rdb/backend/impl/SQLiteDatabase.cpp @@ -1322,7 +1322,6 @@ SQLiteDatabaseImp::oldestAccountTxPage(AccountTxPageOptions const& options) return {}; static std::uint32_t const page_length(200); - auto& idCache = app_.accountIDCache(); auto onUnsavedLedger = std::bind(saveLedgerAsync, std::ref(app_), std::placeholders::_1); AccountTxs ret; @@ -1338,15 +1337,10 @@ SQLiteDatabaseImp::oldestAccountTxPage(AccountTxPageOptions const& options) if (existsTransaction()) { auto db = checkoutTransaction(); - auto newmarker = detail::oldestAccountTxPage( - *db, - idCache, - onUnsavedLedger, - onTransaction, - options, - 0, - page_length) - .first; + auto newmarker = + detail::oldestAccountTxPage( + *db, onUnsavedLedger, onTransaction, options, 0, page_length) + .first; return {ret, newmarker}; } @@ -1363,7 +1357,6 @@ SQLiteDatabaseImp::oldestAccountTxPage(AccountTxPageOptions const& options) return false; auto [marker, total] = detail::oldestAccountTxPage( session, - idCache, onUnsavedLedger, onTransaction, opt, @@ -1391,7 +1384,6 @@ SQLiteDatabaseImp::newestAccountTxPage(AccountTxPageOptions const& options) return {}; static std::uint32_t const page_length(200); - auto& idCache = app_.accountIDCache(); auto onUnsavedLedger = std::bind(saveLedgerAsync, std::ref(app_), std::placeholders::_1); AccountTxs ret; @@ -1407,15 +1399,10 @@ SQLiteDatabaseImp::newestAccountTxPage(AccountTxPageOptions const& options) if (existsTransaction()) { auto db = checkoutTransaction(); - auto newmarker = detail::newestAccountTxPage( - *db, - idCache, - onUnsavedLedger, - onTransaction, - options, - 0, - page_length) - .first; + auto newmarker = + detail::newestAccountTxPage( + *db, onUnsavedLedger, onTransaction, options, 0, page_length) + .first; return {ret, newmarker}; } @@ -1432,7 +1419,6 @@ SQLiteDatabaseImp::newestAccountTxPage(AccountTxPageOptions const& options) return false; auto [marker, total] = detail::newestAccountTxPage( session, - idCache, onUnsavedLedger, onTransaction, opt, @@ -1460,7 +1446,6 @@ SQLiteDatabaseImp::oldestAccountTxPageB(AccountTxPageOptions const& options) return {}; static std::uint32_t const page_length(500); - auto& idCache = app_.accountIDCache(); auto onUnsavedLedger = std::bind(saveLedgerAsync, std::ref(app_), std::placeholders::_1); MetaTxsList ret; @@ -1475,15 +1460,10 @@ SQLiteDatabaseImp::oldestAccountTxPageB(AccountTxPageOptions const& options) if (existsTransaction()) { auto db = checkoutTransaction(); - auto newmarker = detail::oldestAccountTxPage( - *db, - idCache, - onUnsavedLedger, - onTransaction, - options, - 0, - page_length) - .first; + auto newmarker = + detail::oldestAccountTxPage( + *db, onUnsavedLedger, onTransaction, options, 0, page_length) + .first; return {ret, newmarker}; } @@ -1500,7 +1480,6 @@ SQLiteDatabaseImp::oldestAccountTxPageB(AccountTxPageOptions const& options) return false; auto [marker, total] = detail::oldestAccountTxPage( session, - idCache, onUnsavedLedger, onTransaction, opt, @@ -1528,7 +1507,6 @@ SQLiteDatabaseImp::newestAccountTxPageB(AccountTxPageOptions const& options) return {}; static std::uint32_t const page_length(500); - auto& idCache = app_.accountIDCache(); auto onUnsavedLedger = std::bind(saveLedgerAsync, std::ref(app_), std::placeholders::_1); MetaTxsList ret; @@ -1543,15 +1521,10 @@ SQLiteDatabaseImp::newestAccountTxPageB(AccountTxPageOptions const& options) if (existsTransaction()) { auto db = checkoutTransaction(); - auto newmarker = detail::newestAccountTxPage( - *db, - idCache, - onUnsavedLedger, - onTransaction, - options, - 0, - page_length) - .first; + auto newmarker = + detail::newestAccountTxPage( + *db, onUnsavedLedger, onTransaction, options, 0, page_length) + .first; return {ret, newmarker}; } @@ -1568,7 +1541,6 @@ SQLiteDatabaseImp::newestAccountTxPageB(AccountTxPageOptions const& options) return false; auto [marker, total] = detail::newestAccountTxPage( session, - idCache, onUnsavedLedger, onTransaction, opt, diff --git a/src/ripple/core/Config.h b/src/ripple/core/Config.h index c4a5076e76..2d440a1afd 100644 --- a/src/ripple/core/Config.h +++ b/src/ripple/core/Config.h @@ -57,7 +57,8 @@ enum class SizedItem : std::size_t { lgrDBCache, openFinalLimit, burstSize, - ramSizeGB + ramSizeGB, + accountIdCacheSize, }; // This entire derived class is deprecated. diff --git a/src/ripple/core/impl/Config.cpp b/src/ripple/core/impl/Config.cpp index e53d968839..f8d8878a77 100644 --- a/src/ripple/core/impl/Config.cpp +++ b/src/ripple/core/impl/Config.cpp @@ -108,26 +108,27 @@ namespace ripple { // clang-format off // The configurable node sizes are "tiny", "small", "medium", "large", "huge" -inline constexpr std::array>, 12> +inline constexpr std::array>, 13> sizedItems {{ // FIXME: We should document each of these items, explaining exactly // what they control and whether there exists an explicit // config option that can be used to override the default. - // tiny small medium large huge - {SizedItem::sweepInterval, {{ 10, 30, 60, 90, 120 }}}, - {SizedItem::treeCacheSize, {{ 262144, 524288, 2097152, 4194304, 8388608 }}}, - {SizedItem::treeCacheAge, {{ 30, 60, 90, 120, 900 }}}, - {SizedItem::ledgerSize, {{ 32, 32, 64, 256, 384 }}}, - {SizedItem::ledgerAge, {{ 30, 60, 180, 300, 600 }}}, - {SizedItem::ledgerFetch, {{ 2, 3, 4, 5, 8 }}}, - {SizedItem::hashNodeDBCache, {{ 4, 12, 24, 64, 128 }}}, - {SizedItem::txnDBCache, {{ 4, 12, 24, 64, 128 }}}, - {SizedItem::lgrDBCache, {{ 4, 8, 16, 32, 128 }}}, - {SizedItem::openFinalLimit, {{ 8, 16, 32, 64, 128 }}}, - {SizedItem::burstSize, {{ 4, 8, 16, 32, 48 }}}, - {SizedItem::ramSizeGB, {{ 8, 12, 16, 24, 32 }}}, + // tiny small medium large huge + {SizedItem::sweepInterval, {{ 10, 30, 60, 90, 120 }}}, + {SizedItem::treeCacheSize, {{ 262144, 524288, 2097152, 4194304, 8388608 }}}, + {SizedItem::treeCacheAge, {{ 30, 60, 90, 120, 900 }}}, + {SizedItem::ledgerSize, {{ 32, 32, 64, 256, 384 }}}, + {SizedItem::ledgerAge, {{ 30, 60, 180, 300, 600 }}}, + {SizedItem::ledgerFetch, {{ 2, 3, 4, 5, 8 }}}, + {SizedItem::hashNodeDBCache, {{ 4, 12, 24, 64, 128 }}}, + {SizedItem::txnDBCache, {{ 4, 12, 24, 64, 128 }}}, + {SizedItem::lgrDBCache, {{ 4, 8, 16, 32, 128 }}}, + {SizedItem::openFinalLimit, {{ 8, 16, 32, 64, 128 }}}, + {SizedItem::burstSize, {{ 4, 8, 16, 32, 48 }}}, + {SizedItem::ramSizeGB, {{ 8, 12, 16, 24, 32 }}}, + {SizedItem::accountIdCacheSize, {{ 20047, 50053, 77081, 150061, 300007 }}} }}; // Ensure that the order of entries in the table corresponds to the diff --git a/src/ripple/protocol/AccountID.h b/src/ripple/protocol/AccountID.h index e514cfb741..79768eefd7 100644 --- a/src/ripple/protocol/AccountID.h +++ b/src/ripple/protocol/AccountID.h @@ -106,41 +106,20 @@ operator<<(std::ostream& os, AccountID const& x) return os; } -//------------------------------------------------------------------------------ +/** Initialize the global cache used to map AccountID to base58 conversions. -/** Caches the base58 representations of AccountIDs + The cache is optional and need not be initialized. But because conversion + is expensive (it requires a SHA-256 operation) in most cases the overhead + of the cache is worth the benefit. - This operation occurs with sufficient frequency to - justify having a cache. In the future, rippled should - require clients to receive "binary" results, where - AccountIDs are hex-encoded. -*/ -class AccountIDCache -{ -private: - std::mutex mutable mutex_; - std::size_t capacity_; - hash_map mutable m0_; - hash_map mutable m1_; + @param count The number of entries the cache should accomodate. Zero will + disable the cache, releasing any memory associated with it. -public: - AccountIDCache(AccountIDCache const&) = delete; - AccountIDCache& - operator=(AccountIDCache const&) = delete; - - explicit AccountIDCache(std::size_t capacity); - - /** Return ripple::toBase58 for the AccountID - - Thread Safety: - Safe to call from any thread concurrently - - @note This function intentionally returns a - copy for correctness. - */ - std::string - toBase58(AccountID const&) const; -}; + @note The function will only initialize the cache the first time it is + invoked. Subsequent invocations do nothing. + */ +void +initAccountIdCache(std::size_t count); } // namespace ripple diff --git a/src/ripple/protocol/impl/AccountID.cpp b/src/ripple/protocol/impl/AccountID.cpp index 8ca8d1d153..c615807cf8 100644 --- a/src/ripple/protocol/impl/AccountID.cpp +++ b/src/ripple/protocol/impl/AccountID.cpp @@ -17,17 +17,95 @@ */ //============================================================================== +#include +#include #include #include #include #include +#include #include +#include namespace ripple { +namespace detail { + +/** Caches the base58 representations of AccountIDs */ +class AccountIdCache +{ +private: + struct CachedAccountID + { + AccountID id; + char encoding[40] = {0}; + }; + + // The actual cache + std::vector cache_; + + // We use a hash function designed to resist algorithmic complexity attacks + hardened_hash<> hasher_; + + // 64 spinlocks, packed into a single 64-bit value + std::atomic locks_ = 0; + +public: + AccountIdCache(std::size_t count) : cache_(count) + { + // This is non-binding, but we try to avoid wasting memory that + // is caused by overallocation. + cache_.shrink_to_fit(); + } + + std::string + toBase58(AccountID const& id) + { + auto const index = hasher_(id) % cache_.size(); + + packed_spinlock sl(locks_, index % 64); + + { + std::lock_guard lock(sl); + + // The check against the first character of the encoding ensures + // that we don't mishandle the case of the all-zero account: + if (cache_[index].encoding[0] != 0 && cache_[index].id == id) + return cache_[index].encoding; + } + + auto ret = + encodeBase58Token(TokenType::AccountID, id.data(), id.size()); + + assert(ret.size() <= 38); + + { + std::lock_guard lock(sl); + cache_[index].id = id; + std::strcpy(cache_[index].encoding, ret.c_str()); + } + + return ret; + } +}; + +} // namespace detail + +static std::unique_ptr accountIdCache; + +void +initAccountIdCache(std::size_t count) +{ + if (!accountIdCache && count != 0) + accountIdCache = std::make_unique(count); +} + std::string toBase58(AccountID const& v) { + if (accountIdCache) + return accountIdCache->toBase58(v); + return encodeBase58Token(TokenType::AccountID, v.data(), v.size()); } @@ -112,52 +190,4 @@ to_issuer(AccountID& issuer, std::string const& s) return true; } -//------------------------------------------------------------------------------ - -/* VFALCO NOTE - An alternate implementation could use a pair of insert-only - hash maps that each use a single large memory allocation - to store a fixed size hash table and all of the AccountID/string - pairs laid out in memory (wouldn't use std::string here just a - length prefixed or zero terminated array). Possibly using - boost::intrusive as the basis for the unordered container. - This would cut down to one allocate/free cycle per swap of - the map. -*/ - -AccountIDCache::AccountIDCache(std::size_t capacity) : capacity_(capacity) -{ - m1_.reserve(capacity_); -} - -std::string -AccountIDCache::toBase58(AccountID const& id) const -{ - std::lock_guard lock(mutex_); - auto iter = m1_.find(id); - if (iter != m1_.end()) - return iter->second; - iter = m0_.find(id); - std::string result; - if (iter != m0_.end()) - { - result = iter->second; - // Can use insert-only hash maps if - // we didn't erase from here. - m0_.erase(iter); - } - else - { - result = ripple::toBase58(id); - } - if (m1_.size() >= capacity_) - { - m0_ = std::move(m1_); - m1_.clear(); - m1_.reserve(capacity_); - } - m1_.emplace(id, result); - return result; -} - } // namespace ripple diff --git a/src/ripple/rpc/handlers/AccountChannels.cpp b/src/ripple/rpc/handlers/AccountChannels.cpp index cc79173e55..e5059d3ffc 100644 --- a/src/ripple/rpc/handlers/AccountChannels.cpp +++ b/src/ripple/rpc/handlers/AccountChannels.cpp @@ -202,7 +202,7 @@ doAccountChannels(RPC::JsonContext& context) to_string(*marker) + "," + std::to_string(nextHint); } - result[jss::account] = context.app.accountIDCache().toBase58(accountID); + result[jss::account] = toBase58(accountID); for (auto const& item : visitData.items) addChannel(jsonChannels, *item); diff --git a/src/ripple/rpc/handlers/AccountInfo.cpp b/src/ripple/rpc/handlers/AccountInfo.cpp index 417a3ffcd3..f5432dc65a 100644 --- a/src/ripple/rpc/handlers/AccountInfo.cpp +++ b/src/ripple/rpc/handlers/AccountInfo.cpp @@ -215,7 +215,7 @@ doAccountInfo(RPC::JsonContext& context) } else { - result[jss::account] = context.app.accountIDCache().toBase58(accountID); + result[jss::account] = toBase58(accountID); RPC::inject_error(rpcACT_NOT_FOUND, result); } diff --git a/src/ripple/rpc/handlers/AccountLines.cpp b/src/ripple/rpc/handlers/AccountLines.cpp index 843b9ddea5..364d40673f 100644 --- a/src/ripple/rpc/handlers/AccountLines.cpp +++ b/src/ripple/rpc/handlers/AccountLines.cpp @@ -252,7 +252,7 @@ doAccountLines(RPC::JsonContext& context) to_string(*marker) + "," + std::to_string(nextHint); } - result[jss::account] = context.app.accountIDCache().toBase58(accountID); + result[jss::account] = toBase58(accountID); for (auto const& item : visitData.items) addLine(jsonLines, item); diff --git a/src/ripple/rpc/handlers/AccountObjects.cpp b/src/ripple/rpc/handlers/AccountObjects.cpp index 4dcb3aba7d..6424c3afd3 100644 --- a/src/ripple/rpc/handlers/AccountObjects.cpp +++ b/src/ripple/rpc/handlers/AccountObjects.cpp @@ -160,7 +160,7 @@ doAccountNFTs(RPC::JsonContext& context) cp = nullptr; } - result[jss::account] = context.app.accountIDCache().toBase58(accountID); + result[jss::account] = toBase58(accountID); context.loadType = Resource::feeMediumBurdenRPC; return result; } @@ -275,7 +275,7 @@ doAccountObjects(RPC::JsonContext& context) result[jss::account_objects] = Json::arrayValue; } - result[jss::account] = context.app.accountIDCache().toBase58(accountID); + result[jss::account] = toBase58(accountID); context.loadType = Resource::feeMediumBurdenRPC; return result; } diff --git a/src/ripple/rpc/handlers/AccountOffers.cpp b/src/ripple/rpc/handlers/AccountOffers.cpp index d317875630..e957fe8a8e 100644 --- a/src/ripple/rpc/handlers/AccountOffers.cpp +++ b/src/ripple/rpc/handlers/AccountOffers.cpp @@ -77,7 +77,7 @@ doAccountOffers(RPC::JsonContext& context) } // Get info on account. - result[jss::account] = context.app.accountIDCache().toBase58(accountID); + result[jss::account] = toBase58(accountID); if (!ledger->exists(keylet::account(accountID))) return rpcError(rpcACT_NOT_FOUND); diff --git a/src/ripple/rpc/handlers/GatewayBalances.cpp b/src/ripple/rpc/handlers/GatewayBalances.cpp index 825a74ab84..d0770f31ed 100644 --- a/src/ripple/rpc/handlers/GatewayBalances.cpp +++ b/src/ripple/rpc/handlers/GatewayBalances.cpp @@ -80,7 +80,7 @@ doGatewayBalances(RPC::JsonContext& context) context.loadType = Resource::feeHighBurdenRPC; - result[jss::account] = context.app.accountIDCache().toBase58(accountID); + result[jss::account] = toBase58(accountID); // Parse the specified hotwallet(s), if any std::set hotWallets; diff --git a/src/ripple/rpc/handlers/NFTOffers.cpp b/src/ripple/rpc/handlers/NFTOffers.cpp index 34bbc8446b..69a090e27e 100644 --- a/src/ripple/rpc/handlers/NFTOffers.cpp +++ b/src/ripple/rpc/handlers/NFTOffers.cpp @@ -41,12 +41,10 @@ appendNftOfferJson( obj[jss::nft_offer_index] = to_string(offer->key()); obj[jss::flags] = (*offer)[sfFlags]; - obj[jss::owner] = - app.accountIDCache().toBase58(offer->getAccountID(sfOwner)); + obj[jss::owner] = toBase58(offer->getAccountID(sfOwner)); if (offer->isFieldPresent(sfDestination)) - obj[jss::destination] = - app.accountIDCache().toBase58(offer->getAccountID(sfDestination)); + obj[jss::destination] = toBase58(offer->getAccountID(sfDestination)); if (offer->isFieldPresent(sfExpiration)) obj[jss::expiration] = offer->getFieldU32(sfExpiration); diff --git a/src/ripple/rpc/handlers/NoRippleCheck.cpp b/src/ripple/rpc/handlers/NoRippleCheck.cpp index 2a6ab7ca4e..a2af9845fd 100644 --- a/src/ripple/rpc/handlers/NoRippleCheck.cpp +++ b/src/ripple/rpc/handlers/NoRippleCheck.cpp @@ -40,7 +40,7 @@ fillTransaction( ReadView const& ledger) { txArray["Sequence"] = Json::UInt(sequence++); - txArray["Account"] = context.app.accountIDCache().toBase58(accountID); + txArray["Account"] = toBase58(accountID); auto& fees = ledger.fees(); // Convert the reference transaction cost in fee units to drops // scaled to represent the current fee load.