Skip to content
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

Enable Garbage Collection for Interned Values #602

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

ibraheemdev
Copy link
Contributor

@ibraheemdev ibraheemdev commented Oct 23, 2024

Per the mentoring instructions in #597.

Still needs some tests. Note this does not actually implement garbage collection but adds the necessary plumbing to do so.

Copy link

netlify bot commented Oct 23, 2024

Deploy Preview for salsa-rs canceled.

Name Link
🔨 Latest commit c6077c4
🔍 Latest deploy log https://app.netlify.com/sites/salsa-rs/deploys/672886d84f15ed0008fdc67f

Copy link

codspeed-hq bot commented Oct 23, 2024

CodSpeed Performance Report

Merging #602 will not alter performance

Comparing ibraheemdev:gc (db71a89) with master (254c749)

Summary

✅ 8 untouched benchmarks

Copy link
Contributor

@MichaReiser MichaReiser left a comment

Choose a reason for hiding this comment

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

Nice. This looks good to me after adding a few tests.

Comment on lines 42 to 43
let dependency_index = database_key_index;
if !zalsa_local.is_output_of_active_query(dependency_index) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit

Suggested change
let dependency_index = database_key_index;
if !zalsa_local.is_output_of_active_query(dependency_index) {
if !zalsa_local.is_output_of_active_query(database_key_index) {

src/interned.rs Outdated
Comment on lines 71 to 74
// The revision the value was first interned in.
first_interned_at: Revision,
// The most recent interned revision.
last_interned_at: AtomicRevision,
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// The revision the value was first interned in.
first_interned_at: Revision,
// The most recent interned revision.
last_interned_at: AtomicRevision,
/// The revision the value was first interned in.
first_interned_at: Revision,
/// The most recent interned revision.
last_interned_at: AtomicRevision,

Copy link
Member

@nikomatsakis nikomatsakis left a comment

Choose a reason for hiding this comment

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

Yes, looks good to me!

@nikomatsakis
Copy link
Member

I see there is a test failure....have you investigated it at all?

@nikomatsakis
Copy link
Member

My hunch is that the test is testing behavior that's not relevant anymore but I can look.

@ibraheemdev
Copy link
Contributor Author

ibraheemdev commented Nov 4, 2024

@nikomatsakis I'm also hitting the assertion error in this example:

#[salsa::tracked]
fn function<'db>(db: &'db dyn Database, input: Input) -> Interned<'db> {
    Interned::new(db, 0)
}

let input = Input::new(&db, 0);
function(&db, input);
input.set_field1(&mut db).to(1);
function(&db, input);

The function does not read the input so the interned value is memoized, meaning it is not re-interned in R2. What should be done in this case (and the similar failing test)?

I'm also a little unclear about the testing instructions. Should I add a log event for when values are re-interned, or is there another way I can test that? I'm also not sure how to access the reset method that I added in tests.

@MichaReiser
Copy link
Contributor

I'm also a little unclear about the testing instructions. Should I add a log event for when values are re-interned, or is there another way I can test that?

That would sound reasonable to me, similar to the backdate event. We could also consider adding an event when a value is garbage collected.

@MichaReiser
Copy link
Contributor

If I understand it correctly the problem is that interned ingredients created outside of a query are never revalidated because there's no corresponding query that creates them.

I see two options:

  1. We forbid creating interned values outside of queries. That would match the behavior with tracked structs. The downside is that it technically becomes impossible to call a root salsa query with more than one argument because multi-argument functions intern the arguments (outside the query).
  2. We disable garbage collection for ingredients created outside of queries because salsa can't track their creation. Salsa has to consider them as always referenced. That's a bit of a footgun but probably fine?

@MichaReiser
Copy link
Contributor

I took a closer look at test_leaked_inputs_ignored and this is a slightly different variation of the above problem in the sense that the multi-argument-query is called as part of another query, but said query never re-executes and id_to_input panics.

I think the problem here is that function::maybe_changed_after never calls id_to_input when it short-circuits where it probably should, to at least mark the input still as used. However, it's unclear to me how that would work when the query using an interner is deeper in the tree (but the outermost query short-circuits, e.g. because the durability is higher)

@nikomatsakis
Copy link
Member

We had a brief discussion in our meeting today--- @ibraheemdev I think values that are interned outside of any salsa query have to be concerned immortal.

Comment on lines +249 to +257
if value.first_interned_at > revision {
// The slot was reused.
return true;
}

if value.first_interned_at < revision {
// The slot is valid in this revision but we have to sync the value's revision.
value.last_interned_at.store(db.zalsa().current_revision());
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I already discussed this with @ibraheemdev but I document it here in case anyone else ends up reviewing this PR or tries to fix the failing test.

The fix here is to always set last_interned_at even then the first_interned_at is equal to revision

Suggested change
if value.first_interned_at > revision {
// The slot was reused.
return true;
}
if value.first_interned_at < revision {
// The slot is valid in this revision but we have to sync the value's revision.
value.last_interned_at.store(db.zalsa().current_revision());
}
if value.first_interned_at > revision {
// The slot was reused.
true
} else {
// The slot is valid in this revision but we have to sync the value's revision.
value.last_interned_at.store(db.zalsa().current_revision());
false
}

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.

3 participants