Calls
Introduction
In Turbo-Engine, calls to turbo task functions are central to the operation of the system. This section explains how calls work within the Turbo-Engine framework, including the concepts of unresolved and resolved Vc<T>
.
Making Calls
#![allow(unused)] fn main() { let vc: Vc<T> = some_turbo_tasks_function(); }
When you invoke a Turbo-Tasks Function, it returns a Vc<T>
, which is a pointer to the task's return value. Here's what happens during a call:
- Immediate Return: The call returns instantly, and the task is queued for computation.
- Unresolved Vc: The returned
Vc
is a so called "unresolved"Vc
, meaning it points to a return value that may not yet be computed.
Resolved vs Unresolved Vc
s
- Unresolved Vc: An "unresolved"
Vc
points to a return value of a Task - Resolved Vc: A "resolved"
Vc
points to a cell computed by a Task.
Resolving Vc
s
To compare Vc
values or ensure they are ready for use, you can resolve them:
#![allow(unused)] fn main() { let resolved = vc.resolve().await?; }
- Reading: It's not mandatory to resolve
Vc
s before reading them. ReadingVc
s will first resolve it and then read the cell value. - Dependency Registration: Resolving a
Vc
registers the current task as a dependent of the task output, ensuring that the task is recomputed if the return value changes. - Usage as Key: Resolving
Vc
s is mandatory when using them as keys ofHashMap
orHashSet
for the cell identity.
Importance of Resolution
Resolution is crucial for the memoization process:
- Memoization:
Vc
s used as arguments are identified by their pointer. - Automatic Resolution: Unresolved
Vc
s passed to turbo task functions are resolved automatically, ensuring that functions receive resolvedVc
s for memoization.- Intermediate Task: An intermediate task is created internally to handle the resolution of arguments.
Performance Considerations
While automatic resolution is convenient, it introduces the slight overhead of the intermediate Task:
- Manual Resolution: In performance-critical scenarios, resolving
Vc
s manually may be beneficial to reduce overhead.
Code Example
Here's an example of calling a turbo task function and resolving its Vc
:
#![allow(unused)] fn main() { // Calling a turbo task function let vc: Vc<MyType> = compute_something(); // Resolving the Vc for direct comparison or use let resolved_vc: MyType = vc.resolve().await?; }