-
Notifications
You must be signed in to change notification settings - Fork 140
Closed
astral-sh/ruff
#18955Labels
Description
The following example causes the ty run time to grow exponentially with the number of isinstance checks. This is very similar to the problematic case in astral-sh/ruff#18669, with the only difference that the attributes are actually defined, circumventing the patch in astral-sh/ruff#18669.
class C:
def f(self: "C"):
self.a = ""
self.b = ""
if isinstance(self.a, str):
return
if isinstance(self.b, str):
return
if isinstance(self.b, str):
return
# [repeat >10 times]For some reason, this scales even worse than the previous example. We end up doing 16 × 3^N - 38 member_lookup_with_policy calls, where N is the number of isinstance(self.b, str) checks. The previous case "only" scaled with 2^N.
patch with a micro benchmark
diff --git a/crates/ruff_benchmark/benches/ty.rs b/crates/ruff_benchmark/benches/ty.rs
index 3093bd6b7a..a4d9ed2536 100644
--- a/crates/ruff_benchmark/benches/ty.rs
+++ b/crates/ruff_benchmark/benches/ty.rs
@@ -348,10 +348,10 @@ fn benchmark_many_tuple_assignments(criterion: &mut Criterion) {
});
}
-fn benchmark_many_attribute_assignments(criterion: &mut Criterion) {
+fn benchmark_complex_constrained_attributes_1(criterion: &mut Criterion) {
setup_rayon();
- criterion.bench_function("ty_micro[many_attribute_assignments]", |b| {
+ criterion.bench_function("ty_micro[complex_constrained_attributes_1]", |b| {
b.iter_batched_ref(
|| {
// This is a regression benchmark for https://github.com/astral-sh/ty/issues/627.
@@ -400,6 +400,48 @@ fn benchmark_many_attribute_assignments(criterion: &mut Criterion) {
});
}
+fn benchmark_complex_constrained_attributes_2(criterion: &mut Criterion) {
+ setup_rayon();
+
+ criterion.bench_function("ty_micro[complex_constrained_attributes_2]", |b| {
+ b.iter_batched_ref(
+ || {
+ // This is is similar to the case above, but now the attributes are actually defined
+ setup_micro_case(
+ r#"
+ class C:
+ def f(self: "C"):
+ self.a = ""
+ self.b = ""
+
+ if isinstance(self.a, str):
+ return
+
+ if isinstance(self.b, str):
+ return
+ if isinstance(self.b, str):
+ return
+ if isinstance(self.b, str):
+ return
+ if isinstance(self.b, str):
+ return
+ if isinstance(self.b, str):
+ return
+ if isinstance(self.b, str):
+ return
+ "#,
+ )
+ },
+ |case| {
+ let Case { db, .. } = case;
+ let result = db.check();
+ assert_eq!(result.len(), 0);
+ },
+ BatchSize::SmallInput,
+ );
+ });
+}
+
struct ProjectBenchmark<'a> {
project: InstalledProject<'a>,
fs: MemoryFileSystem,
@@ -535,7 +577,8 @@ criterion_group!(
micro,
benchmark_many_string_assignments,
benchmark_many_tuple_assignments,
- benchmark_many_attribute_assignments,
+ benchmark_complex_constrained_attributes_1,
+ benchmark_complex_constrained_attributes_2,
);
criterion_group!(project, anyio, attrs, hydra);
criterion_main!(check_file, micro, project);