optimization: look for NULL prevouts in the sorted values

For the 2 input case we simply check them both, like we did with equality.

For the general case, we take advantage of sorting, making invalid value detection constant time instead of linear in the worst case.

> 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
|--------------------:|--------------------:|--------:|----------:|:----------
|          179,971.00 |            5,556.45 |    0.3% |     11.02 | `CheckBlockBench`

|               ns/op |                op/s |    err% |     total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
|          963,177.98 |            1,038.23 |    1.7% |     10.92 | `DuplicateInputs`
|            9,410.90 |          106,259.75 |    0.3% |     11.01 | `ProcessTransactionBench`

> C++ compiler .......................... GNU 13.3.0

|            ns/block |             block/s |    err% |       ins/block |       cyc/block |    IPC |      bra/block |   miss% |     total | benchmark
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|          834,855.94 |            1,197.81 |    0.0% |    6,518,548.86 |    2,656,039.78 |  2.454 |     919,160.84 |    1.5% |     10.78 | `CheckBlockBench`

|               ns/op |                op/s |    err% |          ins/op |          cyc/op |    IPC |         bra/op |   miss% |     total | benchmark
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|        4,261,492.75 |              234.66 |    0.0% |   17,379,823.40 |   13,559,793.33 |  1.282 |   4,265,714.28 |    3.4% |     11.00 | `DuplicateInputs`
|           55,819.53 |           17,914.88 |    0.1% |      227,828.15 |      177,520.09 |  1.283 |      15,184.36 |    0.4% |     10.91 | `ProcessTransactionBench`
This commit is contained in:
Lőrinc 2025-01-25 14:24:02 +01:00
parent ce6840f701
commit 9f601c0cab

View File

@ -44,25 +44,27 @@ bool CheckTransaction(const CTransaction& tx, TxValidationState& state)
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-cb-length");
}
}
} else if (tx.vin.size() == 2) {
if (tx.vin[0].prevout == tx.vin[1].prevout) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate");
}
if (tx.vin[0].prevout.IsNull() || tx.vin[1].prevout.IsNull()) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-prevout-null");
}
} else {
if (tx.vin.size() == 2) {
if (tx.vin[0].prevout == tx.vin[1].prevout) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-inputs-duplicate");
}
} else {
std::vector<COutPoint> sortedPrevouts;
sortedPrevouts.reserve(tx.vin.size());
for (const auto& txin : tx.vin) {
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");
}
std::vector<COutPoint> sortedPrevouts;
sortedPrevouts.reserve(tx.vin.size());
for (const auto& txin : tx.vin) {
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");
}
for (const auto& txin : tx.vin) {
if (txin.prevout.IsNull()) {
for (const auto& in : sortedPrevouts) {
if (!in.hash.IsNull()) break; // invalid values can only be at the beginning
if (in.IsNull()) {
return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-prevout-null");
}
}