Skip to content
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

add spawn_blocking and use a blocking thread pool #501

Merged
merged 6 commits into from
Jan 17, 2022

Conversation

HippoBaro
Copy link
Member

Sometimes there is no way around doing a long, unyielding job. That's
the world we live in. These fall into two categories:

  • Busy blocking jobs: usually, these are calls to external libraries
    that aren't cooperative and can't be easily broken into bite-size
    pieces. Your usual suspects are compression/decompression and
    marshaling/unmarshalling.
  • Non-busy blocking jobs: stuff that acquires locks or syscalls waiting
    for events.

spawn_blocking works for both cases but is best suited for the latter
because they don't steal CPU cycles from the executor.

So far, we have had a single thread per executor used for blocking
operations. This has been fine to issue the odd syscall that couldn't go
into the ring, but now that we have spawn_blocking, we've opened
pandora's box. Users will submit all sorts of things in there, including
long-running jobs that could stall the entire system.

This commit introduces a static thread pool per executor instead. The
pool is static for now, meaning the threads are created once and live
for as long as the host executor does. Later on, we may want to be
smarter and automatically up/downscale the pool depending on usage.

This work is based #500.

@HippoBaro HippoBaro changed the title Add spawn_blocking and use a blocking thread pool ddd spawn_blocking and use a blocking thread pool Jan 14, 2022
@HippoBaro HippoBaro changed the title ddd spawn_blocking and use a blocking thread pool add spawn_blocking and use a blocking thread pool Jan 14, 2022
examples/echo.rs Outdated Show resolved Hide resolved
glommio/src/sys/mod.rs Outdated Show resolved Hide resolved
glommio/src/executor/placement/mod.rs Outdated Show resolved Hide resolved
glommio/src/executor/mod.rs Outdated Show resolved Hide resolved
glommio/src/executor/mod.rs Show resolved Hide resolved
glommio/src/sys/blocking.rs Show resolved Hide resolved
glommio/src/sys/uring.rs Outdated Show resolved Hide resolved
placement: PoolPlacement,
sleep_notifier: Arc<SleepNotifier>,
) -> io::Result<Self> {
let (in_tx, in_rx) = crossbeam::channel::unbounded();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we use bounded channels?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally I would like to find a channel that would have an async TX and a sync RX.

I don't think it is okay to panic if the que is full like we did before.

And it's also not okay to bubble up blocking to user code. This is meant to be async.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So an ideal way to deal with this would be to provide async back pressure with an async TX. I haven't found one though.

I think there is a way to use tokio channels that way. I'll look into it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See if this does what you need:
https://docs.rs/async-channel/latest/async_channel/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't. Tokio channel however does have a blocking_recv(), but that would imply us depending on Tokio. Going to merge as-is but I agree this is something we'll need to keep an eye on.

Sometimes there is no way around doing a long, unyielding job. That's
the world we live in. These fall into two categories:
* Busy blocking jobs: usually, these are calls to external libraries
  that aren't cooperative and can't be easily broken into bite-size
  pieces. Your usual suspects are compression/decompression and
  marshaling/unmarshalling.
* Non-busy blocking jobs: stuff that acquires locks or syscalls waiting
  for events.

`spawn_blocking` works for both cases but is best suited for the latter
because they don't steal CPU cycles from the executor.
So far, we have had a single thread per executor used for blocking
operations. This has been fine to issue the odd syscall that couldn't go
into the ring, but now that we have `spawn_blocking,` we've opened
pandora's box. Users will submit all sorts of things in there, including
long-running jobs that could stall the entire system.

This commit introduces a static thread pool per executor instead. The
pool is static for now, meaning the threads are created once and live
for as long as the host executor does. Later on, we may want to be
smarter and automatically up/downscale the pool depending on usage.
Add knobs in the local executor builders to configure how many blocking
threads to create in the pool and where to place them on the system. By
default, an executor spawns a single blocking thread in its pool and
collocates it with its thread.

It could be a good idea to loosen that default placement strategy to
bind the blocking thread to any CPUs on the same package as the executor
thread. This would maximize resource utilization and minimize memory
latency. However, there is no easy way to do that using our existing
Placement APIs.
Small performance improvement. Don't allocate a dynamic blocking
operation handler when it isn't necessary.
If a user submits a blocking operation from a latency-sensitive queue,
the blocking thread should unconditionally wake the executor to trigger
a preemption. This is the same principle that applies to foreign
wakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants