-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Refactor scheduler and implement spinner thread for Partr. #56475
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
# This file is a part of Julia. License is MIT: https://julialang.org/license | ||
|
||
module Scheduler | ||
|
||
""" | ||
cong(max::UInt32) | ||
|
||
Return a random UInt32 in the range `1:max` except if max is 0, in that case return 0. | ||
""" | ||
cong(max::UInt32) = iszero(max) ? UInt32(0) : rand_ptls(max) + UInt32(1) #TODO: make sure users don't use 0 and remove this check | ||
|
||
get_ptls_rng() = ccall(:jl_get_ptls_rng, UInt64, ()) | ||
|
||
set_ptls_rng(seed::UInt64) = ccall(:jl_set_ptls_rng, Cvoid, (UInt64,), seed) | ||
|
||
""" | ||
rand_ptls(max::UInt32) | ||
|
||
Return a random UInt32 in the range `0:max-1` using the thread-local RNG | ||
state. Max must be greater than 0. | ||
""" | ||
Base.@assume_effects :removable :inaccessiblememonly :notaskstate function rand_ptls(max::UInt32) | ||
rngseed = get_ptls_rng() | ||
val, seed = rand_uniform_max_int32(max, rngseed) | ||
set_ptls_rng(seed) | ||
return val % UInt32 | ||
end | ||
|
||
# This implementation is based on OpenSSLs implementation of rand_uniform | ||
# https://github.com/openssl/openssl/blob/1d2cbd9b5a126189d5e9bc78a3bdb9709427d02b/crypto/rand/rand_uniform.c#L13-L99 | ||
# Comments are vendored from their implementation as well. | ||
# For the original developer check the PR to swift https://github.com/apple/swift/pull/39143. | ||
|
||
# Essentially it boils down to incrementally generating a fixed point | ||
# number on the interval [0, 1) and multiplying this number by the upper | ||
# range limit. Once it is certain what the fractional part contributes to | ||
# the integral part of the product, the algorithm has produced a definitive | ||
# result. | ||
""" | ||
rand_uniform_max_int32(max::UInt32, seed::UInt64) | ||
|
||
Return a random UInt32 in the range `0:max-1` using the given seed. | ||
Max must be greater than 0. | ||
""" | ||
Base.@assume_effects :total function rand_uniform_max_int32(max::UInt32, seed::UInt64) | ||
if max == UInt32(1) | ||
return UInt32(0), seed | ||
end | ||
# We are generating a fixed point number on the interval [0, 1). | ||
# Multiplying this by the range gives us a number on [0, upper). | ||
# The high word of the multiplication result represents the integral part | ||
# This is not completely unbiased as it's missing the fractional part of the original implementation but it's good enough for our purposes | ||
seed = UInt64(69069) * seed + UInt64(362437) | ||
prod = (UInt64(max)) * (seed % UInt32) # 64 bit product | ||
i = prod >> 32 % UInt32 # integral part | ||
return i % UInt32, seed | ||
end | ||
|
||
include("scheduler/partr.jl") | ||
|
||
const ChosenScheduler = Partr | ||
|
||
|
||
|
||
# Scheduler interface: | ||
# enqueue! which pushes a runnable Task into it | ||
# dequeue! which pops a runnable Task from it | ||
# checktaskempty which returns true if the scheduler has no available Tasks | ||
|
||
enqueue!(t::Task) = ChosenScheduler.enqueue!(t) | ||
dequeue!() = ChosenScheduler.dequeue!() | ||
checktaskempty() = ChosenScheduler.checktaskempty() | ||
|
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -937,7 +937,6 @@ end | |
|
||
function enq_work(t::Task) | ||
(t._state === task_state_runnable && t.queue === nothing) || error("schedule: Task not runnable") | ||
|
||
# Sticky tasks go into their thread's work queue. | ||
if t.sticky | ||
tid = Threads.threadid(t) | ||
|
@@ -968,19 +967,40 @@ function enq_work(t::Task) | |
ccall(:jl_set_task_tid, Cint, (Any, Cint), t, tid-1) | ||
push!(workqueue_for(tid), t) | ||
else | ||
# Otherwise, put the task in the multiqueue. | ||
Partr.multiq_insert(t, t.priority) | ||
# Otherwise, push the task to the scheduler | ||
Scheduler.enqueue!(t) | ||
tid = 0 | ||
end | ||
end | ||
ccall(:jl_wakeup_thread, Cvoid, (Int16,), (tid - 1) % Int16) | ||
|
||
if (tid == 0) | ||
ccall(:jl_wake_any_thread, Cvoid, (Any,), current_task()) | ||
else | ||
ccall(:jl_wakeup_thread, Cvoid, (Int16,), (tid - 1) % Int16) | ||
end | ||
return t | ||
end | ||
|
||
const ChildFirst = false | ||
|
||
function schedule(t::Task) | ||
# [task] created -scheduled-> wait_time | ||
maybe_record_enqueued!(t) | ||
enq_work(t) | ||
if ChildFirst | ||
ct = current_task() | ||
if ct.sticky || t.sticky | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should actually check if set_task_tid succeeded so that this isn't a data race here (even though this is dead code right now) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually any use of yieldto seems to have this problem, so maybe it deserves another look |
||
maybe_record_enqueued!(t) | ||
enq_work(t) | ||
else | ||
maybe_record_enqueued!(t) | ||
enq_work(ct) | ||
yieldto(t) | ||
end | ||
else | ||
maybe_record_enqueued!(t) | ||
enq_work(t) | ||
end | ||
return t | ||
end | ||
|
||
""" | ||
|
@@ -1186,10 +1206,10 @@ function trypoptask(W::StickyWorkqueue) | |
end | ||
return t | ||
end | ||
return Partr.multiq_deletemin() | ||
return Scheduler.dequeue!() | ||
end | ||
|
||
checktaskempty = Partr.multiq_check_empty | ||
checktaskempty = Scheduler.checktaskempty | ||
|
||
function wait() | ||
ct = current_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.
Should we for now not add ChildFirst?
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.
Yeah, it's not even correct for now.