1#[derive(Clone, Debug, Default, PartialEq, Eq)]
5pub struct PartitionLayerRecord {
6 pub bits: u8,
8 pub remote_deltas: u32,
10 pub collectives: u32,
12 pub circuit_index: u32,
14 pub application_index: u32,
16 pub gate_name: &'static str,
18 pub terms_in: Vec<usize>,
20 pub terms_out: Vec<usize>,
22 pub rows_sent: Vec<Vec<u64>>,
24 pub bytes_sent: Vec<Vec<u64>>,
26 pub rows_received: Vec<u64>,
28 pub nanos: Vec<u64>,
30 pub refine_passes: Vec<u32>,
32 pub receive_chunks: Vec<u32>,
34 pub device: Vec<Option<DeviceLayerRecord>>,
36}
37
38#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
40pub struct DeviceLayerRecord {
41 pub records: u64,
43 pub max_block_records: u32,
45 pub record_capacity: u32,
47 pub batches: u32,
49 pub high_word_fallbacks: u32,
51 pub full_key_fallbacks: u32,
53 pub rows_premerged: u64,
55 pub rescaled: bool,
57 pub permuted: bool,
59 pub dense: bool,
61}
62
63#[derive(Clone, Debug, Default, PartialEq, Eq)]
67pub struct PartitionTrace {
68 pub layers: Vec<PartitionLayerRecord>,
70}
71
72impl PartitionTrace {
73 pub fn peak_terms(&self) -> Option<usize> {
75 let first = self.layers.first()?;
76 let total = |counts: &[usize]| counts.iter().sum::<usize>();
77 let peak_out = self.layers.iter().map(|layer| total(&layer.terms_out)).max();
78 Some(total(&first.terms_in).max(peak_out.unwrap_or(0)))
79 }
80
81 pub fn total_rows_exchanged(&self) -> u64 {
83 self.layers.iter().flat_map(|layer| layer.rows_sent.iter()).flat_map(|row| row.iter()).sum()
84 }
85
86 pub fn imbalance(&self) -> Vec<f64> {
88 self.layers
89 .iter()
90 .map(|layer| {
91 let total: usize = layer.terms_in.iter().sum();
92 let max = layer.terms_in.iter().copied().max().unwrap_or(0);
93 if total == 0 {
94 return 1.0;
95 }
96 let mean = total as f64 / layer.terms_in.len() as f64;
97 max as f64 / mean
98 })
99 .collect()
100 }
101
102 pub fn local_layers(&self) -> usize {
104 self.layers.iter().filter(|layer| layer.remote_deltas == 0).count()
105 }
106
107 pub fn remote_layers(&self) -> usize {
109 self.layers.iter().filter(|layer| layer.remote_deltas > 0).count()
110 }
111
112 pub fn total_collectives(&self) -> u64 {
114 self.layers.iter().map(|layer| u64::from(layer.collectives)).sum()
115 }
116}
117
118#[derive(Clone, Debug, Default, PartialEq, Eq)]
120pub(crate) struct PartitionLayerRow {
121 pub bits: u8,
122 pub remote_deltas: u32,
123 pub collectives: u32,
124 pub circuit_index: u32,
125 pub application_index: u32,
126 pub gate_name: &'static str,
127 pub terms_in: usize,
128 pub terms_out: usize,
129 pub rows_sent: Vec<u64>,
130 pub bytes_sent: Vec<u64>,
131 pub rows_received: u64,
132 pub nanos: u64,
133 pub refine_passes: u32,
134 pub receive_chunks: u32,
135 pub device: Option<DeviceLayerRecord>,
136}
137
138#[cold]
140#[inline(never)]
141pub(super) fn record_layer_row(rows: &mut Vec<PartitionLayerRow>, row: PartitionLayerRow) {
142 rows.push(row);
143}
144
145pub(crate) fn assemble(
147 trace: &mut PartitionTrace,
148 per_partition: Vec<Vec<PartitionLayerRow>>,
149 partners: usize,
150) {
151 let size = per_partition.len();
152 let layers = per_partition[0].len();
153 for (rank, rows) in per_partition.iter().enumerate() {
154 assert_eq!(
155 rows.len(),
156 layers,
157 "partition {rank} traced {} layers, partition 0 traced {layers}",
158 rows.len(),
159 );
160 }
161
162 trace.layers.reserve(layers);
163 for k in 0..layers {
164 let head = &per_partition[0][k];
165 let mut record = PartitionLayerRecord {
166 bits: head.bits,
167 remote_deltas: head.remote_deltas,
168 collectives: head.collectives,
169 circuit_index: head.circuit_index,
170 application_index: head.application_index,
171 gate_name: head.gate_name,
172 terms_in: Vec::with_capacity(size),
173 terms_out: Vec::with_capacity(size),
174 rows_sent: Vec::with_capacity(size),
175 bytes_sent: Vec::with_capacity(size),
176 rows_received: Vec::with_capacity(size),
177 nanos: Vec::with_capacity(size),
178 refine_passes: Vec::with_capacity(size),
179 receive_chunks: Vec::with_capacity(size),
180 device: Vec::with_capacity(size),
181 };
182 for (rank, rows) in per_partition.iter().enumerate() {
183 let row = &rows[k];
184 assert_eq!(
185 row.bits, head.bits,
186 "layer {k}: partition {rank} has {} bucket bits, partition 0 has {}",
187 row.bits, head.bits,
188 );
189 assert_eq!(
190 row.remote_deltas, head.remote_deltas,
191 "layer {k}: partition {rank} saw {} remote deltas, partition 0 saw {}",
192 row.remote_deltas, head.remote_deltas,
193 );
194 assert_eq!(
195 row.collectives, head.collectives,
196 "layer {k}: partition {rank} issued {} collectives, partition 0 issued {}",
197 row.collectives, head.collectives,
198 );
199 assert_eq!(
200 row.circuit_index, head.circuit_index,
201 "layer {k}: partition {rank} ran circuit index {}, partition 0 ran {}",
202 row.circuit_index, head.circuit_index,
203 );
204 assert_eq!(
205 row.application_index, head.application_index,
206 "layer {k}: partition {rank} ran application index {}, partition 0 ran {}",
207 row.application_index, head.application_index,
208 );
209 assert_eq!(
210 row.gate_name, head.gate_name,
211 "layer {k}: partition {rank} ran gate {:?}, partition 0 ran {:?}",
212 row.gate_name, head.gate_name,
213 );
214 record.terms_in.push(row.terms_in);
215 record.terms_out.push(row.terms_out);
216 let mut rows_sent = row.rows_sent.clone();
218 rows_sent.resize(partners, 0);
219 let mut bytes_sent = row.bytes_sent.clone();
220 bytes_sent.resize(partners, 0);
221 record.rows_sent.push(rows_sent);
222 record.bytes_sent.push(bytes_sent);
223 record.rows_received.push(row.rows_received);
224 record.nanos.push(row.nanos);
225 record.refine_passes.push(row.refine_passes);
226 record.receive_chunks.push(row.receive_chunks);
227 record.device.push(row.device);
228 }
229 trace.layers.push(record);
230 }
231}
232
233#[cfg(test)]
234mod tests;