I was optimizing a complex block of multithreaded code and discovered that sleep was allocating.
julia> @btime sleep(0)
1.720 μs (5 allocations: 144 bytes)
I was pretty surprised, so checked the source which is basically
wait(Timer(sec)), so it’s allocating a mutable struct for that Timer. Is there a more efficient alternative to that to sleep for a very short time?
I found a reference to usleep but I’m on Windows so that’s not an option for me.
I need a thread to sleep briefly (milliseconds) waiting for more work to come in. I think using yield instead might work for me, but it would be nice to have an efficient sleep option (or improve my understanding if it’s not possible).
sleep(0) only takes 2 microseconds then sleeping for a millisecond (500x longer) should have negligible overhead.
Libc.systemsleep() works on Linux too, and neither allocates there. So I’m puzzled why
sleep() just doesn’t call it.
Anyway what I was digging into, until I saw that answer:
I saw a reason for some of the allocations, and was thinking of making a PR to fix:
timeout ≥ 0 || throw(ArgumentError("timer cannot have negative timeout of $timeout seconds"))
interval ≥ 0 || throw(ArgumentError("timer cannot have negative repeat interval of $interval seconds"))
this = new(Libc.malloc(_sizeof_uv_timer), ThreadSynchronizer(), true, false)
LazyString was added in Julia 1.8, so that you could change like this I believe:
timeout ≥ 0 || throw(ArgumentError(lazy"timer cannot have negative timeout of $timeout seconds"))
timeout ≥ 0 || throw(ArgumentError(LazyString("timer cannot have negative timeout of $timeout seconds")))
It’s good to know about the LazyString, because untaken
throw exceptions can allocate and slow down your code (when string interpolation is used).
I don’t have a fix for the malloc.
What kind of
Libc.systemsleep? The regular (allocating) version has the benefit of allowing other julia tasks to run in the background, since it’s yielding to the scheduler.
Thanks for the mention of Libc.systemsleep. Unfortunately, the doc states: “This function does not yield to Julia’s scheduler and therefore blocks the Julia thread that it is running on for the duration of the sleep time.” This sounds like it won’t be useful. I thought I would test it. I ran the code below and it does hang for 4 seconds (when running Julia with only one thread), so… it appears it won’t do what I need (if the test is correct). The output for duration seems a little odd to me, though.
println("end $(Threads.threadid())") )
t1 = time()
t2 = time()
println("duration: $(t2 - t1)")