optimization: replace tree with sorted vector

A pre-sized vector retains locality (enabling SIMD operations), speeding up sorting and equality checks.
It's also simpler (therefore more reliable) than a sorted set. It also causes less memory fragmentation.

> cmake -B build -DBUILD_BENCH=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build -j$(nproc) && build/src/bench/bench_bitcoin -filter='CheckBlockBench|DuplicateInputs|ProcessTransactionBench' -min-time=10000

> C++ compiler .......................... AppleClang 16.0.0.16000026

|            ns/block |             block/s |    err% |     total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
|          181,922.54 |            5,496.85 |    0.2% |     10.98 | `CheckBlockBench`

|               ns/op |                op/s |    err% |     total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
|          997,739.30 |            1,002.27 |    1.0% |     10.94 | `DuplicateInputs`
|            9,449.28 |          105,828.15 |    0.3% |     10.99 | `ProcessTransactionBench`

Co-authored-by: Pieter Wuille <pieter@wuille.net>
This commit is contained in:
Lőrinc 2025-01-18 11:54:47 +01:00
parent 765b71b90b
commit cb8c012b87

View file

@ -50,11 +50,14 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate");
}
} else {
std::set<COutPoint> vInOutPoints;
std::vector<COutPoint> sortedPrevouts;
sortedPrevouts.reserve(tx.vin.size());
for (const auto& txin : tx.vin) {
if (!vInOutPoints.insert(txin.prevout).second) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate");
}
sortedPrevouts.push_back(txin.prevout);
}
std::sort(sortedPrevouts.begin(), sortedPrevouts.end());
if (std::ranges::adjacent_find(sortedPrevouts) != sortedPrevouts.end()) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate");
}
}