1#[macro_use]
6extern crate eager;
7use core::fmt;
8use std::{
9 collections::HashMap,
10 num::Saturating,
11 ops::{Index, IndexMut},
12};
13
14use enum_iterator::Sequence;
15use rialo_s_pubkey::Pubkey;
16
17#[derive(Default, Debug, PartialEq, Eq)]
18pub struct ProgramTiming {
19 pub accumulated_us: Saturating<u64>,
20 pub accumulated_units: Saturating<u64>,
21 pub count: Saturating<u32>,
22 pub errored_txs_compute_consumed: Vec<u64>,
23 pub total_errored_units: Saturating<u64>,
25}
26
27impl ProgramTiming {
28 pub fn coalesce_error_timings(&mut self, current_estimated_program_cost: u64) {
29 for tx_error_compute_consumed in self.errored_txs_compute_consumed.drain(..) {
30 let compute_units_update =
31 std::cmp::max(current_estimated_program_cost, tx_error_compute_consumed);
32 self.accumulated_units += compute_units_update;
33 self.count += 1;
34 }
35 }
36
37 pub fn accumulate_program_timings(&mut self, other: &ProgramTiming) {
38 self.accumulated_us += other.accumulated_us;
39 self.accumulated_units += other.accumulated_units;
40 self.count += other.count;
41 self.errored_txs_compute_consumed
43 .extend(other.errored_txs_compute_consumed.clone());
44 self.total_errored_units += other.total_errored_units;
45 }
46}
47
48#[derive(Debug, Sequence)]
50pub enum ExecuteTimingType {
51 CheckUs,
52 ValidateFeesUs,
53 LoadUs,
54 ExecuteUs,
55 StoreUs,
56 UpdateStakesCacheUs,
57 UpdateExecutorsUs,
58 NumExecuteBatches,
59 CollectLogsUs,
60 TotalBatchesLen,
61 UpdateTransactionStatuses,
62 ProgramCacheUs,
63 CheckBlockLimitsUs,
64 FilterExecutableUs,
65 MatchingUs,
66 TxBatchesUs,
67 TriggeredTxLatencyUs,
68 TotalUs,
69}
70
71pub struct Metrics([Saturating<u64>; ExecuteTimingType::CARDINALITY]);
72
73impl Index<ExecuteTimingType> for Metrics {
74 type Output = Saturating<u64>;
75 fn index(&self, index: ExecuteTimingType) -> &Self::Output {
76 self.0.index(index as usize)
77 }
78}
79
80impl IndexMut<ExecuteTimingType> for Metrics {
81 fn index_mut(&mut self, index: ExecuteTimingType) -> &mut Self::Output {
82 self.0.index_mut(index as usize)
83 }
84}
85
86impl Default for Metrics {
87 fn default() -> Self {
88 Metrics([Saturating(0); ExecuteTimingType::CARDINALITY])
89 }
90}
91
92impl core::fmt::Debug for Metrics {
93 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
94 self.0.fmt(f)
95 }
96}
97
98eager_macro_rules! { $eager_1
102 #[macro_export]
103 macro_rules! report_execute_timings {
104 ($self: expr, $is_unified_scheduler_enabled: expr) => {
105 (
106 "validate_transactions_us",
107 $self
108 .metrics
109 .index(ExecuteTimingType::CheckUs).0,
110 i64
111 ),
112 (
113 "validate_fees_us",
114 $self
115 .metrics
116 .index(ExecuteTimingType::ValidateFeesUs).0,
117 i64
118 ),
119 (
120 "filter_executable_us",
121 $self
122 .metrics
123 .index(ExecuteTimingType::FilterExecutableUs).0,
124 i64
125 ),
126 (
127 "program_cache_us",
128 $self
129 .metrics
130 .index(ExecuteTimingType::ProgramCacheUs).0,
131 i64
132 ),
133 (
134 "load_us",
135 $self
136 .metrics
137 .index(ExecuteTimingType::LoadUs).0,
138 i64
139 ),
140 (
141 "execute_us",
142 $self
143 .metrics
144 .index(ExecuteTimingType::ExecuteUs).0,
145 i64
146 ),
147 (
148 "collect_logs_us",
149 $self
150 .metrics
151 .index(ExecuteTimingType::CollectLogsUs).0,
152 i64
153 ),
154 (
155 "store_us",
156 $self
157
158 .metrics
159 .index(ExecuteTimingType::StoreUs).0,
160 i64
161 ),
162 (
163 "update_stakes_cache_us",
164 $self
165
166 .metrics
167 .index(ExecuteTimingType::UpdateStakesCacheUs).0,
168 i64
169 ),
170 (
171 "execute_accessories_update_executors_us",
172 $self.metrics.index(ExecuteTimingType::UpdateExecutorsUs).0,
173 i64
174 ),
175 (
176 "total_batches_len",
177 (if $is_unified_scheduler_enabled {
178 None
179 } else {
180 Some($self
181 .metrics
182 .index(ExecuteTimingType::TotalBatchesLen).0)
183 }),
184 Option<i64>
185 ),
186 (
187 "num_execute_batches",
188 (if $is_unified_scheduler_enabled {
189 None
190 } else {
191 Some($self
192 .metrics
193 .index(ExecuteTimingType::NumExecuteBatches).0)
194 }),
195 Option<i64>
196 ),
197 (
198 "update_transaction_statuses",
199 $self
200 .metrics
201 .index(ExecuteTimingType::UpdateTransactionStatuses).0,
202 i64
203 ),
204 (
205 "check_block_limits_us",
206 $self.metrics.index(ExecuteTimingType::CheckBlockLimitsUs).0,
207 i64
208 ),
209 (
210 "execute_details_serialize_us",
211 $self.details.serialize_us.0,
212 i64
213 ),
214 (
215 "execute_details_create_vm_us",
216 $self.details.create_vm_us.0,
217 i64
218 ),
219 (
220 "execute_details_execute_inner_us",
221 $self.details.execute_us.0,
222 i64
223 ),
224 (
225 "execute_details_deserialize_us",
226 $self.details.deserialize_us.0,
227 i64
228 ),
229 (
230 "execute_details_get_or_create_executor_us",
231 $self.details.get_or_create_executor_us.0,
232 i64
233 ),
234 (
235 "execute_details_changed_account_count",
236 $self.details.changed_account_count.0,
237 i64
238 ),
239 (
240 "execute_details_total_account_count",
241 $self.details.total_account_count.0,
242 i64
243 ),
244 (
245 "execute_details_create_executor_register_syscalls_us",
246 $self
247 .details
248 .create_executor_register_syscalls_us.0,
249 i64
250 ),
251 (
252 "execute_details_create_executor_load_elf_us",
253 $self.details.create_executor_load_elf_us.0,
254 i64
255 ),
256 (
257 "execute_details_create_executor_verify_code_us",
258 $self.details.create_executor_verify_code_us.0,
259 i64
260 ),
261 (
262 "execute_details_create_executor_jit_compile_us",
263 $self.details.create_executor_jit_compile_us.0,
264 i64
265 ),
266 (
267 "execute_accessories_feature_set_clone_us",
268 $self
269 .execute_accessories
270 .feature_set_clone_us.0,
271 i64
272 ),
273 (
274 "execute_accessories_get_executors_us",
275 $self.execute_accessories.get_executors_us.0,
276 i64
277 ),
278 (
279 "execute_accessories_process_message_us",
280 $self.execute_accessories.process_message_us.0,
281 i64
282 ),
283 (
284 "execute_accessories_process_instructions_total_us",
285 $self
286 .execute_accessories
287 .process_instructions
288 .total_us.0,
289 i64
290 ),
291 (
292 "execute_accessories_process_instructions_verify_caller_us",
293 $self
294 .execute_accessories
295 .process_instructions
296 .verify_caller_us.0,
297 i64
298 ),
299 (
300 "execute_accessories_process_instructions_process_executable_chain_us",
301 $self
302 .execute_accessories
303 .process_instructions
304 .process_executable_chain_us.0,
305 i64
306 ),
307 (
308 "execute_accessories_process_instructions_verify_callee_us",
309 $self
310 .execute_accessories
311 .process_instructions
312 .verify_callee_us.0,
313 i64
314 ),
315 }
316 }
317}
318
319#[derive(Debug, Default)]
320pub struct ExecuteTimings {
321 pub metrics: Metrics,
322 pub details: ExecuteDetailsTimings,
323 pub execute_accessories: ExecuteAccessoryTimings,
324}
325
326impl ExecuteTimings {
327 pub fn accumulate(&mut self, other: &ExecuteTimings) {
328 for (t1, t2) in self.metrics.0.iter_mut().zip(other.metrics.0.iter()) {
329 *t1 += *t2;
330 }
331 self.details.accumulate(&other.details);
332 self.execute_accessories
333 .accumulate(&other.execute_accessories);
334 }
335
336 pub fn saturating_add_in_place(&mut self, timing_type: ExecuteTimingType, value_to_add: u64) {
337 let idx = timing_type as usize;
338 match self.metrics.0.get_mut(idx) {
339 Some(elem) => *elem += value_to_add,
340 None => debug_assert!(idx < ExecuteTimingType::CARDINALITY, "Index out of bounds"),
341 }
342 }
343}
344
345#[derive(Default, Debug)]
346pub struct ExecuteProcessInstructionTimings {
347 pub total_us: Saturating<u64>,
348 pub verify_caller_us: Saturating<u64>,
349 pub process_executable_chain_us: Saturating<u64>,
350 pub verify_callee_us: Saturating<u64>,
351}
352
353impl ExecuteProcessInstructionTimings {
354 pub fn accumulate(&mut self, other: &ExecuteProcessInstructionTimings) {
355 self.total_us += other.total_us;
356 self.verify_caller_us += other.verify_caller_us;
357 self.process_executable_chain_us += other.process_executable_chain_us;
358 self.verify_callee_us += other.verify_callee_us;
359 }
360}
361
362#[derive(Default, Debug)]
363pub struct ExecuteAccessoryTimings {
364 pub feature_set_clone_us: Saturating<u64>,
365 pub get_executors_us: Saturating<u64>,
366 pub process_message_us: Saturating<u64>,
367 pub process_instructions: ExecuteProcessInstructionTimings,
368}
369
370impl ExecuteAccessoryTimings {
371 pub fn accumulate(&mut self, other: &ExecuteAccessoryTimings) {
372 self.feature_set_clone_us += other.feature_set_clone_us;
373 self.get_executors_us += other.get_executors_us;
374 self.process_message_us += other.process_message_us;
375 self.process_instructions
376 .accumulate(&other.process_instructions);
377 }
378}
379
380#[derive(Default, Debug, PartialEq, Eq)]
381pub struct ExecuteDetailsTimings {
382 pub serialize_us: Saturating<u64>,
383 pub create_vm_us: Saturating<u64>,
384 pub execute_us: Saturating<u64>,
385 pub deserialize_us: Saturating<u64>,
386 pub initialize_memory_us: Saturating<u64>,
387 pub get_or_create_executor_us: Saturating<u64>,
388 pub changed_account_count: Saturating<u64>,
389 pub total_account_count: Saturating<u64>,
390 pub create_executor_register_syscalls_us: Saturating<u64>,
391 pub create_executor_load_elf_us: Saturating<u64>,
392 pub create_executor_verify_code_us: Saturating<u64>,
393 pub create_executor_jit_compile_us: Saturating<u64>,
394 pub per_program_timings: HashMap<Pubkey, ProgramTiming>,
395}
396
397impl ExecuteDetailsTimings {
398 pub fn accumulate(&mut self, other: &ExecuteDetailsTimings) {
399 self.serialize_us += other.serialize_us;
400 self.create_vm_us += other.create_vm_us;
401 self.execute_us += other.execute_us;
402 self.deserialize_us += other.deserialize_us;
403 self.initialize_memory_us += other.initialize_memory_us;
404 self.get_or_create_executor_us += other.get_or_create_executor_us;
405 self.changed_account_count += other.changed_account_count;
406 self.total_account_count += other.total_account_count;
407 self.create_executor_register_syscalls_us += other.create_executor_register_syscalls_us;
408 self.create_executor_load_elf_us += other.create_executor_load_elf_us;
409 self.create_executor_verify_code_us += other.create_executor_verify_code_us;
410 self.create_executor_jit_compile_us += other.create_executor_jit_compile_us;
411 for (id, other) in &other.per_program_timings {
412 let program_timing = self.per_program_timings.entry(*id).or_default();
413 program_timing.accumulate_program_timings(other);
414 }
415 }
416
417 pub fn accumulate_program(
418 &mut self,
419 program_id: &Pubkey,
420 us: u64,
421 compute_units_consumed: u64,
422 is_error: bool,
423 ) {
424 let program_timing = self.per_program_timings.entry(*program_id).or_default();
425 program_timing.accumulated_us += us;
426 if is_error {
427 program_timing
428 .errored_txs_compute_consumed
429 .push(compute_units_consumed);
430 program_timing.total_errored_units += compute_units_consumed;
431 } else {
432 program_timing.accumulated_units += compute_units_consumed;
433 program_timing.count += 1;
434 };
435 }
436}
437
438#[cfg(test)]
439mod tests {
440 use super::*;
441
442 fn construct_execute_timings_with_program(
443 program_id: &Pubkey,
444 us: u64,
445 compute_units_consumed: u64,
446 ) -> ExecuteDetailsTimings {
447 let mut execute_details_timings = ExecuteDetailsTimings::default();
448
449 let is_error = true;
451 execute_details_timings.accumulate_program(
452 program_id,
453 us,
454 compute_units_consumed,
455 is_error,
456 );
457
458 let is_error = false;
460 execute_details_timings.accumulate_program(
461 program_id,
462 us,
463 compute_units_consumed,
464 is_error,
465 );
466
467 let program_timings = execute_details_timings
468 .per_program_timings
469 .get(program_id)
470 .unwrap();
471
472 assert_eq!(program_timings.accumulated_us.0, us.saturating_mul(2));
474 assert_eq!(program_timings.accumulated_units.0, compute_units_consumed);
475 assert_eq!(program_timings.count.0, 1,);
476 assert_eq!(
477 program_timings.errored_txs_compute_consumed,
478 vec![compute_units_consumed]
479 );
480 assert_eq!(
481 program_timings.total_errored_units.0,
482 compute_units_consumed,
483 );
484
485 execute_details_timings
486 }
487
488 #[test]
489 fn test_execute_details_timing_acumulate_program() {
490 let program_id = Pubkey::new_unique();
492 let us = 100;
493 let compute_units_consumed = 1;
494 construct_execute_timings_with_program(&program_id, us, compute_units_consumed);
495 }
496
497 #[test]
498 fn test_execute_details_timing_acumulate() {
499 let program_id = Pubkey::new_unique();
501 let us = 100;
502 let compute_units_consumed = 1;
503 let mut execute_details_timings = ExecuteDetailsTimings::default();
504
505 let mut other_execute_details_timings =
507 construct_execute_timings_with_program(&program_id, us, compute_units_consumed);
508 let account_count = 1;
509 other_execute_details_timings.serialize_us.0 = us;
510 other_execute_details_timings.create_vm_us.0 = us;
511 other_execute_details_timings.execute_us.0 = us;
512 other_execute_details_timings.deserialize_us.0 = us;
513 other_execute_details_timings.changed_account_count.0 = account_count;
514 other_execute_details_timings.total_account_count.0 = account_count;
515
516 execute_details_timings.accumulate(&other_execute_details_timings);
518
519 assert_eq!(execute_details_timings, other_execute_details_timings);
521 }
522
523 #[test]
524 fn execute_timings_saturating_add_in_place() {
525 let mut timings = ExecuteTimings::default();
526 timings.saturating_add_in_place(ExecuteTimingType::CheckUs, 1);
527 let check_us = timings.metrics.index(ExecuteTimingType::CheckUs);
528 assert_eq!(1, check_us.0);
529
530 timings.saturating_add_in_place(ExecuteTimingType::CheckUs, 2);
531 let check_us = timings.metrics.index(ExecuteTimingType::CheckUs);
532 assert_eq!(3, check_us.0);
533 }
534}