Optimize SHAMapItem and leverage new slab allocator: (#4218)

The `SHAMapItem` class contains a variable-sized buffer that
holds the serialized data associated with a particular item
inside a `SHAMap`.

Prior to this commit, the buffer for the serialized data was
allocated separately. Coupled with the fact that most instances
of `SHAMapItem` were wrapped around a `std::shared_ptr` meant
that an instantiation might result in up to three separate
memory allocations.

This commit switches away from `std::shared_ptr` for `SHAMapItem`
and uses `boost::intrusive_ptr` instead, allowing the reference
count for an instance to live inside the instance itself. Coupled
with using a slab-based allocator to optimize memory allocation
for the most commonly sized buffers, the net result is significant
memory savings. In testing, the reduction in memory usage hovers
between 400MB and 650MB. Other scenarios might result in larger
savings.

In performance testing with NFTs, this commit reduces memory size by
about 15% sustained over long duration.

Commit 2 of 3 in #4218.
This commit is contained in:
Nik Bougalis
2023-03-31 21:42:07 -07:00
committed by Elliot Lee
parent b7f588b789
commit c3acbce82d
36 changed files with 293 additions and 178 deletions

View File

@@ -181,7 +181,7 @@ RCLConsensus::Adaptor::share(RCLCxTx const& tx)
if (app_.getHashRouter().shouldRelay(tx.id()))
{
JLOG(j_.debug()) << "Relaying disputed tx " << tx.id();
auto const slice = tx.tx_.slice();
auto const slice = tx.tx_->slice();
protocol::TMTransaction msg;
msg.set_rawtransaction(slice.data(), slice.size());
msg.set_status(protocol::tsNEW);
@@ -325,7 +325,7 @@ RCLConsensus::Adaptor::onClose(
tx.first->add(s);
initialSet->addItem(
SHAMapNodeType::tnTRANSACTION_NM,
SHAMapItem(tx.first->getTransactionID(), s.slice()));
make_shamapitem(tx.first->getTransactionID(), s.slice()));
}
// Add pseudo-transactions to the set
@@ -369,7 +369,8 @@ RCLConsensus::Adaptor::onClose(
RCLCensorshipDetector<TxID, LedgerIndex>::TxIDSeqVec proposed;
initialSet->visitLeaves(
[&proposed, seq](std::shared_ptr<SHAMapItem const> const& item) {
[&proposed,
seq](boost::intrusive_ptr<SHAMapItem const> const& item) {
proposed.emplace_back(item->key(), seq);
});
@@ -529,7 +530,7 @@ RCLConsensus::Adaptor::doAccept(
std::vector<TxID> accepted;
result.txns.map_->visitLeaves(
[&accepted](std::shared_ptr<SHAMapItem const> const& item) {
[&accepted](boost::intrusive_ptr<SHAMapItem const> const& item) {
accepted.push_back(item->key());
});
@@ -604,7 +605,7 @@ RCLConsensus::Adaptor::doAccept(
<< "Test applying disputed transaction that did"
<< " not get in " << dispute.tx().id();
SerialIter sit(dispute.tx().tx_.slice());
SerialIter sit(dispute.tx().tx_->slice());
auto txn = std::make_shared<STTx const>(sit);
// Disputed pseudo-transactions that were not accepted

View File

@@ -42,7 +42,7 @@ public:
@param txn The transaction to wrap
*/
RCLCxTx(SHAMapItem const& txn) : tx_{txn}
RCLCxTx(boost::intrusive_ptr<SHAMapItem const> txn) : tx_(std::move(txn))
{
}
@@ -50,11 +50,11 @@ public:
ID const&
id() const
{
return tx_.key();
return tx_->key();
}
//! The SHAMapItem that represents the transaction.
SHAMapItem const tx_;
boost::intrusive_ptr<SHAMapItem const> tx_;
};
/** Represents a set of transactions in RCLConsensus.
@@ -90,8 +90,7 @@ public:
bool
insert(Tx const& t)
{
return map_->addItem(
SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{t.tx_});
return map_->addItem(SHAMapNodeType::tnTRANSACTION_NM, t.tx_);
}
/** Remove a transaction from the set.
@@ -145,7 +144,7 @@ public:
code uses the shared_ptr semantics to know whether the find
was successful and properly creates a Tx as needed.
*/
std::shared_ptr<const SHAMapItem> const&
boost::intrusive_ptr<SHAMapItem const> const&
find(Tx::ID const& entry) const
{
return map_->peekItem(entry);

View File

@@ -119,9 +119,8 @@ public:
sles_type::value_type
dereference() const override
{
auto const item = *iter_;
SerialIter sit(item.slice());
return std::make_shared<SLE const>(sit, item.key());
SerialIter sit(iter_->slice());
return std::make_shared<SLE const>(sit, iter_->key());
}
};
@@ -168,7 +167,7 @@ public:
txs_type::value_type
dereference() const override
{
auto const item = *iter_;
auto const& item = *iter_;
if (metadata_)
return deserializeTxPlusMeta(item);
return {deserializeTx(item), nullptr};
@@ -404,8 +403,8 @@ bool
Ledger::addSLE(SLE const& sle)
{
auto const s = sle.getSerializer();
SHAMapItem item(sle.key(), s.slice());
return stateMap_->addItem(SHAMapNodeType::tnACCOUNT_STATE, std::move(item));
return stateMap_->addItem(
SHAMapNodeType::tnACCOUNT_STATE, make_shamapitem(sle.key(), s.slice()));
}
//------------------------------------------------------------------------------
@@ -565,7 +564,7 @@ Ledger::rawInsert(std::shared_ptr<SLE> const& sle)
sle->add(ss);
if (!stateMap_->addGiveItem(
SHAMapNodeType::tnACCOUNT_STATE,
std::make_shared<SHAMapItem const>(sle->key(), ss.slice())))
make_shamapitem(sle->key(), ss.slice())))
LogicError("Ledger::rawInsert: key already exists");
}
@@ -576,7 +575,7 @@ Ledger::rawReplace(std::shared_ptr<SLE> const& sle)
sle->add(ss);
if (!stateMap_->updateGiveItem(
SHAMapNodeType::tnACCOUNT_STATE,
std::make_shared<SHAMapItem const>(sle->key(), ss.slice())))
make_shamapitem(sle->key(), ss.slice())))
LogicError("Ledger::rawReplace: key not found");
}
@@ -593,8 +592,7 @@ Ledger::rawTxInsert(
s.addVL(txn->peekData());
s.addVL(metaData->peekData());
if (!txMap().addGiveItem(
SHAMapNodeType::tnTRANSACTION_MD,
std::make_shared<SHAMapItem const>(key, s.slice())))
SHAMapNodeType::tnTRANSACTION_MD, make_shamapitem(key, s.slice())))
LogicError("duplicate_tx: " + to_string(key));
}
@@ -610,7 +608,7 @@ Ledger::rawTxInsertWithHash(
Serializer s(txn->getDataLength() + metaData->getDataLength() + 16);
s.addVL(txn->peekData());
s.addVL(metaData->peekData());
auto item = std::make_shared<SHAMapItem const>(key, s.slice());
auto item = make_shamapitem(key, s.slice());
auto hash = sha512Half(HashPrefix::txNode, item->slice(), item->key());
if (!txMap().addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, std::move(item)))
LogicError("duplicate_tx: " + to_string(key));

View File

@@ -105,7 +105,7 @@ public:
void
gotSkipList(
LedgerInfo const& info,
std::shared_ptr<SHAMapItem const> const& data);
boost::intrusive_ptr<SHAMapItem const> const& data);
/**
* Process a ledger delta (extracted from a TMReplayDeltaResponse message)

View File

@@ -68,7 +68,7 @@ public:
std::shared_ptr<STTx const>
fetch(
std::shared_ptr<SHAMapItem> const& item,
boost::intrusive_ptr<SHAMapItem> const& item,
SHAMapNodeType type,
std::uint32_t uCommitLedger);

View File

@@ -163,15 +163,15 @@ LedgerReplayMsgHandler::processProofPathResponse(
JLOG(journal_.debug()) << "Bad message: Cannot deserialize";
return false;
}
auto item = static_cast<SHAMapLeafNode*>(node.get())->peekItem();
if (!item)
if (auto item = static_cast<SHAMapLeafNode*>(node.get())->peekItem())
{
JLOG(journal_.debug()) << "Bad message: Cannot get ShaMapItem";
return false;
replayer_.gotSkipList(info, item);
return true;
}
replayer_.gotSkipList(info, item);
return true;
JLOG(journal_.debug()) << "Bad message: Cannot get ShaMapItem";
return false;
}
protocol::TMReplayDeltaResponse
@@ -206,9 +206,10 @@ LedgerReplayMsgHandler::processReplayDeltaRequest(
reply.set_ledgerheader(nData.getDataPtr(), nData.getLength());
// pack transactions
auto const& txMap = ledger->txMap();
txMap.visitLeaves([&](std::shared_ptr<SHAMapItem const> const& txNode) {
reply.add_transaction(txNode->data(), txNode->size());
});
txMap.visitLeaves(
[&](boost::intrusive_ptr<SHAMapItem const> const& txNode) {
reply.add_transaction(txNode->data(), txNode->size());
});
JLOG(journal_.debug()) << "getReplayDelta for ledger " << ledgerHash
<< " txMap hash " << txMap.getHash().as_uint256();
@@ -264,10 +265,9 @@ LedgerReplayMsgHandler::processReplayDeltaResponse(
STObject meta(metaSit, sfMetadata);
orderedTxns.emplace(meta[sfTransactionIndex], std::move(tx));
auto item =
std::make_shared<SHAMapItem const>(tid, shaMapItemData.slice());
if (!item ||
!txMap.addGiveItem(SHAMapNodeType::tnTRANSACTION_MD, item))
if (!txMap.addGiveItem(
SHAMapNodeType::tnTRANSACTION_MD,
make_shamapitem(tid, shaMapItemData.slice())))
{
JLOG(journal_.debug()) << "Bad message: Cannot deserialize";
return false;

View File

@@ -172,7 +172,7 @@ LedgerReplayer::createDeltas(std::shared_ptr<LedgerReplayTask> task)
void
LedgerReplayer::gotSkipList(
LedgerInfo const& info,
std::shared_ptr<SHAMapItem const> const& item)
boost::intrusive_ptr<SHAMapItem const> const& item)
{
std::shared_ptr<SkipListAcquire> skipList = {};
{

View File

@@ -137,7 +137,7 @@ SkipListAcquire::pmDowncast()
void
SkipListAcquire::processData(
std::uint32_t ledgerSeq,
std::shared_ptr<SHAMapItem const> const& item)
boost::intrusive_ptr<SHAMapItem const> const& item)
{
assert(ledgerSeq != 0 && item);
ScopedLockType sl(mtx_);

View File

@@ -96,7 +96,7 @@ public:
void
processData(
std::uint32_t ledgerSeq,
std::shared_ptr<SHAMapItem const> const& item);
boost::intrusive_ptr<SHAMapItem const> const& item);
/**
* Add a callback that will be called when the skipList is ready or failed.

View File

@@ -107,7 +107,7 @@ TransactionMaster::fetch(
std::shared_ptr<STTx const>
TransactionMaster::fetch(
std::shared_ptr<SHAMapItem> const& item,
boost::intrusive_ptr<SHAMapItem> const& item,
SHAMapNodeType type,
std::uint32_t uCommitLedger)
{

View File

@@ -172,8 +172,7 @@ public:
initialPosition->addGiveItem(
SHAMapNodeType::tnTRANSACTION_NM,
std::make_shared<SHAMapItem>(
amendTx.getTransactionID(), s.slice()));
make_shamapitem(amendTx.getTransactionID(), s.slice()));
}
}
};

View File

@@ -326,7 +326,7 @@ FeeVoteImpl::doVoting(
if (!initialPosition->addGiveItem(
SHAMapNodeType::tnTRANSACTION_NM,
std::make_shared<SHAMapItem>(txID, s.slice())))
make_shamapitem(txID, s.slice())))
{
JLOG(journal_.warn()) << "Ledger already had fee change";
}

View File

@@ -20,6 +20,7 @@
#include <ripple/app/consensus/RCLValidations.h>
#include <ripple/app/ledger/Ledger.h>
#include <ripple/app/misc/NegativeUNLVote.h>
#include <ripple/shamap/SHAMapItem.h>
namespace ripple {
@@ -115,12 +116,11 @@ NegativeUNLVote::addTx(
obj.setFieldVL(sfUNLModifyValidator, vp.slice());
});
uint256 txID = negUnlTx.getTransactionID();
Serializer s;
negUnlTx.add(s);
if (!initialSet->addGiveItem(
SHAMapNodeType::tnTRANSACTION_NM,
std::make_shared<SHAMapItem>(txID, s.slice())))
make_shamapitem(negUnlTx.getTransactionID(), s.slice())))
{
JLOG(j_.warn()) << "N-UNL: ledger seq=" << seq
<< ", add ttUNL_MODIFY tx failed";
@@ -128,8 +128,8 @@ NegativeUNLVote::addTx(
else
{
JLOG(j_.debug()) << "N-UNL: ledger seq=" << seq
<< ", add a ttUNL_MODIFY Tx with txID: " << txID
<< ", the validator to "
<< ", add a ttUNL_MODIFY Tx with txID: "
<< negUnlTx.getTransactionID() << ", the validator to "
<< (modify == ToDisable ? "disable: " : "re-enable: ")
<< vp;
}

View File

@@ -1643,7 +1643,7 @@ Consensus<Adaptor>::createDisputes(TxSet_t const& o)
(inThisSet && result_->txns.find(txId) && !o.find(txId)) ||
(!inThisSet && !result_->txns.find(txId) && o.find(txId)));
Tx_t tx = inThisSet ? *result_->txns.find(txId) : *o.find(txId);
Tx_t tx = inThisSet ? result_->txns.find(txId) : o.find(txId);
auto txID = tx.id();
if (result_->disputes.find(txID) != result_->disputes.end())

View File

@@ -195,7 +195,7 @@ three database entries upon completion.
Since downloads execute serially by design, the entries in this table always
correspond to the contents of a single file.
| Bytes | Size | Part |
| Bytes | size | Part |
|:------:|:----------:|:----:|
| 0x... | 2147483647 | 0 |
| 0x... | 2147483647 | 1 |

View File

@@ -22,7 +22,7 @@ uint64 Appnum Application defined constant
uint16 KeySize Key size in bytes
uint64 Salt A random seed
uint64 Pepper The salt hashed
uint16 BlockSize Size of a file block in bytes
uint16 BlockSize size of a file block in bytes
uint16 LoadFactor Target fraction in 65536ths
uint8[56] Reserved Zeroes
uint8[] Reserved Zero-pad to block size
@@ -160,4 +160,3 @@ Iteration 0: RIPEMD160[nudb.dat] = FAE6AE84C15968B0419FDFC014931EA12A396C71
Iteration 1: RIPEMD160[nudb.key] = F96BF2722AB2EE009FFAE4A36AAFC4F220E21951
Iteration 1: RIPEMD160[nudb.dat] = FAE6AE84C15968B0419FDFC014931EA12A396C71
```

View File

@@ -1,4 +1,4 @@
# Shard Size Tuning
# Shard size Tuning
The purpose of this document is to compare the sizes of shards containing
varying amounts of ledgers.

View File

@@ -38,7 +38,7 @@ DecodedBlob::DecodedBlob(void const* key, void const* value, int valueBytes)
m_success = false;
m_key = key;
// VFALCO NOTE Ledger indexes should have started at 1
// VFALCO NOTE Ledger indexes should hav e started at 1
m_objectType = hotUNKNOWN;
m_objectData = nullptr;
m_dataBytes = std::max(0, valueBytes - 9);

View File

@@ -120,8 +120,8 @@ public:
static inline constexpr unsigned int leafDepth = 64;
using DeltaItem = std::pair<
std::shared_ptr<SHAMapItem const>,
std::shared_ptr<SHAMapItem const>>;
boost::intrusive_ptr<SHAMapItem const>,
boost::intrusive_ptr<SHAMapItem const>>;
using Delta = std::map<uint256, DeltaItem>;
SHAMap(SHAMap const&) = delete;
@@ -190,23 +190,27 @@ public:
delItem(uint256 const& id);
bool
addItem(SHAMapNodeType type, SHAMapItem&& i);
addItem(SHAMapNodeType type, boost::intrusive_ptr<SHAMapItem const> item);
SHAMapHash
getHash() const;
// save a copy if you have a temporary anyway
bool
updateGiveItem(SHAMapNodeType type, std::shared_ptr<SHAMapItem const>);
updateGiveItem(
SHAMapNodeType type,
boost::intrusive_ptr<SHAMapItem const> item);
bool
addGiveItem(SHAMapNodeType type, std::shared_ptr<SHAMapItem const> item);
addGiveItem(
SHAMapNodeType type,
boost::intrusive_ptr<SHAMapItem const> item);
// Save a copy if you need to extend the life
// of the SHAMapItem beyond this SHAMap
std::shared_ptr<SHAMapItem const> const&
boost::intrusive_ptr<SHAMapItem const> const&
peekItem(uint256 const& id) const;
std::shared_ptr<SHAMapItem const> const&
boost::intrusive_ptr<SHAMapItem const> const&
peekItem(uint256 const& id, SHAMapHash& hash) const;
// traverse functions
@@ -253,8 +257,8 @@ public:
*/
void
visitLeaves(
std::function<void(std::shared_ptr<SHAMapItem const> const&)> const&)
const;
std::function<
void(boost::intrusive_ptr<SHAMapItem const> const&)> const&) const;
// comparison/sync functions
@@ -361,8 +365,8 @@ private:
using SharedPtrNodeStack =
std::stack<std::pair<std::shared_ptr<SHAMapTreeNode>, SHAMapNodeID>>;
using DeltaRef = std::pair<
std::shared_ptr<SHAMapItem const> const&,
std::shared_ptr<SHAMapItem const> const&>;
boost::intrusive_ptr<SHAMapItem const>,
boost::intrusive_ptr<SHAMapItem const>>;
// tree node cache operations
std::shared_ptr<SHAMapTreeNode>
@@ -475,7 +479,7 @@ private:
descendNoStore(std::shared_ptr<SHAMapInnerNode> const&, int branch) const;
/** If there is only one leaf below this node, get its contents */
std::shared_ptr<SHAMapItem const> const&
boost::intrusive_ptr<SHAMapItem const> const&
onlyBelow(SHAMapTreeNode*) const;
bool
@@ -490,7 +494,7 @@ private:
bool
walkBranch(
SHAMapTreeNode* node,
std::shared_ptr<SHAMapItem const> const& otherMapItem,
boost::intrusive_ptr<SHAMapItem const> const& otherMapItem,
bool isFirstMap,
Delta& differences,
int& maxCount) const;

View File

@@ -36,7 +36,7 @@ class SHAMapAccountStateLeafNode final
{
public:
SHAMapAccountStateLeafNode(
std::shared_ptr<SHAMapItem const> item,
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t cowid)
: SHAMapLeafNode(std::move(item), cowid)
{
@@ -44,7 +44,7 @@ public:
}
SHAMapAccountStateLeafNode(
std::shared_ptr<SHAMapItem const> item,
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t cowid,
SHAMapHash const& hash)
: SHAMapLeafNode(std::move(item), cowid, hash)

View File

@@ -20,26 +20,68 @@
#ifndef RIPPLE_SHAMAP_SHAMAPITEM_H_INCLUDED
#define RIPPLE_SHAMAP_SHAMAPITEM_H_INCLUDED
#include <ripple/basics/Buffer.h>
#include <ripple/basics/ByteUtilities.h>
#include <ripple/basics/CountedObject.h>
#include <ripple/basics/SlabAllocator.h>
#include <ripple/basics/Slice.h>
#include <ripple/basics/base_uint.h>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <cassert>
namespace ripple {
// an item stored in a SHAMap
class SHAMapItem : public CountedObject<SHAMapItem>
{
// These are used to support boost::intrusive_ptr reference counting
// These functions are used internally by boost::intrusive_ptr to handle
// lifetime management.
friend void
intrusive_ptr_add_ref(SHAMapItem const* x);
friend void
intrusive_ptr_release(SHAMapItem const* x);
// This is the interface for creating new instances of this class.
friend boost::intrusive_ptr<SHAMapItem>
make_shamapitem(uint256 const& tag, Slice data);
private:
uint256 tag_;
Buffer data_;
uint256 const tag_;
// We use std::uint32_t to minimize the size; there's no SHAMapItem whose
// size exceeds 4GB and there won't ever be (famous last words?), so this
// is safe.
std::uint32_t const size_;
// This is the reference count used to support boost::intrusive_ptr
mutable std::atomic<std::uint32_t> refcount_ = 1;
// Because of the unusual way in which SHAMapItem objects are constructed
// the only way to properly create one is to first allocate enough memory
// so we limit this constructor to codepaths that do this right and limit
// arbitrary construction.
SHAMapItem(uint256 const& tag, Slice data)
: tag_(tag), size_(static_cast<std::uint32_t>(data.size()))
{
std::memcpy(
reinterpret_cast<std::uint8_t*>(this) + sizeof(*this),
data.data(),
data.size());
}
public:
SHAMapItem() = delete;
SHAMapItem(uint256 const& tag, Slice data) : tag_(tag), data_(data)
{
}
SHAMapItem(SHAMapItem const& other) = delete;
SHAMapItem&
operator=(SHAMapItem const& other) = delete;
SHAMapItem(SHAMapItem&& other) = delete;
SHAMapItem&
operator=(SHAMapItem&&) = delete;
uint256 const&
key() const
@@ -47,25 +89,101 @@ public:
return tag_;
}
Slice
slice() const
{
return static_cast<Slice>(data_);
}
std::size_t
size() const
{
return data_.size();
return size_;
}
void const*
data() const
{
return data_.data();
return reinterpret_cast<std::uint8_t const*>(this) + sizeof(*this);
}
Slice
slice() const
{
return {data(), size()};
}
};
namespace detail {
// clang-format off
// The slab cutoffs and the number of megabytes per allocation are customized
// based on the number of objects of each size we expect to need at any point
// in time and with an eye to minimize the number of slack bytes in a block.
inline SlabAllocatorSet<SHAMapItem> slabber({
{ 128, megabytes(std::size_t(60)) },
{ 192, megabytes(std::size_t(46)) },
{ 272, megabytes(std::size_t(60)) },
{ 384, megabytes(std::size_t(56)) },
{ 564, megabytes(std::size_t(40)) },
{ 772, megabytes(std::size_t(46)) },
{ 1052, megabytes(std::size_t(60)) },
});
// clang-format on
} // namespace detail
inline void
intrusive_ptr_add_ref(SHAMapItem const* x)
{
// This can only happen if someone releases the last reference to the
// item while we were trying to increment the refcount.
if (x->refcount_++ == 0)
LogicError("SHAMapItem: the reference count is 0!");
}
inline void
intrusive_ptr_release(SHAMapItem const* x)
{
if (--x->refcount_ == 0)
{
auto p = reinterpret_cast<std::uint8_t const*>(x);
// The SHAMapItem constuctor isn't trivial (because the destructor
// for CountedObject isn't) so we can't avoid calling it here, but
// plan for a future where we might not need to.
if constexpr (!std::is_trivially_destructible_v<SHAMapItem>)
std::destroy_at(x);
// If the slabber doens't claim this pointer, it was allocated
// manually, so we free it manually.
if (!detail::slabber.deallocate(const_cast<std::uint8_t*>(p)))
delete[] p;
}
}
inline boost::intrusive_ptr<SHAMapItem>
make_shamapitem(uint256 const& tag, Slice data)
{
assert(data.size() <= megabytes<std::size_t>(16));
std::uint8_t* raw = detail::slabber.allocate(data.size());
// If we can't grab memory from the slab allocators, we fall back to
// the standard library and try to grab a precisely-sized memory block:
if (raw == nullptr)
raw = new std::uint8_t[sizeof(SHAMapItem) + data.size()];
// We do not increment the reference count here on purpose: the
// constructor of SHAMapItem explicitly sets it to 1. We use the fact
// that the refcount can never be zero before incrementing as an
// invariant.
return {new (raw) SHAMapItem{tag, data}, false};
}
static_assert(alignof(SHAMapItem) != 40);
static_assert(alignof(SHAMapItem) == 8 || alignof(SHAMapItem) == 4);
inline boost::intrusive_ptr<SHAMapItem>
make_shamapitem(SHAMapItem const& other)
{
return make_shamapitem(other.key(), other.slice());
}
} // namespace ripple
#endif

View File

@@ -32,11 +32,14 @@ namespace ripple {
class SHAMapLeafNode : public SHAMapTreeNode
{
protected:
std::shared_ptr<SHAMapItem const> item_;
boost::intrusive_ptr<SHAMapItem const> item_;
SHAMapLeafNode(std::shared_ptr<SHAMapItem const> item, std::uint32_t cowid);
SHAMapLeafNode(
std::shared_ptr<SHAMapItem const> item,
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t cowid);
SHAMapLeafNode(
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t cowid,
SHAMapHash const& hash);
@@ -61,7 +64,7 @@ public:
invariants(bool is_root = false) const final override;
public:
std::shared_ptr<SHAMapItem const> const&
boost::intrusive_ptr<SHAMapItem const> const&
peekItem() const;
/** Set the item that this node points to and update the node's hash.
@@ -71,7 +74,7 @@ public:
hash was unchanged); true otherwise.
*/
bool
setItem(std::shared_ptr<SHAMapItem const> i);
setItem(boost::intrusive_ptr<SHAMapItem const> i);
std::string
getString(SHAMapNodeID const&) const final override;

View File

@@ -35,7 +35,7 @@ class SHAMapTxLeafNode final : public SHAMapLeafNode,
{
public:
SHAMapTxLeafNode(
std::shared_ptr<SHAMapItem const> item,
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t cowid)
: SHAMapLeafNode(std::move(item), cowid)
{
@@ -43,7 +43,7 @@ public:
}
SHAMapTxLeafNode(
std::shared_ptr<SHAMapItem const> item,
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t cowid,
SHAMapHash const& hash)
: SHAMapLeafNode(std::move(item), cowid, hash)

View File

@@ -36,7 +36,7 @@ class SHAMapTxPlusMetaLeafNode final
{
public:
SHAMapTxPlusMetaLeafNode(
std::shared_ptr<SHAMapItem const> item,
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t cowid)
: SHAMapLeafNode(std::move(item), cowid)
{
@@ -44,7 +44,7 @@ public:
}
SHAMapTxPlusMetaLeafNode(
std::shared_ptr<SHAMapItem const> item,
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t cowid,
SHAMapHash const& hash)
: SHAMapLeafNode(std::move(item), cowid, hash)

View File

@@ -30,7 +30,7 @@ namespace ripple {
[[nodiscard]] std::shared_ptr<SHAMapLeafNode>
makeTypedLeaf(
SHAMapNodeType type,
std::shared_ptr<SHAMapItem const> item,
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t owner)
{
if (type == SHAMapNodeType::tnTRANSACTION_NM)
@@ -512,9 +512,9 @@ SHAMap::firstBelow(
return belowHelper(node, stack, branch, {init, cmp, incr});
}
static const std::shared_ptr<SHAMapItem const> no_item;
static const boost::intrusive_ptr<SHAMapItem const> no_item;
std::shared_ptr<SHAMapItem const> const&
boost::intrusive_ptr<SHAMapItem const> const&
SHAMap::onlyBelow(SHAMapTreeNode* node) const
{
// If there is only one item below this node, return it
@@ -593,7 +593,7 @@ SHAMap::peekNextItem(uint256 const& id, SharedPtrNodeStack& stack) const
return nullptr;
}
std::shared_ptr<SHAMapItem const> const&
boost::intrusive_ptr<SHAMapItem const> const&
SHAMap::peekItem(uint256 const& id) const
{
SHAMapLeafNode* leaf = findKey(id);
@@ -604,7 +604,7 @@ SHAMap::peekItem(uint256 const& id) const
return leaf->peekItem();
}
std::shared_ptr<SHAMapItem const> const&
boost::intrusive_ptr<SHAMapItem const> const&
SHAMap::peekItem(uint256 const& id, SHAMapHash& hash) const
{
SHAMapLeafNode* leaf = findKey(id);
@@ -776,7 +776,9 @@ SHAMap::delItem(uint256 const& id)
}
bool
SHAMap::addGiveItem(SHAMapNodeType type, std::shared_ptr<SHAMapItem const> item)
SHAMap::addGiveItem(
SHAMapNodeType type,
boost::intrusive_ptr<SHAMapItem const> item)
{
assert(state_ != SHAMapState::Immutable);
assert(type != SHAMapNodeType::tnINNER);
@@ -813,7 +815,7 @@ SHAMap::addGiveItem(SHAMapNodeType type, std::shared_ptr<SHAMapItem const> item)
// this is a leaf node that has to be made an inner node holding two
// items
auto leaf = std::static_pointer_cast<SHAMapLeafNode>(node);
std::shared_ptr<SHAMapItem const> otherItem = leaf->peekItem();
auto otherItem = leaf->peekItem();
assert(otherItem && (tag != otherItem->key()));
node = std::make_shared<SHAMapInnerNode>(node->cowid());
@@ -844,9 +846,11 @@ SHAMap::addGiveItem(SHAMapNodeType type, std::shared_ptr<SHAMapItem const> item)
}
bool
SHAMap::addItem(SHAMapNodeType type, SHAMapItem&& i)
SHAMap::addItem(
SHAMapNodeType type,
boost::intrusive_ptr<SHAMapItem const> item)
{
return addGiveItem(type, std::make_shared<SHAMapItem const>(std::move(i)));
return addGiveItem(type, std::move(item));
}
SHAMapHash
@@ -864,7 +868,7 @@ SHAMap::getHash() const
bool
SHAMap::updateGiveItem(
SHAMapNodeType type,
std::shared_ptr<SHAMapItem const> item)
boost::intrusive_ptr<SHAMapItem const> item)
{
// can't change the tag but can change the hash
uint256 tag = item->key();
@@ -889,13 +893,13 @@ SHAMap::updateGiveItem(
if (node->getType() != type)
{
JLOG(journal_.fatal()) << "SHAMap::setItem: cross-type change!";
JLOG(journal_.fatal()) << "SHAMap::updateGiveItem: cross-type change!";
return false;
}
node = unshareNode(std::move(node), nodeID);
if (node->setItem(std::move(item)))
if (node->setItem(item))
dirtyUp(stack, tag, node);
return true;

View File

@@ -37,7 +37,7 @@ namespace ripple {
bool
SHAMap::walkBranch(
SHAMapTreeNode* node,
std::shared_ptr<SHAMapItem const> const& otherMapItem,
boost::intrusive_ptr<SHAMapItem const> const& otherMapItem,
bool isFirstMap,
Delta& differences,
int& maxCount) const
@@ -71,13 +71,11 @@ SHAMap::walkBranch(
{
// unmatched
if (isFirstMap)
differences.insert(std::make_pair(
item->key(),
DeltaRef(item, std::shared_ptr<SHAMapItem const>())));
differences.insert(
std::make_pair(item->key(), DeltaRef(item, nullptr)));
else
differences.insert(std::make_pair(
item->key(),
DeltaRef(std::shared_ptr<SHAMapItem const>(), item)));
differences.insert(
std::make_pair(item->key(), DeltaRef(nullptr, item)));
if (--maxCount <= 0)
return false;
@@ -110,12 +108,10 @@ SHAMap::walkBranch(
// otherMapItem was unmatched, must add
if (isFirstMap) // this is first map, so other item is from second
differences.insert(std::make_pair(
otherMapItem->key(),
DeltaRef(std::shared_ptr<SHAMapItem const>(), otherMapItem)));
otherMapItem->key(), DeltaRef(nullptr, otherMapItem)));
else
differences.insert(std::make_pair(
otherMapItem->key(),
DeltaRef(otherMapItem, std::shared_ptr<SHAMapItem const>())));
otherMapItem->key(), DeltaRef(otherMapItem, nullptr)));
if (--maxCount <= 0)
return false;
@@ -173,17 +169,13 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const
{
differences.insert(std::make_pair(
ours->peekItem()->key(),
DeltaRef(
ours->peekItem(),
std::shared_ptr<SHAMapItem const>())));
DeltaRef(ours->peekItem(), nullptr)));
if (--maxCount <= 0)
return false;
differences.insert(std::make_pair(
other->peekItem()->key(),
DeltaRef(
std::shared_ptr<SHAMapItem const>(),
other->peekItem())));
DeltaRef(nullptr, other->peekItem())));
if (--maxCount <= 0)
return false;
}
@@ -216,11 +208,7 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const
// We have a branch, the other tree does not
SHAMapTreeNode* iNode = descendThrow(ours, i);
if (!walkBranch(
iNode,
std::shared_ptr<SHAMapItem const>(),
true,
differences,
maxCount))
iNode, nullptr, true, differences, maxCount))
return false;
}
else if (ours->isEmptyBranch(i))
@@ -228,11 +216,7 @@ SHAMap::compare(SHAMap const& otherMap, Delta& differences, int maxCount) const
// The other tree has a branch, we do not
SHAMapTreeNode* iNode = otherMap.descendThrow(other, i);
if (!otherMap.walkBranch(
iNode,
std::shared_ptr<SHAMapItem const>(),
false,
differences,
maxCount))
iNode, nullptr, false, differences, maxCount))
return false;
}
else // The two trees have different non-empty branches

View File

@@ -24,7 +24,7 @@
namespace ripple {
SHAMapLeafNode::SHAMapLeafNode(
std::shared_ptr<SHAMapItem const> item,
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t cowid)
: SHAMapTreeNode(cowid), item_(std::move(item))
{
@@ -32,7 +32,7 @@ SHAMapLeafNode::SHAMapLeafNode(
}
SHAMapLeafNode::SHAMapLeafNode(
std::shared_ptr<SHAMapItem const> item,
boost::intrusive_ptr<SHAMapItem const> item,
std::uint32_t cowid,
SHAMapHash const& hash)
: SHAMapTreeNode(cowid, hash), item_(std::move(item))
@@ -40,17 +40,17 @@ SHAMapLeafNode::SHAMapLeafNode(
assert(item_->size() >= 12);
}
std::shared_ptr<SHAMapItem const> const&
boost::intrusive_ptr<SHAMapItem const> const&
SHAMapLeafNode::peekItem() const
{
return item_;
}
bool
SHAMapLeafNode::setItem(std::shared_ptr<SHAMapItem const> i)
SHAMapLeafNode::setItem(boost::intrusive_ptr<SHAMapItem const> item)
{
assert(cowid_ != 0);
item_ = std::move(i);
item_ = std::move(item);
auto const oldHash = hash_;

View File

@@ -25,8 +25,8 @@ namespace ripple {
void
SHAMap::visitLeaves(
std::function<void(std::shared_ptr<SHAMapItem const> const& item)> const&
leafFunction) const
std::function<void(boost::intrusive_ptr<SHAMapItem const> const&
item)> const& leafFunction) const
{
visitNodes([&leafFunction](SHAMapTreeNode& node) {
if (!node.isInner())

View File

@@ -42,8 +42,8 @@ SHAMapTreeNode::makeTransaction(
SHAMapHash const& hash,
bool hashValid)
{
auto item = std::make_shared<SHAMapItem const>(
sha512Half(HashPrefix::transactionID, data), data);
auto item =
make_shamapitem(sha512Half(HashPrefix::transactionID, data), data);
if (hashValid)
return std::make_shared<SHAMapTxLeafNode>(std::move(item), 0, hash);
@@ -71,7 +71,7 @@ SHAMapTreeNode::makeTransactionWithMeta(
s.chop(tag.bytes);
auto item = std::make_shared<SHAMapItem const>(tag, s.slice());
auto item = make_shamapitem(tag, s.slice());
if (hashValid)
return std::make_shared<SHAMapTxPlusMetaLeafNode>(
@@ -103,7 +103,7 @@ SHAMapTreeNode::makeAccountState(
if (tag.isZero())
Throw<std::runtime_error>("Invalid AS node");
auto item = std::make_shared<SHAMapItem const>(tag, s.slice());
auto item = make_shamapitem(tag, s.slice());
if (hashValid)
return std::make_shared<SHAMapAccountStateLeafNode>(

View File

@@ -1292,8 +1292,8 @@ struct LedgerReplayer_test : public beast::unit_test::suite
std::uint8_t payload[55] = {
0x6A, 0x09, 0xE6, 0x67, 0xF3, 0xBC, 0xC9, 0x08, 0xB2};
auto item = std::make_shared<SHAMapItem>(
uint256(12345), Slice(payload, sizeof(payload)));
auto item =
make_shamapitem(uint256(12345), Slice(payload, sizeof(payload)));
skipList->processData(l->seq(), item);
std::vector<TaskStatus> deltaStatuses;

View File

@@ -78,7 +78,7 @@ class ByzantineFailureSim_test : public beast::unit_test::suite
// All peers see some TX 0
for (Peer* peer : network)
{
peer->submit(Tx(0));
peer->submit(Tx{0});
// Peers 0,1,2,6 will close the next ledger differently by injecting
// a non-consensus approved transaciton
if (byzantineNodes.contains(peer))

View File

@@ -25,6 +25,7 @@
#include <map>
#include <ostream>
#include <string>
#include <type_traits>
namespace ripple {
namespace test {
@@ -40,6 +41,11 @@ public:
{
}
template <typename T, typename = std::enable_if_t<std::is_same_v<T, Tx>>>
Tx(T const* t) : id_{t->id_}
{
}
ID
id() const
{

View File

@@ -348,7 +348,7 @@ struct LedgerHistoryHelper
assert(seen.emplace(s.back()).second);
Ledger const& parent = (*this)[s.substr(0, s.size() - 1)];
return ledgers.emplace(s, oracle.accept(parent, ++nextTx))
return ledgers.emplace(s, oracle.accept(parent, Tx{++nextTx}))
.first->second;
}
};

View File

@@ -85,13 +85,13 @@ public:
beast::Journal mJournal;
};
std::shared_ptr<Item>
boost::intrusive_ptr<Item>
make_random_item(beast::xor_shift_engine& r)
{
Serializer s;
for (int d = 0; d < 3; ++d)
s.add32(ripple::rand_int<std::uint32_t>(r));
return std::make_shared<Item>(s.getSHA512Half(), s.slice());
return make_shamapitem(s.getSHA512Half(), s.slice());
}
void
@@ -99,9 +99,8 @@ public:
{
while (n--)
{
std::shared_ptr<SHAMapItem> item(make_random_item(r));
auto const result(
t.addItem(SHAMapNodeType::tnACCOUNT_STATE, std::move(*item)));
auto const result(t.addItem(
SHAMapNodeType::tnACCOUNT_STATE, make_random_item(r)));
assert(result);
(void)result;
}

View File

@@ -34,14 +34,14 @@ class SHAMapSync_test : public beast::unit_test::suite
public:
beast::xor_shift_engine eng_;
std::shared_ptr<SHAMapItem>
boost::intrusive_ptr<SHAMapItem>
makeRandomAS()
{
Serializer s;
for (int d = 0; d < 3; ++d)
s.add32(rand_int<std::uint32_t>(eng_));
return std::make_shared<SHAMapItem>(s.getSHA512Half(), s.slice());
return make_shamapitem(s.getSHA512Half(), s.slice());
}
bool
@@ -55,10 +55,10 @@ public:
for (int i = 0; i < count; ++i)
{
std::shared_ptr<SHAMapItem> item = makeRandomAS();
auto item = makeRandomAS();
items.push_back(item->key());
if (!map.addItem(SHAMapNodeType::tnACCOUNT_STATE, std::move(*item)))
if (!map.addItem(SHAMapNodeType::tnACCOUNT_STATE, item))
{
log << "Unable to add item to map\n";
return false;
@@ -97,8 +97,7 @@ public:
int items = 10000;
for (int i = 0; i < items; ++i)
{
source.addItem(
SHAMapNodeType::tnACCOUNT_STATE, std::move(*makeRandomAS()));
source.addItem(SHAMapNodeType::tnACCOUNT_STATE, makeRandomAS());
if (i % 100 == 0)
source.invariants();
}

View File

@@ -22,7 +22,6 @@
#include <ripple/beast/unit_test.h>
#include <ripple/beast/utility/Journal.h>
#include <ripple/shamap/SHAMap.h>
#include <algorithm>
#include <test/shamap/common.h>
#include <test/unit_test/SuiteJournal.h>
@@ -45,10 +44,7 @@ static_assert(std::is_move_assignable<SHAMap::const_iterator>{}, "");
static_assert(std::is_nothrow_destructible<SHAMapItem>{}, "");
static_assert(!std::is_default_constructible<SHAMapItem>{}, "");
static_assert(std::is_copy_constructible<SHAMapItem>{}, "");
static_assert(std::is_copy_assignable<SHAMapItem>{}, "");
static_assert(std::is_move_constructible<SHAMapItem>{}, "");
static_assert(std::is_move_assignable<SHAMapItem>{}, "");
static_assert(!std::is_copy_constructible<SHAMapItem>{}, "");
static_assert(std::is_nothrow_destructible<SHAMapNodeID>{}, "");
static_assert(std::is_default_constructible<SHAMapNodeID>{}, "");
@@ -155,37 +151,43 @@ public:
if (!backed)
sMap.setUnbacked();
SHAMapItem i1(h1, IntToVUC(1)), i2(h2, IntToVUC(2)),
i3(h3, IntToVUC(3)), i4(h4, IntToVUC(4)), i5(h5, IntToVUC(5));
auto i1 = make_shamapitem(h1, IntToVUC(1));
auto i2 = make_shamapitem(h2, IntToVUC(2));
auto i3 = make_shamapitem(h3, IntToVUC(3));
auto i4 = make_shamapitem(h4, IntToVUC(4));
auto i5 = make_shamapitem(h5, IntToVUC(5));
unexpected(
!sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{i2}),
!sMap.addItem(
SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(*i2)),
"no add");
sMap.invariants();
unexpected(
!sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{i1}),
!sMap.addItem(
SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(*i1)),
"no add");
sMap.invariants();
auto i = sMap.begin();
auto e = sMap.end();
unexpected(i == e || (*i != i1), "bad traverse");
unexpected(i == e || (*i != *i1), "bad traverse");
++i;
unexpected(i == e || (*i != i2), "bad traverse");
unexpected(i == e || (*i != *i2), "bad traverse");
++i;
unexpected(i != e, "bad traverse");
sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{i4});
sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(*i4));
sMap.invariants();
sMap.delItem(i2.key());
sMap.delItem(i2->key());
sMap.invariants();
sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, SHAMapItem{i3});
sMap.addItem(SHAMapNodeType::tnTRANSACTION_NM, make_shamapitem(*i3));
sMap.invariants();
i = sMap.begin();
e = sMap.end();
unexpected(i == e || (*i != i1), "bad traverse");
unexpected(i == e || (*i != *i1), "bad traverse");
++i;
unexpected(i == e || (*i != i3), "bad traverse");
unexpected(i == e || (*i != *i3), "bad traverse");
++i;
unexpected(i == e || (*i != i4), "bad traverse");
unexpected(i == e || (*i != *i4), "bad traverse");
++i;
unexpected(i != e, "bad traverse");
@@ -265,9 +267,9 @@ public:
BEAST_EXPECT(map.getHash() == beast::zero);
for (int k = 0; k < keys.size(); ++k)
{
SHAMapItem item(keys[k], IntToVUC(k));
BEAST_EXPECT(map.addItem(
SHAMapNodeType::tnTRANSACTION_NM, std::move(item)));
SHAMapNodeType::tnTRANSACTION_NM,
make_shamapitem(keys[k], IntToVUC(k))));
BEAST_EXPECT(map.getHash().as_uint256() == hashes[k]);
map.invariants();
}
@@ -312,7 +314,7 @@ public:
{
map.addItem(
SHAMapNodeType::tnTRANSACTION_NM,
SHAMapItem{k, IntToVUC(0)});
make_shamapitem(k, IntToVUC(0)));
map.invariants();
}
@@ -346,7 +348,7 @@ class SHAMapPathProof_test : public beast::unit_test::suite
uint256 k(c);
map.addItem(
SHAMapNodeType::tnACCOUNT_STATE,
SHAMapItem{k, Slice{k.data(), k.size()}});
make_shamapitem(k, Slice{k.data(), k.size()}));
map.invariants();
auto root = map.getHash().as_uint256();