-
Notifications
You must be signed in to change notification settings - Fork 175
Use generational identifiers for tracked structs #864
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
✅ Deploy Preview for salsa-rs canceled.
|
CodSpeed Performance ReportMerging #864 will degrade performances by 15.82%Comparing Summary
Benchmarks breakdown
|
This is huge! I'm leaning towards removing the |
57ec532
to
66bbb88
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, merge whenever you'd like to?
I'd be interested to explore the memory overhead if we changed |
d980d6c
to
5ce6735
Compare
@@ -152,6 +152,9 @@ fn revalidate_no_changes() { | |||
"salsa_event(DidValidateMemoizedValue { database_key: read_value(Id(400)) })", | |||
"salsa_event(DidReinternValue { key: query_d::interned_arguments(Id(800)), revision: R2 })", | |||
"salsa_event(DidValidateMemoizedValue { database_key: query_d(Id(800)) })", | |||
"salsa_event(DidValidateMemoizedValue { database_key: read_value(Id(401)) })", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, this is interesting
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you know why this test changed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure. Maybe the memos are being validated instead of the function being re-executing, if the function previously had a read dependency on a tracked struct (and that dependency no longer exists)?
I updated the PR to store Unfortunately it looks like the benchmarks don't like this change... |
5ce6735
to
4920a8a
Compare
Is there any perf difference on ty? (When running on codspeed) |
4920a8a
to
19d00eb
Compare
No performance difference on ty benchmarks (astral-sh/ruff#18226). |
I plan to review this tomorrow. I'm okay with the regression given that it enables interned GC without a 50% incremental perf and memory regression. If you haven't done so already, maybe take an hour or two to see if you can spot the source of the regression in a local recorded profile (take the benchmark that regresses the most) This is a substantial change where I'd like to get at least a thumbs up from r-a too, given that the performance is now regressing on micro benchmarks. Cc @Veykril |
I don't think there is a specific source, I think the regression is directly related to the size of |
19d00eb
to
8b6cc9f
Compare
let updated_id = self.update(zalsa, current_revision, id, ¤t_deps, fields); | ||
if id != updated_id { | ||
// Overwrite the previous ID if we are reusing the slot with new fields. | ||
zalsa_local.store_tracked_struct_id(identity, updated_id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels a little wrong, but I think it is correct. If the struct is recreated with the same fields, we want to return the latest generation for the tracked structs to align with the memos. If it is recreated with new fields, we have to create a new generation and invalidate the previous version.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awesome. I've a few smaller comments that should be addressed before landing (assuming r-a is on board with this change)
@@ -152,6 +152,9 @@ fn revalidate_no_changes() { | |||
"salsa_event(DidValidateMemoizedValue { database_key: read_value(Id(400)) })", | |||
"salsa_event(DidReinternValue { key: query_d::interned_arguments(Id(800)), revision: R2 })", | |||
"salsa_event(DidValidateMemoizedValue { database_key: query_d(Id(800)) })", | |||
"salsa_event(DidValidateMemoizedValue { database_key: read_value(Id(401)) })", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you know why this test changed?
I would like to cut another release before merging this if possible. I expect this to have a noticable memory impact for rust-analyzer, having this be part of a separate release cycle would make that easier to check. |
6fbf77d
to
9c27c3d
Compare
9c27c3d
to
bc403d9
Compare
// the unlikely case that the ID is already at its maximum generation, we are forced to leak | ||
// the previous slot and allocate a new value. | ||
if id.generation() == u32::MAX { | ||
return Err(()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be good to log at least an info message that we leaked this node.
When rebased onto this branch, #839 is actually a ~5% improvement to ty's incremental performance. |
@Veykril what's your timeline on the next release? |
I'm fine with cutting a release now if you want to land this to try it out in ruff/ty asap |
I'll land this once all feedback is addressed. |
3a47df9
to
5a2c276
Compare
Pack a generation into input IDs.
The generation of a tracked struct is incremented after it is reused, allowing us to avoid read dependencies. Generational IDs were originally meant for #839, as adding the necessary read dependency on interned structs that may be reused introduced a large (~50%) regression to ty's incremental performance.
This increases the size of
Id
from au32
to au64
. However, if the generation is restricted tou16
, and ingredient indices are restricted tou16
, this does not increase the size ofDatabaseKeyIndex
, so the memory usage effect is limited (~5% increase to ty's peak memory usage). However, this should allow us to implement garbage collection for interned values without significant performance concerns, so memory usage over time should benefit.If the generation exceeds
u16::MAX
, we can fallback to adding read dependencies on tracked structs. An alternative would be to leak the slot, which would also allow us to remove thecreated_at
field on tracked structs and may alleviate the memory usage concerns. This might be more feasible if the generation stole a few more bits from ingredient indices (as the number of ingredients is effectively static for a given salsa program).This has a small (~4%) performance improvement on ty's benchmarks.