# Julia seems an order of magnitude slower than Python when printing to the terminal, because of issue with "sleep"

**URL:** https://discourse.julialang.org/t/julia-seems-an-order-of-magnitude-slower-than-python-when-printing-to-the-terminal-because-of-issue-with-sleep/78151
**Category:** General Usage
**Tags:** performance
**Created:** [March 20, 2022, 4:17am UTC](https://discourse.julialang.org/t/julia-seems-an-order-of-magnitude-slower-than-python-when-printing-to-the-terminal-because-of-issue-with-sleep/78151 "2022-03-20T04:17:43Z")
**Posts on this page:** 8
**Page:** 4

<div class="post-metadata">

### Author: ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)
#### Post date: [June 28, 2024, 7:37am UTC](https://discourse.julialang.org/t/julia-seems-an-order-of-magnitude-slower-than-python-when-printing-to-the-terminal-because-of-issue-with-sleep/78151/62 "2024-06-28T07:37:36Z")

</div>

> [@Sukera](#):
>
> That depends on what exactly you mean with “nonblocking”. Inherently, `systemsleep` _is_ blocking, since it prevents the thread currently executing from doing other more useful work.

I just mean blocking the main thread. In this model you would want to allocate an extra thread when starting Julia so that one thread can just be used for sleeping. (I agree this is not a great solution, but regular `sleep` is so heavy that there’s no other option).

> [@Sukera](#):
>
> `Threads.nthreads()` is not a constant

Really? Is this a recent change? (Does this mean I can dynamically create a thread for sleeping?)

> [@Sukera](#):
>
> The Libc version isn’t necessarily lightweight either. On my machine (Linux), this ends up calling `usleep`, which also doesn’t guarantee any upper bounds on execution time:

Yeah it’s not a perfect solution. But on Linux, `Libc.systemsleep` is much quicker than `Base.sleep` (100x faster – lower end of 10 microseconds compared to 1 millisecond), which is enough of a win for me to want to use it.

It would be nice if there was `waitany` version of `wait(::Channel)` to react to any channel in a `Vector{Channel}` being written to. Right now I have to loop through them individually and then sleep – that sleep is what’s bottlenecking my performance a bit. Although `wait` isn’t great either, because my main thread is also doing other stuff in between polls. So a fast `sleep` is really useful here.

---

<div class="post-metadata">

### Author: ![Sukera](https://avatars.discourse-cdn.com/v4/letter/s/ce7236/32.png) [@Sukera](https://discourse.julialang.org/u/Sukera)
#### Post date: [June 28, 2024, 8:01am UTC](https://discourse.julialang.org/t/julia-seems-an-order-of-magnitude-slower-than-python-when-printing-to-the-terminal-because-of-issue-with-sleep/78151/63 "2024-06-28T08:01:08Z")

</div>

> [@MilesCranmer](#):
>
> Really? Is this a recent change?

That’s been a thing since 1.9 - see also [PSA: Thread-local state is no longer recommended](https://julialang.org/blog/2023/07/PSA-dont-use-threadid/) for some examples of how relying on `nthreads` being constant leads to buggy code.

> [@MilesCranmer](#):
>
> It would be nice if there was `waitany` version of `wait(::Channel)` to react to any channel in a `Vector{Channel}` being written to.

There will be with 1.12:

> <https://github.com/JuliaLang/julia/pull/53341>
>
> I would like to propose adding two functions, \`waitany\` and \`waitall\`, discussed… in the issue #53226. These functions wait for multiple tasks at once. The \`waitany\` function blocks until one task finishes. The \`waitall\` function blocks until all tasks finish. There is an optional keyword argument, \`failfast\`, for the \`waitall\` function. The default of \`failfast\` is \`false\`. The \`waitall\` function will immediately stop if any task ends with an exception when the \`failfast\` is \`true\`.
> 
> This is my own implementation, but I have regrets about the type of the first argument. I wanted to represent a container type from which \`Task\` objects can be taken out using the \`iterate\` function, but it seems impossible in current Julia, so I used a union type of \`AbstractVector\`, \`Tuple\`, and \`Set\`. I would like to know if there is a better way to write this part.

---

<div class="post-metadata">

### Author: ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)
#### Post date: [June 28, 2024, 9:20am UTC](https://discourse.julialang.org/t/julia-seems-an-order-of-magnitude-slower-than-python-when-printing-to-the-terminal-because-of-issue-with-sleep/78151/64 "2024-06-28T09:20:26Z")

</div>

Nice! Thanks!

So I guess `Threads.@spawn Libc.systemsleep` is still the best option for now. Maybe I could also refactor the code to use `waitany` such that the main thread’s only job is waiting and dispatching.

---

<div class="post-metadata">

### Author: ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)
#### Post date: [June 28, 2024, 12:02pm UTC](https://discourse.julialang.org/t/julia-seems-an-order-of-magnitude-slower-than-python-when-printing-to-the-terminal-because-of-issue-with-sleep/78151/65 "2024-06-28T12:02:15Z")

</div>

I had another read into this and the reasons for blocking. I think the best approach to this problem is `@threadcall`: [Multi-Threading · The Julia Language](https://docs.julialang.org/en/v1/manual/multi-threading/#@threadcall) which is designed for this exact problem:

> External libraries, such as those called via `ccall`, pose a problem for Julia’s task-based I/O mechanism. If a C library performs a blocking operation, that prevents the Julia scheduler from executing any other tasks until the call returns. (Exceptions are calls into custom C code that call back into Julia, which may then yield, or C code that calls `jl_yield()`, the C equivalent of `yield`.)
> 
> The `@threadcall` macro provides a way to avoid stalling execution in such a scenario. It schedules a C function for execution in a separate thread. A threadpool with a default size of 4 is used for this. The size of the threadpool is controlled via environment variable `UV_THREADPOOL_SIZE`. While waiting for a free thread, and during function execution once a thread is available, the requesting task (on the main Julia event loop) yields to other tasks. Note that `@threadcall` does not return until the execution is complete. From a user point of view, it is therefore a blocking call like other Julia APIs.

So perhaps the very best way to solve this problem is as follows:

```julia
function systemsleep(seconds::Number)
    microseconds = round(Int, 1e6 * seconds)
    @threadcall(:usleep, Int, (Int,), microseconds)
    return nothing
end

```

which is lighter than `Base.sleep`:

```julia
julia> using BenchmarkTools

julia> @btime systemsleep(1e-6)
  19.208 μs (11 allocations: 496 bytes)

julia> @btime sleep(1e-6)
  1.152 ms (4 allocations: 112 bytes)

```

without blocking the thread, and also doesn’t require the hand-rolled `@spawn` trick.

(This seems like a nice addition to Base maybe?)

The one downside of this is:

> `@threadcall` may be removed/changed in future versions of Julia.

So just be wary.

---

<div class="post-metadata">

### Author: ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)
#### Post date: [June 28, 2024, 12:34pm UTC](https://discourse.julialang.org/t/julia-seems-an-order-of-magnitude-slower-than-python-when-printing-to-the-terminal-because-of-issue-with-sleep/78151/66 "2024-06-28T12:34:24Z")

</div>

Formal feature request: [Feature request: Finer-grained sleep function · Issue #54971 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/54971)

---

<div class="post-metadata">

### Author: ![ufechner7](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ufechner7/32/51363_2.png) [@ufechner7](https://discourse.julialang.org/u/ufechner7)
#### Post date: [June 28, 2024, 12:42pm UTC](https://discourse.julialang.org/t/julia-seems-an-order-of-magnitude-slower-than-python-when-printing-to-the-terminal-because-of-issue-with-sleep/78151/67 "2024-06-28T12:42:09Z")

</div>

I made a similar request years ago, which is still open: [Accuracy and resolution of sleep() on Linux should be improved · Issue #12770 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/12770)

---

<div class="post-metadata">

### Author: ![MilesCranmer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/milescranmer/32/21070_2.png) [@MilesCranmer](https://discourse.julialang.org/u/MilesCranmer)
#### Post date: [June 28, 2024, 1:41pm UTC](https://discourse.julialang.org/t/julia-seems-an-order-of-magnitude-slower-than-python-when-printing-to-the-terminal-because-of-issue-with-sleep/78151/68 "2024-06-28T13:41:19Z")

</div>

Thanks!

(slightly ironic that an issue about sleep being slow has been sitting idle for 9 years 😄)

---

<div class="post-metadata">

### Author: ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)
#### Post date: [June 28, 2024, 1:55pm UTC](https://discourse.julialang.org/t/julia-seems-an-order-of-magnitude-slower-than-python-when-printing-to-the-terminal-because-of-issue-with-sleep/78151/69 "2024-06-28T13:55:28Z")

</div>

> [@MilesCranmer](#):
>
> (slightly ironic that an issue about sleep being slow has been sitting idle for 9 years 😄)

We’re really sleeping on fixing that issue, but no one knows for how long.

[Previous page](https://discourse.julialang.org/t/julia-seems-an-order-of-magnitude-slower-than-python-when-printing-to-the-terminal-because-of-issue-with-sleep/78151.md?page=3)
