Skip to content

Optimize schema-comparator diffing performance#94

Open
swalkinshaw wants to merge 1 commit intomainfrom
schema-comparator-perf
Open

Optimize schema-comparator diffing performance#94
swalkinshaw wants to merge 1 commit intomainfrom
schema-comparator-perf

Conversation

@swalkinshaw
Copy link
Contributor

@swalkinshaw swalkinshaw commented Mar 17, 2026

Optimize schema-comparator diffing performance (~65% faster on large schemas)

Key optimizations:

  • Eliminate intermediate Vec allocations via diff_into(&mut Vec) pattern
  • Single-pass field matching: merge removals + common diffs into one iteration
  • Unified diff_directives_into replacing 3 separate directive functions
  • Ptr equality fast path for identical schemas (0µs)
  • HashMap fallback for types with 64+ fields (O(1) lookups on large types)
  • CriticalityLevel enum to avoid Cow string allocations during sorting
  • Early exits for empty directives/arguments, #[inline] hints on hot paths
  • Pre-allocate changes Vec with capacity 256
  • Criterion benchmark suite with real schema fixtures

Note: this is a manually curated (with the help of Claude) and cleaned up version of an /autoresearch run

@swalkinshaw swalkinshaw requested a review from adampetro March 17, 2026 16:21
…schemas)

Key optimizations:
- Eliminate intermediate Vec allocations via diff_into(&mut Vec) pattern
- Single-pass field matching: merge removals + common diffs into one iteration
- Unified diff_directives_into replacing 3 separate directive functions
- Ptr equality fast path for identical schemas (0µs)
- HashMap fallback for types with 64+ fields (O(1) lookups on large types)
- CriticalityLevel enum to avoid Cow string allocations during sorting
- Early exits for empty directives/arguments, #[inline] hints on hot paths
- Pre-allocate changes Vec with capacity 256
- Criterion benchmark suite with real schema fixtures

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@swalkinshaw swalkinshaw force-pushed the schema-comparator-perf branch from c4fb557 to 73d5d13 Compare March 17, 2026 16:45
Comment on lines +16 to +20
let src = std::fs::read_to_string(data_path(name)).unwrap();
let doc: DefinitionDocument = DefinitionDocument::parse(Box::leak(src.into_boxed_str()))
.result
.expect("Schema had parse errors");
let leaked = Box::leak(Box::new(doc));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use include_str! to avoid some of the leaking? Or would that slow down compiles a lot?

Comment on lines +43 to +46
fn compare_with_changes(c: &mut Criterion) {
c.bench_function("docs schema vs itself (no changes)", |b| {
b.iter(|| compare(&*DOCS_SCHEMA, &*DOCS_SCHEMA));
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused because it's called compare_with_changes but then the first one has no changes?

);
}

#[cold]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TIL

Comment on lines -81 to -99
changes.extend(
directive_additions::<S, _>(self.old_type_definition, self.new_type_definition).map(
|directive| Change::DirectiveAdded {
directive,
location: DirectiveLocation::InputFieldDefinition,
member_name: self.old_field_definition.name(),
},
),
);

changes.extend(
directive_removals::<S, _>(self.old_type_definition, self.new_type_definition).map(
|directive| Change::DirectiveRemoved {
directive,
location: DirectiveLocation::InputFieldDefinition,
member_name: self.old_field_definition.name(),
},
),
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was this a bug?

Comment on lines +28 to +31
// Fast path: if both schemas are the same object, there are no changes
if std::ptr::eq(self.old_schema_definition, self.new_schema_definition) {
return Vec::new();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this ever be useful in practice?

}),
);

// Schema-level directive removals
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are we missing directive diffs?

directive_definition: _,
argument_definition,
} => {
if argument_definition.is_required() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we be checking for a default value?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants