[ty] Solve simple constraint conjunctions directly#25879
Conversation
Typing conformance resultsNo changes detected ✅Current numbersThe percentage of diagnostics emitted that were expected errors held steady at 94.37%. The percentage of expected errors that received a diagnostic held steady at 89.00%. The number of fully passing files held steady at 94/134. |
Memory usage reportSummary
Significant changesClick to expand detailed breakdowntrio
flake8
sphinx
prefect
|
|
| Project | Old Time | New Time | Change |
|---|---|---|---|
isort |
0.30s | 0.73s | +143% |
Flaky changes detected. This PR summary excludes flaky changes; see the HTML report for details.
9f671c8 to
d790d35
Compare
Merging this PR will improve performance by 40.99%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ⚡ | Memory | ty_micro[typevar_mapping_accumulation] |
90.8 MB | 28.5 MB | ×3.2 |
| ⚡ | Simulation | ty_micro[typevar_mapping_accumulation] |
883 ms | 326.2 ms | ×2.7 |
| ⚡ | Simulation | ty_micro[typevar_mapping_small_accumulations] |
185.6 ms | 172.7 ms | +7.48% |
| ⚡ | WallTime | pydantic |
10 s | 9.5 s | +5.37% |
| ⚡ | WallTime | pandas |
83 s | 79.3 s | +4.62% |
| ⚡ | Memory | ty_micro[typevar_mapping_small_accumulations] |
12.7 MB | 12.2 MB | +4.21% |
| ⚡ | WallTime | static_frame |
27.4 s | 26.3 s | +4.18% |
Tip
Curious why this is faster? Use the CodSpeed MCP and ask your agent.
Comparing charlie/benchmark-simple-specialization (0c94738) with main (1b011de)
Footnotes
-
64 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
|
4d61703 to
4b39907
Compare
4b39907 to
c44782a
Compare
c44782a to
8883f41
Compare
8883f41 to
598ab50
Compare
598ab50 to
f03b69c
Compare
dcreager
left a comment
There was a problem hiding this comment.
A minor concern about some repeated work, but otherwise this looks good
| assert!(matches!(&solutions, Solutions::Constrained(_))); | ||
| if let Solutions::Constrained(solutions) = solutions { | ||
| assert_eq!(solutions.len(), 1); | ||
| assert_eq!(solutions[0].len(), 1); | ||
| assert_eq!(solutions[0][0].bound_typevar, t); | ||
| assert_eq!( | ||
| solutions[0][0].solution, | ||
| UnionType::from_elements(&db, [int, str]) | ||
| ); | ||
| } |
There was a problem hiding this comment.
nit: Can this be re-expressed as a single assert_eq call?
| assert!(matches!(&solutions, Solutions::Constrained(_))); | |
| if let Solutions::Constrained(solutions) = solutions { | |
| assert_eq!(solutions.len(), 1); | |
| assert_eq!(solutions[0].len(), 1); | |
| assert_eq!(solutions[0][0].bound_typevar, t); | |
| assert_eq!( | |
| solutions[0][0].solution, | |
| UnionType::from_elements(&db, [int, str]) | |
| ); | |
| } | |
| assert_eq!(solutions, Solutions::Constrained(vec![ | |
| vec![TypeVarSolution { | |
| bound_typevar: t, | |
| solution: UnionType::from_elements(&db, [int, str]), | |
| }], | |
| ])); |
| if self | ||
| .node | ||
| .simple_lower_bound_conjunction(db, builder) |
There was a problem hiding this comment.
I have a mild concern that this is calling a method that builds up a Vec to determine if we can engage the fast path. And if so, we end up calling simple_lower_bound_conjunction (and building up that Vec) again when we find the solutions. It might be worth making two copies: one for use here, which just returns a bool if the BDD is a simple conjunction (and also checks is_inferable); and a second which builds up the Vec for use down in PathBounds::compute
f03b69c to
6e58ca3
Compare
## Summary The simple-lower-bound fast path added in #25879 currently recognizes the same BDD shape in two separate traversals: an allocation-free inferability predicate and a collector used for path-bound construction. This replaces both implementations with one lazy iterator built with `std::iter::from_fn`. `remove_noninferable` consumes it with `Iterator::all`, so eligibility remains allocation-free, while solution construction collects the same iterator into the `Vec` it already needs. Invalid BDD shapes yield an error item and take the existing general fallback. This is an alternative to #26247. It avoids a callback-based visitor at the cost of encoding shape failure in the iterator item type. The full `ty_python_semantic` suite, crate-level Clippy, and file-scoped repository hooks pass.
Summary
Constraint-set solution extraction currently traverses every BDD path and builds a sequent map even when the BDD is only a positive conjunction of concrete lower bounds. This recognizes that shape and accumulates its path bounds directly, avoiding
PathAssignmentsand pairwise sequent analysis.The fast path remains inside the constraint solver and still uses normal solution selection, including validation against declared type-variable bounds and constraints. Specialization-specific inference and map reuse are left to a separate stacked change.
Performance
On the existing accumulation microbenchmark, simulation time drops from 974.7 ms to 374.9 ms and peak memory drops from 91.8 MB to 29.6 MB. The small-accumulation benchmark improves from 195.8 ms to 185.1 ms.