Skip to content

Commit 18ab1f9

Browse files
committedFeb 6, 2025
feat: group P3 & banking into 50ms batches
1 parent 2123fa0 commit 18ab1f9

File tree

17 files changed

+235
-25
lines changed

17 files changed

+235
-25
lines changed
 

‎banking-bench/src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ fn main() {
485485
false,
486486
HashSet::default(),
487487
BundleAccountLocker::default(),
488+
Duration::from_millis(50),
488489
);
489490

490491
// This is so that the signal_receiver does not go out of scope after the closure.

‎core/src/banking_simulation.rs

+1
Original file line numberDiff line numberDiff line change
@@ -819,6 +819,7 @@ impl BankingSimulator {
819819
false,
820820
collections::HashSet::default(),
821821
BundleAccountLocker::default(),
822+
Duration::from_millis(50),
822823
);
823824

824825
let (&_slot, &raw_base_event_time) = freeze_time_by_slot

‎core/src/banking_stage.rs

+12
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ pub(crate) mod transaction_scheduler;
8282
// Fixed thread size seems to be fastest on GCP setup
8383
pub const NUM_THREADS: u32 = 6;
8484

85+
pub const DEFAULT_BATCH_INTERVAL: Duration = Duration::from_millis(50);
86+
8587
const TOTAL_BUFFERED_PACKETS: usize = 100_000;
8688

8789
const NUM_VOTE_PROCESSING_THREADS: u32 = 2;
@@ -365,6 +367,7 @@ impl BankingStage {
365367
enable_forwarding: bool,
366368
blacklisted_accounts: HashSet<Pubkey>,
367369
bundle_account_locker: BundleAccountLocker,
370+
batch_interval: Duration,
368371
) -> Self {
369372
Self::new_num_threads(
370373
block_production_method,
@@ -383,6 +386,7 @@ impl BankingStage {
383386
enable_forwarding,
384387
blacklisted_accounts,
385388
bundle_account_locker,
389+
batch_interval,
386390
)
387391
}
388392

@@ -404,6 +408,7 @@ impl BankingStage {
404408
enable_forwarding: bool,
405409
blacklisted_accounts: HashSet<Pubkey>,
406410
bundle_account_locker: BundleAccountLocker,
411+
batch_interval: Duration,
407412
) -> Self {
408413
match block_production_method {
409414
BlockProductionMethod::ThreadLocalMultiIterator => {
@@ -440,6 +445,7 @@ impl BankingStage {
440445
enable_forwarding,
441446
blacklisted_accounts,
442447
bundle_account_locker,
448+
batch_interval,
443449
),
444450
}
445451
}
@@ -551,6 +557,7 @@ impl BankingStage {
551557
enable_forwarding: bool,
552558
blacklisted_accounts: HashSet<Pubkey>,
553559
bundle_account_locker: BundleAccountLocker,
560+
batch_interval: Duration,
554561
) -> Self {
555562
assert!(num_threads >= MIN_TOTAL_THREADS);
556563
// Single thread to generate entries from many banks.
@@ -659,6 +666,7 @@ impl BankingStage {
659666
scheduler,
660667
worker_metrics,
661668
forwarder,
669+
batch_interval,
662670
);
663671
Builder::new()
664672
.name("solBnkTxSched".to_string())
@@ -939,6 +947,7 @@ mod tests {
939947
false,
940948
HashSet::default(),
941949
BundleAccountLocker::default(),
950+
Duration::from_millis(50),
942951
);
943952
drop(non_vote_sender);
944953
drop(tpu_vote_sender);
@@ -997,6 +1006,7 @@ mod tests {
9971006
false,
9981007
HashSet::default(),
9991008
BundleAccountLocker::default(),
1009+
Duration::from_millis(50),
10001010
);
10011011
trace!("sending bank");
10021012
drop(non_vote_sender);
@@ -1084,6 +1094,7 @@ mod tests {
10841094
false,
10851095
HashSet::default(),
10861096
BundleAccountLocker::default(),
1097+
Duration::from_millis(50),
10871098
);
10881099

10891100
// fund another account so we can send 2 good transactions in a single batch.
@@ -1464,6 +1475,7 @@ mod tests {
14641475
false,
14651476
HashSet::default(),
14661477
BundleAccountLocker::default(),
1478+
Duration::from_millis(50),
14671479
);
14681480

14691481
let keypairs = (0..100).map(|_| Keypair::new()).collect_vec();

‎core/src/banking_stage/read_write_account_set.rs

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ mod tests {
140140
VersionedTransaction::try_new(message, &[write_keypair]).unwrap(),
141141
MessageHash::Compute,
142142
Some(false),
143+
false,
143144
bank,
144145
bank.get_reserved_account_keys(),
145146
)

‎core/src/banking_stage/transaction_scheduler/scheduler_controller.rs

+52-8
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ pub(crate) struct SchedulerController<T: LikeClusterInfo> {
7171
worker_metrics: Vec<Arc<ConsumeWorkerMetrics>>,
7272
/// State for forwarding packets to the leader, if enabled.
7373
forwarder: Option<Forwarder<T>>,
74+
batch: Vec<ImmutableDeserializedPacket>,
75+
batch_start: Instant,
76+
batch_interval: Duration,
7477
}
7578

7679
impl<T: LikeClusterInfo> SchedulerController<T> {
@@ -81,6 +84,7 @@ impl<T: LikeClusterInfo> SchedulerController<T> {
8184
scheduler: GreedyScheduler,
8285
worker_metrics: Vec<Arc<ConsumeWorkerMetrics>>,
8386
forwarder: Option<Forwarder<T>>,
87+
batch_interval: Duration,
8488
) -> Self {
8589
Self {
8690
decision_maker,
@@ -94,6 +98,10 @@ impl<T: LikeClusterInfo> SchedulerController<T> {
9498
timing_metrics: SchedulerTimingMetrics::default(),
9599
worker_metrics,
96100
forwarder,
101+
102+
batch: Vec::default(),
103+
batch_start: Instant::now(),
104+
batch_interval,
97105
}
98106
}
99107

@@ -123,6 +131,7 @@ impl<T: LikeClusterInfo> SchedulerController<T> {
123131
.maybe_report_and_reset_slot(new_leader_slot);
124132

125133
self.receive_completed()?;
134+
self.maybe_queue_batch();
126135
self.process_transactions(&decision)?;
127136
if !self.receive_and_buffer_packets(&decision) {
128137
break;
@@ -146,6 +155,23 @@ impl<T: LikeClusterInfo> SchedulerController<T> {
146155
Ok(())
147156
}
148157

158+
fn maybe_queue_batch(&mut self) {
159+
// Early return if the current batch is empty.
160+
if self.batch.is_empty() {
161+
return;
162+
}
163+
164+
if self.batch_start.elapsed() > self.batch_interval {
165+
Self::buffer_packets(
166+
&self.bank_forks,
167+
&mut self.container,
168+
&mut self.transaction_id_generator,
169+
&mut self.count_metrics,
170+
self.batch.drain(..),
171+
);
172+
}
173+
}
174+
149175
/// Process packets based on decision.
150176
fn process_transactions(
151177
&mut self,
@@ -474,9 +500,11 @@ impl<T: LikeClusterInfo> SchedulerController<T> {
474500
});
475501

476502
if should_buffer {
477-
let (_, buffer_time_us) = measure_us!(
478-
self.buffer_packets(receive_packet_results.deserialized_packets)
479-
);
503+
// Packets are not immediately schedulable but are instead
504+
// grouped into 50ms batches (measured from the recv time of
505+
// the first packet).
506+
let (_, buffer_time_us) =
507+
measure_us!(self.extend_batch(receive_packet_results.deserialized_packets));
480508
self.timing_metrics.update(|timing_metrics| {
481509
saturating_add_assign!(timing_metrics.buffer_time_us, buffer_time_us);
482510
});
@@ -496,12 +524,27 @@ impl<T: LikeClusterInfo> SchedulerController<T> {
496524
true
497525
}
498526

499-
fn buffer_packets(&mut self, packets: Vec<ImmutableDeserializedPacket>) {
527+
fn extend_batch(&mut self, packets: Vec<ImmutableDeserializedPacket>) {
528+
// If this is the first packet in the batch, set the
529+
// start timestamp for the batch.
530+
if self.batch.is_empty() {
531+
self.batch_start = Instant::now();
532+
}
533+
self.batch.extend(packets);
534+
}
535+
536+
fn buffer_packets(
537+
bank_forks: &Arc<RwLock<BankForks>>,
538+
container: &mut TransactionStateContainer,
539+
transaction_id_generator: &mut TransactionIdGenerator,
540+
count_metrics: &mut SchedulerCountMetrics,
541+
packets: impl Iterator<Item = ImmutableDeserializedPacket>,
542+
) {
500543
// Convert to Arcs
501544
let packets: Vec<_> = packets.into_iter().map(Arc::new).collect();
502545
// Sanitize packets, generate IDs, and insert into the container.
503546
let (root_bank, working_bank) = {
504-
let bank_forks = self.bank_forks.read().unwrap();
547+
let bank_forks = bank_forks.read().unwrap();
505548
let root_bank = bank_forks.root_bank();
506549
let working_bank = bank_forks.working_bank();
507550
(root_bank, working_bank)
@@ -586,7 +629,7 @@ impl<T: LikeClusterInfo> SchedulerController<T> {
586629
.filter(|(_, check_result)| check_result.is_ok())
587630
{
588631
saturating_add_assign!(post_transaction_check_count, 1);
589-
let transaction_id = self.transaction_id_generator.next();
632+
let transaction_id = transaction_id_generator.next();
590633

591634
let (priority, cost) = Self::calculate_priority_and_cost(
592635
&transaction,
@@ -598,7 +641,7 @@ impl<T: LikeClusterInfo> SchedulerController<T> {
598641
max_age,
599642
};
600643

601-
if self.container.insert_new_transaction(
644+
if container.insert_new_transaction(
602645
transaction_id,
603646
transaction_ttl,
604647
packet,
@@ -617,7 +660,7 @@ impl<T: LikeClusterInfo> SchedulerController<T> {
617660
let num_dropped_on_transaction_checks =
618661
post_lock_validation_count.saturating_sub(post_transaction_check_count);
619662

620-
self.count_metrics.update(|count_metrics| {
663+
count_metrics.update(|count_metrics| {
621664
saturating_add_assign!(
622665
count_metrics.num_dropped_on_capacity,
623666
num_dropped_on_capacity
@@ -809,6 +852,7 @@ mod tests {
809852
GreedyScheduler::new(consume_work_senders, finished_consume_work_receiver),
810853
vec![], // no actual workers with metrics to report, this can be empty
811854
None,
855+
Duration::from_millis(50),
812856
);
813857

814858
(test_frame, scheduler_controller)

‎core/src/bundle_stage/bundle_consumer.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ impl BundleConsumer {
224224
&locked_bundle,
225225
bank_start,
226226
bundle_stage_leader_metrics,
227-
Some(10u64.pow(6) / 10), // 0.1 lamports per CU
227+
Some(10u64.pow(6) / 1000), // 0.001 lamports per CU (filling a block at this rate pays ~1c).
228228
));
229229
bundle_stage_leader_metrics
230230
.leader_slot_metrics_tracker()
@@ -277,7 +277,7 @@ impl BundleConsumer {
277277
if bank_start.working_bank.slot() != *last_tip_updated_slot
278278
&& Self::bundle_touches_tip_pdas(
279279
locked_bundle.sanitized_bundle(),
280-
&tip_manager.get_tip_accounts(),
280+
tip_manager.get_tip_accounts(),
281281
)
282282
{
283283
let start = Instant::now();
@@ -482,6 +482,7 @@ impl BundleConsumer {
482482
))
483483
}
484484

485+
#[allow(clippy::too_many_arguments)]
485486
fn update_qos_and_execute_record_commit_bundle(
486487
committer: &Committer,
487488
recorder: &TransactionRecorder,
@@ -1139,6 +1140,7 @@ mod tests {
11391140
leader_schedule_cache,
11401141
TipManagerConfig {
11411142
funnel,
1143+
rewards_split: None,
11421144
tip_payment_program_id: Pubkey::from_str(
11431145
"T1pyyaTNZsKv2WcRAB8oVnk93mLJw2XzjtVYqCsaHqt",
11441146
)

0 commit comments

Comments
 (0)
Please sign in to comment.