-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Compiler: add Task call, invoke, and fetch optimizations and operations #59221
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
base: master
Are you sure you want to change the base?
Conversation
This commit implements a Task optimizations by adding a `invoked` field to the Task definition, which optionally causes it to do an `invoke` call internally instead of a regular `call`. Key changes: - Add `invoked` field to Task structure, supporting Type, Method, MethodInstance, and CodeInstance, just like invoke - Implement `_task` builtin function to construct Task - Create PartialTask lattice element for precise task result and error type tracking - Unify several CallInfo types into IndirectCallInfo - Optimized calls to `_task` constructor to inject CodeInstance. Calling `fully_covers` isn't necessary, as the runtime will check that. In the future we can make this field user-inaccessible (like scope) and then be able to optimize based on the type returned from `fetch`. Also added documentation to base/docs/basedocs.jl on how to easily add more Builtin functions, following this as an example. 🤖 Generated with help from [Claude Code](https://claude.ai/code)
- `modifyglobal!(mod, var, op, x, order)` where `op(getval(), x)` is called | ||
- `memoryrefmodify!(memref, op, x, order, boundscheck)` where `op(getval(), x)` is called | ||
- `Intrinsics.atomic_pointermodify(ptr, op, x, order)` where `op(getval(), x)` is called | ||
- `Core._task(f, size)` where `f()` will be called when the task runs |
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.
Aren't task and finalizer different from the modify functions in that the modify functions eagerly call the argument at the call site, while task and finalizer are deferred? I thought that was important.
@@ -498,6 +498,10 @@ function serialize(s::AbstractSerializer, t::Task) | |||
if istaskstarted(t) && !istaskdone(t) | |||
error("cannot serialize a running Task") | |||
end | |||
if isdefined(t, :invoked) | |||
error("cannot serialize a Task constructed with invoke info") |
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 this is helpful, since if you hit this you can't really control it. We could just skip this field and hope it works out.
@@ -2,7 +2,11 @@ | |||
|
|||
## basic task functions and TLS | |||
|
|||
Core.Task(@nospecialize(f), reserved_stack::Int=0) = Core._Task(f, reserved_stack, ThreadSynchronizer()) | |||
Core.Task(@nospecialize(f), reserved_stack::Int=0) = begin |
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.
Core.Task(@nospecialize(f), reserved_stack::Int=0) = begin | |
function Core.Task(@nospecialize(f), reserved_stack::Int=0) |
@@ -2160,6 +2160,18 @@ JL_CALLABLE(jl_f__svec_ref) | |||
return jl_svecref(s, idx-1); | |||
} | |||
|
|||
JL_CALLABLE(jl_f__task) |
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 wonder how much overhead this has. Might need codegen support.
Any benchmarks? |
end | ||
# Check that size argument is an Int | ||
size_arg = argtypes[3] | ||
if !(widenconst(size_arg) ⊑ Int) |
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.
if !(widenconst(size_arg) ⊑ Int) | |
if !hasintersect(widenconst(size_arg), Int) |
if la < 3 || la > 4 | ||
return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) | ||
end | ||
# Check that size argument is an Int |
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.
# Check that size argument is an Int |
# Check argument count: _task(func, size) or _task(func, size, ci) | ||
if la < 3 || la > 4 | ||
return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) | ||
end |
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.
# Check argument count: _task(func, size) or _task(func, size, ci) | |
if la < 3 || la > 4 | |
return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) | |
end | |
if !isempty(argtypes) && !isvarargtype(argtypes[end]) | |
if !(3 <= la <= 4) | |
return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) | |
end | |
elseif isempty(argtypes) || la > 5 | |
return Future(CallMeta(Bottom, Any, EFFECTS_THROWS, NoCallInfo())) | |
end |
I was unable to confirm optimization results even in the simplest case on my end unfortunately. julia> func2(i) = fetch(Threads.@spawn sin(i));
julia> using BenchmarkTools
julia> @benchmark func2(42)
|
It was entirely process_events and scheduling issues for me, which are separately addressable. The main point is inference and trim, not performance at this time. |
This commit implements a Task optimizations by adding a
invoked
field to the Task definition, which optionally causes it to do aninvoke
call internally instead of a regularcall
.Key changes:
invoked
field to Task structure, supporting Type, Method, MethodInstance, and CodeInstance, just like invoke_task
builtin function to construct Task_task
constructor to inject CodeInstance. Callingfully_covers
isn't necessary, as the runtime will check that.In the future we can consider making this field user-inaccessible (like scope) and then be able to optimize based on the type returned from
fetch
.Also added documentation to base/docs/basedocs.jl on how to easily add more Builtin functions, following this as an example.
🤖 Generated with help from Claude Code