Skip to content

Commit

Permalink
add unloading of tasks to GC (#8671)
Browse files Browse the repository at this point in the history
### Description

Inactive tasks can be completely unloaded. This disconnects them from the graph and frees nearly all memory (except for the TaskId mapping).

When running GC for a task we check if it's inactive and unload it when it is.

But we also need to enqueue a task for GC again when it becomes inactive. So we maintain a queue of potentially inactive tasks and walk the children during GC.
  • Loading branch information
sokra committed Jul 15, 2024
1 parent f5a1e7d commit c454e35
Show file tree
Hide file tree
Showing 5 changed files with 417 additions and 166 deletions.
8 changes: 4 additions & 4 deletions crates/turbo-tasks-memory/src/aggregation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl<I, A> AggregationNode<I, A> {
AggregationNode::Leaf {
aggregation_number, ..
} => *aggregation_number as u32,
AggregationNode::Aggegating(aggegating) => aggegating.aggregation_number,
AggregationNode::Aggegating(aggregating) => aggregating.aggregation_number,
}
}

Expand All @@ -92,21 +92,21 @@ impl<I, A> AggregationNode<I, A> {
fn uppers(&self) -> &CountHashSet<I> {
match self {
AggregationNode::Leaf { uppers, .. } => uppers,
AggregationNode::Aggegating(aggegating) => &aggegating.uppers,
AggregationNode::Aggegating(aggregating) => &aggregating.uppers,
}
}

fn uppers_mut(&mut self) -> &mut CountHashSet<I> {
match self {
AggregationNode::Leaf { uppers, .. } => uppers,
AggregationNode::Aggegating(aggegating) => &mut aggegating.uppers,
AggregationNode::Aggegating(aggregating) => &mut aggregating.uppers,
}
}

fn followers(&self) -> Option<&CountHashSet<I>> {
match self {
AggregationNode::Leaf { .. } => None,
AggregationNode::Aggegating(aggegating) => Some(&aggegating.followers),
AggregationNode::Aggegating(aggregating) => Some(&aggregating.followers),
}
}
}
Expand Down
58 changes: 39 additions & 19 deletions crates/turbo-tasks-memory/src/cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,14 @@ impl Cell {
}
}

/// Assigns a new content to the cell. Will notify dependent tasks if the
/// content has changed.
/// If clean = true, the task inputs weren't changes since the last
/// execution and can be assumed to produce the same content again.
pub fn assign(
&mut self,
content: CellContent,
clean: bool,
turbo_tasks: &dyn TurboTasksBackendApi<MemoryBackend>,
) {
match self {
Expand All @@ -175,31 +180,46 @@ impl Cell {
ref mut dependent_tasks,
} => {
event.notify(usize::MAX);
// Assigning to a cell will invalidate all dependent tasks as the content might
// have changed.
if !dependent_tasks.is_empty() {
turbo_tasks.schedule_notify_tasks_set(dependent_tasks);
if clean {
// We can assume that the task is deterministic and produces the same content
// again. No need to notify dependent tasks.
*self = Cell::Value {
content,
dependent_tasks: take(dependent_tasks),
};
} else {
// Assigning to a cell will invalidate all dependent tasks as the content might
// have changed.
if !dependent_tasks.is_empty() {
turbo_tasks.schedule_notify_tasks_set(dependent_tasks);
}
*self = Cell::Value {
content,
dependent_tasks: AutoSet::default(),
};
}
*self = Cell::Value {
content,
dependent_tasks: AutoSet::default(),
};
}
&mut Cell::TrackedValueless {
ref mut dependent_tasks,
} => {
// Assigning to a cell will invalidate all dependent tasks as the content might
// have changed.
// TODO this leads to flagging task unnecessarily dirty when a GC'ed task is
// recomputed. We need to use the notification of changed cells for the current
// task to check if it's valid to skip the invalidation here
if !dependent_tasks.is_empty() {
turbo_tasks.schedule_notify_tasks_set(dependent_tasks);
if clean {
// We can assume that the task is deterministic and produces the same content
// again. No need to notify dependent tasks.
*self = Cell::Value {
content,
dependent_tasks: take(dependent_tasks),
};
} else {
// Assigning to a cell will invalidate all dependent tasks as the content might
// have changed.
if !dependent_tasks.is_empty() {
turbo_tasks.schedule_notify_tasks_set(dependent_tasks);
}
*self = Cell::Value {
content,
dependent_tasks: AutoSet::default(),
};
}
*self = Cell::Value {
content,
dependent_tasks: AutoSet::default(),
};
}
Cell::Value {
content: ref mut cell_content,
Expand Down
Loading

0 comments on commit c454e35

Please sign in to comment.