Skip to content

Commit 9218bf7

Browse files
authored
[ty] Print salsa memory usage totals in mypy primer CI runs (#18973)
## Summary Print the [new salsa memory usage dumps](#18928) in mypy primer CI runs to help us catch memory regressions. The numbers are rounded to the nearest power of 1.1 (about a 5% threshold between buckets) to avoid overly sensitive diffs.
1 parent 29927f2 commit 9218bf7

File tree

3 files changed

+137
-54
lines changed

3 files changed

+137
-54
lines changed

.github/workflows/mypy_primer.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ jobs:
4848

4949
- name: Run mypy_primer
5050
shell: bash
51+
env:
52+
TY_MEMORY_REPORT: mypy_primer
5153
run: |
5254
cd ruff
5355

crates/ty/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
146146
let mut stdout = stdout().lock();
147147
match std::env::var("TY_MEMORY_REPORT").as_deref() {
148148
Ok("short") => write!(stdout, "{}", db.salsa_memory_dump().display_short())?,
149+
Ok("mypy_primer") => write!(stdout, "{}", db.salsa_memory_dump().display_mypy_primer())?,
149150
Ok("full") => write!(stdout, "{}", db.salsa_memory_dump().display_full())?,
150151
_ => {}
151152
}

crates/ty_project/src/db.rs

Lines changed: 134 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -121,71 +121,92 @@ impl ProjectDatabase {
121121
ingredients.sort_by_key(|ingredient| cmp::Reverse(ingredient.size_of_fields()));
122122
memos.sort_by_key(|(_, memo)| cmp::Reverse(memo.size_of_fields()));
123123

124-
SalsaMemoryDump { ingredients, memos }
124+
let mut total_fields = 0;
125+
let mut total_metadata = 0;
126+
for ingredient in &ingredients {
127+
total_metadata += ingredient.size_of_metadata();
128+
total_fields += ingredient.size_of_fields();
129+
}
130+
131+
let mut total_memo_fields = 0;
132+
let mut total_memo_metadata = 0;
133+
for (_, memo) in &memos {
134+
total_memo_fields += memo.size_of_fields();
135+
total_memo_metadata += memo.size_of_metadata();
136+
}
137+
138+
SalsaMemoryDump {
139+
total_fields,
140+
total_metadata,
141+
total_memo_fields,
142+
total_memo_metadata,
143+
ingredients,
144+
memos,
145+
}
125146
}
126147
}
127148

128149
/// Stores memory usage information.
129150
pub struct SalsaMemoryDump {
151+
total_fields: usize,
152+
total_metadata: usize,
153+
total_memo_fields: usize,
154+
total_memo_metadata: usize,
130155
ingredients: Vec<salsa::IngredientInfo>,
131156
memos: Vec<(&'static str, salsa::IngredientInfo)>,
132157
}
133158

134159
#[allow(clippy::cast_precision_loss)]
160+
fn bytes_to_mb(total: usize) -> f64 {
161+
total as f64 / 1_000_000.
162+
}
163+
164+
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
135165
impl SalsaMemoryDump {
136166
/// Returns a short report that provides total memory usage information.
137167
pub fn display_short(&self) -> impl fmt::Display + '_ {
138168
struct DisplayShort<'a>(&'a SalsaMemoryDump);
139169

140170
impl fmt::Display for DisplayShort<'_> {
141171
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142-
let mut total_fields = 0;
143-
let mut total_metadata = 0;
144-
for ingredient in &self.0.ingredients {
145-
total_metadata += ingredient.size_of_metadata();
146-
total_fields += ingredient.size_of_fields();
147-
}
148-
149-
let mut total_memo_fields = 0;
150-
let mut total_memo_metadata = 0;
151-
for (_, memo) in &self.0.memos {
152-
total_memo_fields += memo.size_of_fields();
153-
total_memo_metadata += memo.size_of_metadata();
154-
}
172+
let SalsaMemoryDump {
173+
total_fields,
174+
total_metadata,
175+
total_memo_fields,
176+
total_memo_metadata,
177+
ref ingredients,
178+
ref memos,
179+
} = *self.0;
155180

156181
writeln!(f, "=======SALSA SUMMARY=======")?;
157182

158183
writeln!(
159184
f,
160185
"TOTAL MEMORY USAGE: {:.2}MB",
161-
(total_metadata + total_fields + total_memo_fields + total_memo_metadata)
162-
as f64
163-
/ 1_000_000.,
186+
bytes_to_mb(
187+
total_metadata + total_fields + total_memo_fields + total_memo_metadata
188+
)
164189
)?;
165190

166191
writeln!(
167192
f,
168193
" struct metadata = {:.2}MB",
169-
total_metadata as f64 / 1_000_000.,
170-
)?;
171-
writeln!(
172-
f,
173-
" struct fields = {:.2}MB",
174-
total_fields as f64 / 1_000_000.,
194+
bytes_to_mb(total_metadata),
175195
)?;
196+
writeln!(f, " struct fields = {:.2}MB", bytes_to_mb(total_fields))?;
176197
writeln!(
177198
f,
178199
" memo metadata = {:.2}MB",
179-
total_memo_metadata as f64 / 1_000_000.,
200+
bytes_to_mb(total_memo_metadata),
180201
)?;
181202
writeln!(
182203
f,
183204
" memo fields = {:.2}MB",
184-
total_memo_fields as f64 / 1_000_000.
205+
bytes_to_mb(total_memo_fields),
185206
)?;
186207

187-
writeln!(f, "QUERY COUNT: {}", self.0.memos.len())?;
188-
writeln!(f, "STRUCT COUNT: {}", self.0.ingredients.len())?;
208+
writeln!(f, "QUERY COUNT: {}", memos.len())?;
209+
writeln!(f, "STRUCT COUNT: {}", ingredients.len())?;
189210

190211
Ok(())
191212
}
@@ -201,39 +222,38 @@ impl SalsaMemoryDump {
201222

202223
impl fmt::Display for DisplayFull<'_> {
203224
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204-
writeln!(f, "=======SALSA STRUCTS=======")?;
225+
let SalsaMemoryDump {
226+
total_fields,
227+
total_metadata,
228+
total_memo_fields,
229+
total_memo_metadata,
230+
ref ingredients,
231+
ref memos,
232+
} = *self.0;
205233

206-
let mut total_fields = 0;
207-
let mut total_metadata = 0;
208-
for ingredient in &self.0.ingredients {
209-
total_metadata += ingredient.size_of_metadata();
210-
total_fields += ingredient.size_of_fields();
234+
writeln!(f, "=======SALSA STRUCTS=======")?;
211235

236+
for ingredient in ingredients {
212237
writeln!(
213238
f,
214239
"{:<50} metadata={:<8} fields={:<8} count={}",
215240
format!("`{}`", ingredient.debug_name()),
216-
format!("{:.2}MB", ingredient.size_of_metadata() as f64 / 1_000_000.),
217-
format!("{:.2}MB", ingredient.size_of_fields() as f64 / 1_000_000.),
241+
format!("{:.2}MB", bytes_to_mb(ingredient.size_of_metadata())),
242+
format!("{:.2}MB", bytes_to_mb(ingredient.size_of_fields())),
218243
ingredient.count()
219244
)?;
220245
}
221246

222247
writeln!(f, "=======SALSA QUERIES=======")?;
223248

224-
let mut total_memo_fields = 0;
225-
let mut total_memo_metadata = 0;
226-
for (query_fn, memo) in &self.0.memos {
227-
total_memo_fields += memo.size_of_fields();
228-
total_memo_metadata += memo.size_of_metadata();
229-
249+
for (query_fn, memo) in memos {
230250
writeln!(f, "`{query_fn} -> {}`", memo.debug_name())?;
231251

232252
writeln!(
233253
f,
234254
" metadata={:<8} fields={:<8} count={}",
235-
format!("{:.2}MB", memo.size_of_metadata() as f64 / 1_000_000.),
236-
format!("{:.2}MB", memo.size_of_fields() as f64 / 1_000_000.),
255+
format!("{:.2}MB", bytes_to_mb(memo.size_of_metadata())),
256+
format!("{:.2}MB", bytes_to_mb(memo.size_of_fields())),
237257
memo.count()
238258
)?;
239259
}
@@ -242,30 +262,26 @@ impl SalsaMemoryDump {
242262
writeln!(
243263
f,
244264
"TOTAL MEMORY USAGE: {:.2}MB",
245-
(total_metadata + total_fields + total_memo_fields + total_memo_metadata)
246-
as f64
247-
/ 1_000_000.,
265+
bytes_to_mb(
266+
total_metadata + total_fields + total_memo_fields + total_memo_metadata
267+
)
248268
)?;
249269

250270
writeln!(
251271
f,
252272
" struct metadata = {:.2}MB",
253-
total_metadata as f64 / 1_000_000.,
254-
)?;
255-
writeln!(
256-
f,
257-
" struct fields = {:.2}MB",
258-
total_fields as f64 / 1_000_000.,
273+
bytes_to_mb(total_metadata),
259274
)?;
275+
writeln!(f, " struct fields = {:.2}MB", bytes_to_mb(total_fields))?;
260276
writeln!(
261277
f,
262278
" memo metadata = {:.2}MB",
263-
total_memo_metadata as f64 / 1_000_000.,
279+
bytes_to_mb(total_memo_metadata),
264280
)?;
265281
writeln!(
266282
f,
267283
" memo fields = {:.2}MB",
268-
total_memo_fields as f64 / 1_000_000.
284+
bytes_to_mb(total_memo_fields),
269285
)?;
270286

271287
Ok(())
@@ -274,6 +290,70 @@ impl SalsaMemoryDump {
274290

275291
DisplayFull(self)
276292
}
293+
294+
/// Returns a redacted report that provides rounded totals of memory usage, to avoid
295+
/// overly sensitive diffs in `mypy-primer` runs.
296+
pub fn display_mypy_primer(&self) -> impl fmt::Display + '_ {
297+
struct DisplayShort<'a>(&'a SalsaMemoryDump);
298+
299+
fn round_memory(total: usize) -> usize {
300+
// Round the number to the nearest power of 1.1. This gives us a
301+
// 5% threshold before the memory usage number is considered to have
302+
// changed.
303+
//
304+
// TODO: Small changes in memory usage may cause the number to be rounded
305+
// into the next power if it happened to already be close to the threshold.
306+
// This also means that differences may surface as a result of small changes
307+
// over time that are unrelated to the current change. Ideally we could compare
308+
// the exact numbers across runs and compute the difference, but we don't have
309+
// the infrastructure for that currently.
310+
const BASE: f64 = 1.1;
311+
BASE.powf(bytes_to_mb(total).log(BASE).round()) as usize
312+
}
313+
314+
impl fmt::Display for DisplayShort<'_> {
315+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316+
let SalsaMemoryDump {
317+
total_fields,
318+
total_metadata,
319+
total_memo_fields,
320+
total_memo_metadata,
321+
..
322+
} = *self.0;
323+
324+
writeln!(f, "=======SALSA SUMMARY=======")?;
325+
326+
writeln!(
327+
f,
328+
"TOTAL MEMORY USAGE: ~{}MB",
329+
round_memory(
330+
total_metadata + total_fields + total_memo_fields + total_memo_metadata
331+
)
332+
)?;
333+
334+
writeln!(
335+
f,
336+
" struct metadata = ~{}MB",
337+
round_memory(total_metadata)
338+
)?;
339+
writeln!(f, " struct fields = ~{}MB", round_memory(total_fields))?;
340+
writeln!(
341+
f,
342+
" memo metadata = ~{}MB",
343+
round_memory(total_memo_metadata)
344+
)?;
345+
writeln!(
346+
f,
347+
" memo fields = ~{}MB",
348+
round_memory(total_memo_fields)
349+
)?;
350+
351+
Ok(())
352+
}
353+
}
354+
355+
DisplayShort(self)
356+
}
277357
}
278358

279359
#[salsa::db]

0 commit comments

Comments
 (0)