Here you have the version with work being replaced by Libc.systemsleep
. Spoiler - it behaves the same: sleep
internals are messing things up for the sleep
function (the exact same outcome as the OP MWE version).
using Base.Threads, Dates
@info "started with $(nthreads()) threads"
@info "interactive threadpool: $(nthreads(:interactive))"
@info "default threadpool: $(nthreads(:default))"
println(repeat("*", 40))
function mainspin(s, c; withyield=false)
@info "mainspin executing on thread $(threadid()) "
r = 0.0
counter = 0
while counter < c
Libc.systemsleep(s)
counter += 1
if withyield
yield()
end
end
end
function sleeper(s, c, id)
@info "sleeper $id executing on thread $(threadid()) "
counter = 0
@time "sleeper $id" while counter < c
sleep(s)
counter += 1
end
end
function libcworker(s, c, id)
@info "worker $id executing on thread $(threadid()) "
r = 0.0
counter = 0
@time "worker $id" while counter < c
Libc.systemsleep(s)
counter += 1
yield()
end
end
begin
# phase 1
@info "phase 1: control run - no main thread work"
# yields 10 times (total time ~10 seconds)
task1 = @spawn libcworker(1.02, 10, 1) # 1.02 because weird @time printing overlap
# yields 10 times (total time ~10 seconds)
task11 = @spawn sleeper(1, 10, 1)
wait(task1)
wait(task11)
# we waited for task1 and task11 to finish
# phase 2
println(repeat("*", 40))
@info "phase 2: main thread work without yield"
# yields 10 times (total time ~10 seconds)
task2 = @spawn libcworker(1, 10, 2)
# yields 10 times (total time ~10 seconds)
task22 = @spawn sleeper(1, 10, 2)
# we wait 5 seconds before starting to spin
# the main thread
sleep(5)
# now we start spinning
# while task2 and task22 are already running
mainspin(1, 7)
# main thread work does not impact the real worker
# total time ~10 seconds
wait(task2)
# main thread work heavily impacting the sleeper
# total time >15 seconds
wait(task22)
# phase 3
println(repeat("*", 40))
@info "phase 3: main thread work with yield"
# yields 10 times (total time ~10 seconds)
task3 = @spawn libcworker(1, 10, 3)
# yields 10 times (total time ~10 seconds)
task33 = @spawn sleeper(1, 10, 3)
# we wait 5 seconds before starting to spin
# the main thread
sleep(5)
# now we start spinning with yield
# while task3 and task33 are running
mainspin(1, 7, withyield=true)
# main thread work does not impact
# the real worker - total time ~10 seconds
wait(task3)
# main thread work (with yield) still impacting the sleeper
# but we consistently get better times (total time ~13 seconds)
wait(task33)
end
Outcome with Libc.systemsleep
:
[ Info: started with 12 threads
[ Info: interactive threadpool: 6
[ Info: default threadpool: 12
****************************************
[ Info: phase 1: control run - no main thread work
[ Info: worker 1 executing on thread 7
[ Info: sleeper 1 executing on thread 8
sleeper 1: 10.020645 seconds (55 allocations: 1.562 KiB)
worker 1: 10.203365 seconds (7.26 k allocations: 535.814 KiB, 0.38% compilation time)
****************************************
[ Info: phase 2: main thread work without yield
[ Info: sleeper 2 executing on thread 16
[ Info: worker 2 executing on thread 9
[ Info: mainspin executing on thread 1
worker 2: 10.004138 seconds (12.28 k allocations: 864.277 KiB, 0.22% compilation time)
sleeper 2: 16.967642 seconds (12.34 k allocations: 867.816 KiB, 0.13% compilation time)
****************************************
[ Info: phase 3: main thread work with yield
[ Info: worker 3 executing on thread 7
[ Info: sleeper 3 executing on thread 9
[ Info: mainspin executing on thread 1
worker 3: 10.003031 seconds (37.10 k allocations: 2.621 MiB, 0.07% compilation time)
sleeper 3: 14.002402 seconds (2.67 k allocations: 249.821 KiB, 0.05% compilation time)