Skip to content

Commit 6055f3a

Browse files
committed
checkqueue: Use buckets for batch validation to prevent lock contention
1 parent 59f6ea0 commit 6055f3a

File tree

2 files changed

+21
-7
lines changed

2 files changed

+21
-7
lines changed

src/checkqueue.h

+20-6
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ class CCheckQueue
4848

4949
BatchSchnorrVerifier m_batch;
5050

51+
static constexpr size_t SIGNATURE_BUCKETS = 15; // Same as MAX_SCRIPTCHECK_THREADS
52+
std::array<std::vector<SchnorrSignatureToVerify>, SIGNATURE_BUCKETS + 1 /* Add one for master thread */> m_signature_buckets;
53+
54+
void AddSignaturesToBucket(const std::vector<SchnorrSignatureToVerify>& signatures, size_t thread_idx) {
55+
assert(thread_idx >= 0 && thread_idx < m_signature_buckets.size());
56+
57+
auto& bucket = m_signature_buckets[thread_idx];
58+
bucket.insert(bucket.end(), signatures.begin(), signatures.end());
59+
}
60+
5161
//! Mutex to protect the inner state
5262
Mutex m_mutex;
5363

@@ -84,7 +94,7 @@ class CCheckQueue
8494
bool m_request_stop GUARDED_BY(m_mutex){false};
8595

8696
/** Internal function that does bulk of the verification work. If fMaster, return the final result. */
87-
std::optional<R> Loop(bool fMaster) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
97+
std::optional<R> Loop(bool fMaster, size_t thread_idx) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
8898
{
8999
std::condition_variable& cond = fMaster ? m_master_cv : m_worker_cv;
90100
std::vector<T> vChecks;
@@ -114,6 +124,12 @@ class CCheckQueue
114124
if (fMaster && nTodo == 0) {
115125
// All checks are done; master performs batch verification
116126
if constexpr (std::is_same_v<R, ScriptFailureResult>) {
127+
for (auto& bucket : m_signature_buckets) {
128+
for (const auto& sig : bucket) {
129+
m_batch.Add(sig.sig, sig.pubkey, sig.sighash);
130+
}
131+
bucket.clear();
132+
}
117133
if (!m_batch.Verify()) {
118134
m_result = ScriptFailureResult(SCRIPT_ERR_BATCH_VALIDATION_FAILED, "Schnorr batch validation failed");
119135
}
@@ -159,9 +175,7 @@ class CCheckQueue
159175
}
160176
// Check succeeded; add signatures to shared batch
161177
const auto& signatures = std::get<std::vector<SchnorrSignatureToVerify>>(check_result);
162-
for (const auto& sig : signatures) {
163-
m_batch.Add(sig.sig, sig.pubkey, sig.sighash);
164-
}
178+
AddSignaturesToBucket(signatures, thread_idx);
165179
} else {
166180
local_result = check_result;
167181
if (local_result.has_value()) break;
@@ -185,7 +199,7 @@ class CCheckQueue
185199
for (int n = 0; n < worker_threads_num; ++n) {
186200
m_worker_threads.emplace_back([this, n]() {
187201
util::ThreadRename(strprintf("scriptch.%i", n));
188-
Loop(false /* worker thread */);
202+
Loop(false /* worker thread */, n /* index */);
189203
});
190204
}
191205
}
@@ -201,7 +215,7 @@ class CCheckQueue
201215
//! its error.
202216
std::optional<R> Complete() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
203217
{
204-
return Loop(true /* master thread */);
218+
return Loop(true /* master thread */, SIGNATURE_BUCKETS /* take the last bucket */);
205219
}
206220

207221
//! Add a batch of checks to the queue

src/test/transaction_tests.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ BOOST_AUTO_TEST_CASE(test_big_witness_transaction)
567567

568568
// check all inputs concurrently, with the cache
569569
PrecomputedTransactionData txdata(tx);
570-
CCheckQueue<CScriptCheck, ScriptFailureResult> scriptcheckqueue(/*batch_size=*/128, /*worker_threads_num=*/20);
570+
CCheckQueue<CScriptCheck, ScriptFailureResult> scriptcheckqueue(/*batch_size=*/128, /*worker_threads_num=*/15);
571571
CCheckQueueControl<CScriptCheck, ScriptFailureResult> control(&scriptcheckqueue);
572572

573573
std::vector<Coin> coins;

0 commit comments

Comments
 (0)