[ty] Print salsa memory usage totals in mypy primer CI runs (#18973)
Some checks are pending
CI / Determine changes (push) Waiting to run
CI / cargo fmt (push) Waiting to run
CI / cargo clippy (push) Blocked by required conditions
CI / cargo test (linux) (push) Blocked by required conditions
CI / cargo test (linux, release) (push) Blocked by required conditions
CI / cargo test (windows) (push) Blocked by required conditions
CI / cargo test (wasm) (push) Blocked by required conditions
CI / cargo build (release) (push) Waiting to run
CI / cargo build (msrv) (push) Blocked by required conditions
CI / cargo fuzz build (push) Blocked by required conditions
CI / fuzz parser (push) Blocked by required conditions
CI / test scripts (push) Blocked by required conditions
CI / ecosystem (push) Blocked by required conditions
CI / Fuzz for new ty panics (push) Blocked by required conditions
CI / cargo shear (push) Blocked by required conditions
CI / python package (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / mkdocs (push) Waiting to run
CI / formatter instabilities and black similarity (push) Blocked by required conditions
CI / test ruff-lsp (push) Blocked by required conditions
CI / check playground (push) Blocked by required conditions
CI / benchmarks-instrumented (push) Blocked by required conditions
CI / benchmarks-walltime (push) Blocked by required conditions
[ty Playground] Release / publish (push) Waiting to run

## Summary

Print the [new salsa memory usage
dumps](https://github.com/astral-sh/ruff/pull/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.
This commit is contained in:
Ibraheem Ahmed 2025-06-28 15:09:50 -04:00 committed by GitHub
parent 29927f2b59
commit 9218bf72ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 137 additions and 54 deletions

View file

@ -48,6 +48,8 @@ jobs:
- name: Run mypy_primer
shell: bash
env:
TY_MEMORY_REPORT: mypy_primer
run: |
cd ruff

View file

@ -146,6 +146,7 @@ fn run_check(args: CheckCommand) -> anyhow::Result<ExitStatus> {
let mut stdout = stdout().lock();
match std::env::var("TY_MEMORY_REPORT").as_deref() {
Ok("short") => write!(stdout, "{}", db.salsa_memory_dump().display_short())?,
Ok("mypy_primer") => write!(stdout, "{}", db.salsa_memory_dump().display_mypy_primer())?,
Ok("full") => write!(stdout, "{}", db.salsa_memory_dump().display_full())?,
_ => {}
}

View file

@ -121,17 +121,47 @@ impl ProjectDatabase {
ingredients.sort_by_key(|ingredient| cmp::Reverse(ingredient.size_of_fields()));
memos.sort_by_key(|(_, memo)| cmp::Reverse(memo.size_of_fields()));
SalsaMemoryDump { ingredients, memos }
let mut total_fields = 0;
let mut total_metadata = 0;
for ingredient in &ingredients {
total_metadata += ingredient.size_of_metadata();
total_fields += ingredient.size_of_fields();
}
let mut total_memo_fields = 0;
let mut total_memo_metadata = 0;
for (_, memo) in &memos {
total_memo_fields += memo.size_of_fields();
total_memo_metadata += memo.size_of_metadata();
}
SalsaMemoryDump {
total_fields,
total_metadata,
total_memo_fields,
total_memo_metadata,
ingredients,
memos,
}
}
}
/// Stores memory usage information.
pub struct SalsaMemoryDump {
total_fields: usize,
total_metadata: usize,
total_memo_fields: usize,
total_memo_metadata: usize,
ingredients: Vec<salsa::IngredientInfo>,
memos: Vec<(&'static str, salsa::IngredientInfo)>,
}
#[allow(clippy::cast_precision_loss)]
fn bytes_to_mb(total: usize) -> f64 {
total as f64 / 1_000_000.
}
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
impl SalsaMemoryDump {
/// Returns a short report that provides total memory usage information.
pub fn display_short(&self) -> impl fmt::Display + '_ {
@ -139,53 +169,44 @@ impl SalsaMemoryDump {
impl fmt::Display for DisplayShort<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut total_fields = 0;
let mut total_metadata = 0;
for ingredient in &self.0.ingredients {
total_metadata += ingredient.size_of_metadata();
total_fields += ingredient.size_of_fields();
}
let mut total_memo_fields = 0;
let mut total_memo_metadata = 0;
for (_, memo) in &self.0.memos {
total_memo_fields += memo.size_of_fields();
total_memo_metadata += memo.size_of_metadata();
}
let SalsaMemoryDump {
total_fields,
total_metadata,
total_memo_fields,
total_memo_metadata,
ref ingredients,
ref memos,
} = *self.0;
writeln!(f, "=======SALSA SUMMARY=======")?;
writeln!(
f,
"TOTAL MEMORY USAGE: {:.2}MB",
(total_metadata + total_fields + total_memo_fields + total_memo_metadata)
as f64
/ 1_000_000.,
bytes_to_mb(
total_metadata + total_fields + total_memo_fields + total_memo_metadata
)
)?;
writeln!(
f,
" struct metadata = {:.2}MB",
total_metadata as f64 / 1_000_000.,
)?;
writeln!(
f,
" struct fields = {:.2}MB",
total_fields as f64 / 1_000_000.,
bytes_to_mb(total_metadata),
)?;
writeln!(f, " struct fields = {:.2}MB", bytes_to_mb(total_fields))?;
writeln!(
f,
" memo metadata = {:.2}MB",
total_memo_metadata as f64 / 1_000_000.,
bytes_to_mb(total_memo_metadata),
)?;
writeln!(
f,
" memo fields = {:.2}MB",
total_memo_fields as f64 / 1_000_000.
bytes_to_mb(total_memo_fields),
)?;
writeln!(f, "QUERY COUNT: {}", self.0.memos.len())?;
writeln!(f, "STRUCT COUNT: {}", self.0.ingredients.len())?;
writeln!(f, "QUERY COUNT: {}", memos.len())?;
writeln!(f, "STRUCT COUNT: {}", ingredients.len())?;
Ok(())
}
@ -201,39 +222,38 @@ impl SalsaMemoryDump {
impl fmt::Display for DisplayFull<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let SalsaMemoryDump {
total_fields,
total_metadata,
total_memo_fields,
total_memo_metadata,
ref ingredients,
ref memos,
} = *self.0;
writeln!(f, "=======SALSA STRUCTS=======")?;
let mut total_fields = 0;
let mut total_metadata = 0;
for ingredient in &self.0.ingredients {
total_metadata += ingredient.size_of_metadata();
total_fields += ingredient.size_of_fields();
for ingredient in ingredients {
writeln!(
f,
"{:<50} metadata={:<8} fields={:<8} count={}",
format!("`{}`", ingredient.debug_name()),
format!("{:.2}MB", ingredient.size_of_metadata() as f64 / 1_000_000.),
format!("{:.2}MB", ingredient.size_of_fields() as f64 / 1_000_000.),
format!("{:.2}MB", bytes_to_mb(ingredient.size_of_metadata())),
format!("{:.2}MB", bytes_to_mb(ingredient.size_of_fields())),
ingredient.count()
)?;
}
writeln!(f, "=======SALSA QUERIES=======")?;
let mut total_memo_fields = 0;
let mut total_memo_metadata = 0;
for (query_fn, memo) in &self.0.memos {
total_memo_fields += memo.size_of_fields();
total_memo_metadata += memo.size_of_metadata();
for (query_fn, memo) in memos {
writeln!(f, "`{query_fn} -> {}`", memo.debug_name())?;
writeln!(
f,
" metadata={:<8} fields={:<8} count={}",
format!("{:.2}MB", memo.size_of_metadata() as f64 / 1_000_000.),
format!("{:.2}MB", memo.size_of_fields() as f64 / 1_000_000.),
format!("{:.2}MB", bytes_to_mb(memo.size_of_metadata())),
format!("{:.2}MB", bytes_to_mb(memo.size_of_fields())),
memo.count()
)?;
}
@ -242,30 +262,26 @@ impl SalsaMemoryDump {
writeln!(
f,
"TOTAL MEMORY USAGE: {:.2}MB",
(total_metadata + total_fields + total_memo_fields + total_memo_metadata)
as f64
/ 1_000_000.,
bytes_to_mb(
total_metadata + total_fields + total_memo_fields + total_memo_metadata
)
)?;
writeln!(
f,
" struct metadata = {:.2}MB",
total_metadata as f64 / 1_000_000.,
)?;
writeln!(
f,
" struct fields = {:.2}MB",
total_fields as f64 / 1_000_000.,
bytes_to_mb(total_metadata),
)?;
writeln!(f, " struct fields = {:.2}MB", bytes_to_mb(total_fields))?;
writeln!(
f,
" memo metadata = {:.2}MB",
total_memo_metadata as f64 / 1_000_000.,
bytes_to_mb(total_memo_metadata),
)?;
writeln!(
f,
" memo fields = {:.2}MB",
total_memo_fields as f64 / 1_000_000.
bytes_to_mb(total_memo_fields),
)?;
Ok(())
@ -274,6 +290,70 @@ impl SalsaMemoryDump {
DisplayFull(self)
}
/// Returns a redacted report that provides rounded totals of memory usage, to avoid
/// overly sensitive diffs in `mypy-primer` runs.
pub fn display_mypy_primer(&self) -> impl fmt::Display + '_ {
struct DisplayShort<'a>(&'a SalsaMemoryDump);
fn round_memory(total: usize) -> usize {
// Round the number to the nearest power of 1.1. This gives us a
// 5% threshold before the memory usage number is considered to have
// changed.
//
// TODO: Small changes in memory usage may cause the number to be rounded
// into the next power if it happened to already be close to the threshold.
// This also means that differences may surface as a result of small changes
// over time that are unrelated to the current change. Ideally we could compare
// the exact numbers across runs and compute the difference, but we don't have
// the infrastructure for that currently.
const BASE: f64 = 1.1;
BASE.powf(bytes_to_mb(total).log(BASE).round()) as usize
}
impl fmt::Display for DisplayShort<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let SalsaMemoryDump {
total_fields,
total_metadata,
total_memo_fields,
total_memo_metadata,
..
} = *self.0;
writeln!(f, "=======SALSA SUMMARY=======")?;
writeln!(
f,
"TOTAL MEMORY USAGE: ~{}MB",
round_memory(
total_metadata + total_fields + total_memo_fields + total_memo_metadata
)
)?;
writeln!(
f,
" struct metadata = ~{}MB",
round_memory(total_metadata)
)?;
writeln!(f, " struct fields = ~{}MB", round_memory(total_fields))?;
writeln!(
f,
" memo metadata = ~{}MB",
round_memory(total_memo_metadata)
)?;
writeln!(
f,
" memo fields = ~{}MB",
round_memory(total_memo_fields)
)?;
Ok(())
}
}
DisplayShort(self)
}
}
#[salsa::db]