mirror of
https://github.com/dashpay/dash.git
synced 2024-12-27 21:12:48 +01:00
Merge #12118: Sort mempool by min(feerate, ancestor_feerate)
0a22a52
Use mempool's ancestor sort in transaction selection (Suhas Daftuar)7abfa53
Add test for new ancestor feerate sort behavior (Suhas Daftuar)9a51319
Sort mempool by min(feerate, ancestor_feerate) (Suhas Daftuar)6773f92
Refactor CompareTxMemPoolEntryByDescendantScore (Suhas Daftuar) Pull request description: This more closely approximates the desirability of a given transaction for mining, and should result in less re-sorting when transactions get removed from the mempool after being mined. I measured this as approximately a 5% speedup in removeForBlock. Tree-SHA512: ffa36b567c5dfe3e8908c545a459b6a5ec0de26e7dc81b1050dd235cac9046564b4409a3f8c5ba97bd8b30526e8fec8f78480a912e317979467f32305c3dd37b
This commit is contained in:
parent
9cc5078218
commit
c4ffc620d0
@ -410,7 +410,7 @@ void BlockAssembler::addPackageTxs(int &nPackagesSelected, int &nDescendantsUpda
|
|||||||
// Try to compare the mapTx entry to the mapModifiedTx entry
|
// Try to compare the mapTx entry to the mapModifiedTx entry
|
||||||
iter = mempool.mapTx.project<0>(mi);
|
iter = mempool.mapTx.project<0>(mi);
|
||||||
if (modit != mapModifiedTx.get<ancestor_score>().end() &&
|
if (modit != mapModifiedTx.get<ancestor_score>().end() &&
|
||||||
CompareModifiedEntry()(*modit, CTxMemPoolModifiedEntry(iter))) {
|
CompareTxMemPoolEntryByAncestorFee()(*modit, CTxMemPoolModifiedEntry(iter))) {
|
||||||
// The best entry in mapModifiedTx has higher score
|
// The best entry in mapModifiedTx has higher score
|
||||||
// than the one from mapTx.
|
// than the one from mapTx.
|
||||||
// Switch which transaction (package) to consider
|
// Switch which transaction (package) to consider
|
||||||
|
23
src/miner.h
23
src/miner.h
@ -44,6 +44,12 @@ struct CTxMemPoolModifiedEntry {
|
|||||||
nSigOpCountWithAncestors = entry->GetSigOpCountWithAncestors();
|
nSigOpCountWithAncestors = entry->GetSigOpCountWithAncestors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t GetModifiedFee() const { return iter->GetModifiedFee(); }
|
||||||
|
uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; }
|
||||||
|
CAmount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
|
||||||
|
size_t GetTxSize() const { return iter->GetTxSize(); }
|
||||||
|
const CTransaction& GetTx() const { return iter->GetTx(); }
|
||||||
|
|
||||||
CTxMemPool::txiter iter;
|
CTxMemPool::txiter iter;
|
||||||
uint64_t nSizeWithAncestors;
|
uint64_t nSizeWithAncestors;
|
||||||
CAmount nModFeesWithAncestors;
|
CAmount nModFeesWithAncestors;
|
||||||
@ -70,21 +76,6 @@ struct modifiedentry_iter {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// This matches the calculation in CompareTxMemPoolEntryByAncestorFee,
|
|
||||||
// except operating on CTxMemPoolModifiedEntry.
|
|
||||||
// TODO: refactor to avoid duplication of this logic.
|
|
||||||
struct CompareModifiedEntry {
|
|
||||||
bool operator()(const CTxMemPoolModifiedEntry &a, const CTxMemPoolModifiedEntry &b) const
|
|
||||||
{
|
|
||||||
double f1 = (double)a.nModFeesWithAncestors * b.nSizeWithAncestors;
|
|
||||||
double f2 = (double)b.nModFeesWithAncestors * a.nSizeWithAncestors;
|
|
||||||
if (f1 == f2) {
|
|
||||||
return CTxMemPool::CompareIteratorByHash()(a.iter, b.iter);
|
|
||||||
}
|
|
||||||
return f1 > f2;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// A comparator that sorts transactions based on number of ancestors.
|
// A comparator that sorts transactions based on number of ancestors.
|
||||||
// This is sufficient to sort an ancestor package in an order that is valid
|
// This is sufficient to sort an ancestor package in an order that is valid
|
||||||
// to appear in a block.
|
// to appear in a block.
|
||||||
@ -109,7 +100,7 @@ typedef boost::multi_index_container<
|
|||||||
// Reuse same tag from CTxMemPool's similar index
|
// Reuse same tag from CTxMemPool's similar index
|
||||||
boost::multi_index::tag<ancestor_score>,
|
boost::multi_index::tag<ancestor_score>,
|
||||||
boost::multi_index::identity<CTxMemPoolModifiedEntry>,
|
boost::multi_index::identity<CTxMemPoolModifiedEntry>,
|
||||||
CompareModifiedEntry
|
CompareTxMemPoolEntryByAncestorFee
|
||||||
>
|
>
|
||||||
>
|
>
|
||||||
> indexed_modified_transaction_set;
|
> indexed_modified_transaction_set;
|
||||||
|
@ -397,6 +397,23 @@ BOOST_AUTO_TEST_CASE(MempoolAncestorIndexingTest)
|
|||||||
sortedOrder.erase(sortedOrder.end()-2);
|
sortedOrder.erase(sortedOrder.end()-2);
|
||||||
sortedOrder.insert(sortedOrder.begin(), tx7.GetHash().ToString());
|
sortedOrder.insert(sortedOrder.begin(), tx7.GetHash().ToString());
|
||||||
CheckSort<ancestor_score>(pool, sortedOrder);
|
CheckSort<ancestor_score>(pool, sortedOrder);
|
||||||
|
|
||||||
|
// High-fee parent, low-fee child
|
||||||
|
// tx7 -> tx8
|
||||||
|
CMutableTransaction tx8 = CMutableTransaction();
|
||||||
|
tx8.vin.resize(1);
|
||||||
|
tx8.vin[0].prevout = COutPoint(tx7.GetHash(), 0);
|
||||||
|
tx8.vin[0].scriptSig = CScript() << OP_11;
|
||||||
|
tx8.vout.resize(1);
|
||||||
|
tx8.vout[0].scriptPubKey = CScript() << OP_11 << OP_EQUAL;
|
||||||
|
tx8.vout[0].nValue = 10*COIN;
|
||||||
|
|
||||||
|
// Check that we sort by min(feerate, ancestor_feerate):
|
||||||
|
// set the fee so that the ancestor feerate is above tx1/5,
|
||||||
|
// but the transaction's own feerate is lower
|
||||||
|
pool.addUnchecked(tx8.GetHash(), entry.Fee(5000LL).FromTx(tx8));
|
||||||
|
sortedOrder.insert(sortedOrder.end()-1, tx8.GetHash().ToString());
|
||||||
|
CheckSort<ancestor_score>(pool, sortedOrder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -214,18 +214,14 @@ class CompareTxMemPoolEntryByDescendantScore
|
|||||||
public:
|
public:
|
||||||
bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const
|
bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const
|
||||||
{
|
{
|
||||||
bool fUseADescendants = UseDescendantScore(a);
|
double a_mod_fee, a_size, b_mod_fee, b_size;
|
||||||
bool fUseBDescendants = UseDescendantScore(b);
|
|
||||||
|
|
||||||
double aModFee = fUseADescendants ? a.GetModFeesWithDescendants() : a.GetModifiedFee();
|
GetModFeeAndSize(a, a_mod_fee, a_size);
|
||||||
double aSize = fUseADescendants ? a.GetSizeWithDescendants() : a.GetTxSize();
|
GetModFeeAndSize(b, b_mod_fee, b_size);
|
||||||
|
|
||||||
double bModFee = fUseBDescendants ? b.GetModFeesWithDescendants() : b.GetModifiedFee();
|
|
||||||
double bSize = fUseBDescendants ? b.GetSizeWithDescendants() : b.GetTxSize();
|
|
||||||
|
|
||||||
// Avoid division by rewriting (a/b > c/d) as (a*d > c*b).
|
// Avoid division by rewriting (a/b > c/d) as (a*d > c*b).
|
||||||
double f1 = aModFee * bSize;
|
double f1 = a_mod_fee * b_size;
|
||||||
double f2 = aSize * bModFee;
|
double f2 = a_size * b_mod_fee;
|
||||||
|
|
||||||
if (f1 == f2) {
|
if (f1 == f2) {
|
||||||
return a.GetTime() >= b.GetTime();
|
return a.GetTime() >= b.GetTime();
|
||||||
@ -233,12 +229,21 @@ public:
|
|||||||
return f1 < f2;
|
return f1 < f2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate which score to use for an entry (avoiding division).
|
// Return the fee/size we're using for sorting this entry.
|
||||||
bool UseDescendantScore(const CTxMemPoolEntry &a) const
|
void GetModFeeAndSize(const CTxMemPoolEntry &a, double &mod_fee, double &size) const
|
||||||
{
|
{
|
||||||
|
// Compare feerate with descendants to feerate of the transaction, and
|
||||||
|
// return the fee/size for the max.
|
||||||
double f1 = (double)a.GetModifiedFee() * a.GetSizeWithDescendants();
|
double f1 = (double)a.GetModifiedFee() * a.GetSizeWithDescendants();
|
||||||
double f2 = (double)a.GetModFeesWithDescendants() * a.GetTxSize();
|
double f2 = (double)a.GetModFeesWithDescendants() * a.GetTxSize();
|
||||||
return f2 > f1;
|
|
||||||
|
if (f2 > f1) {
|
||||||
|
mod_fee = a.GetModFeesWithDescendants();
|
||||||
|
size = a.GetSizeWithDescendants();
|
||||||
|
} else {
|
||||||
|
mod_fee = a.GetModifiedFee();
|
||||||
|
size = a.GetTxSize();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -269,27 +274,48 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** \class CompareTxMemPoolEntryByAncestorScore
|
||||||
|
*
|
||||||
|
* Sort an entry by min(score/size of entry's tx, score/size with all ancestors).
|
||||||
|
*/
|
||||||
class CompareTxMemPoolEntryByAncestorFee
|
class CompareTxMemPoolEntryByAncestorFee
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) const
|
template<typename T>
|
||||||
|
bool operator()(const T& a, const T& b) const
|
||||||
{
|
{
|
||||||
double aFees = a.GetModFeesWithAncestors();
|
double a_mod_fee, a_size, b_mod_fee, b_size;
|
||||||
double aSize = a.GetSizeWithAncestors();
|
|
||||||
|
|
||||||
double bFees = b.GetModFeesWithAncestors();
|
GetModFeeAndSize(a, a_mod_fee, a_size);
|
||||||
double bSize = b.GetSizeWithAncestors();
|
GetModFeeAndSize(b, b_mod_fee, b_size);
|
||||||
|
|
||||||
// Avoid division by rewriting (a/b > c/d) as (a*d > c*b).
|
// Avoid division by rewriting (a/b > c/d) as (a*d > c*b).
|
||||||
double f1 = aFees * bSize;
|
double f1 = a_mod_fee * b_size;
|
||||||
double f2 = aSize * bFees;
|
double f2 = a_size * b_mod_fee;
|
||||||
|
|
||||||
if (f1 == f2) {
|
if (f1 == f2) {
|
||||||
return a.GetTx().GetHash() < b.GetTx().GetHash();
|
return a.GetTx().GetHash() < b.GetTx().GetHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
return f1 > f2;
|
return f1 > f2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return the fee/size we're using for sorting this entry.
|
||||||
|
template <typename T>
|
||||||
|
void GetModFeeAndSize(const T &a, double &mod_fee, double &size) const
|
||||||
|
{
|
||||||
|
// Compare feerate with ancestors to feerate of the transaction, and
|
||||||
|
// return the fee/size for the min.
|
||||||
|
double f1 = (double)a.GetModifiedFee() * a.GetSizeWithAncestors();
|
||||||
|
double f2 = (double)a.GetModFeesWithAncestors() * a.GetTxSize();
|
||||||
|
|
||||||
|
if (f1 > f2) {
|
||||||
|
mod_fee = a.GetModFeesWithAncestors();
|
||||||
|
size = a.GetSizeWithAncestors();
|
||||||
|
} else {
|
||||||
|
mod_fee = a.GetModifiedFee();
|
||||||
|
size = a.GetTxSize();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Multi_index tag names
|
// Multi_index tag names
|
||||||
|
Loading…
Reference in New Issue
Block a user