NIOFS has to offload the blocking I/O calls onto an auxiliary thread pool, away from the Swift Concurrency pools where you mustn't block. Apart from truly async file stuff like IOCP or io_uring that's really the only correct option.
But this has at least two consequences
- Multiple dispatches are necessary or a one shot I/O (like read/write full file) -> perf could be better
- Users who do not control the concurrency levels (yes, they really have to or else OOM and other issues will hit them later) may face a situation where we run out of file descriptors. For example, if they dispatch 10,000 file reads at the same time. In that situation we may attempt the 10,000 opens first and in later dispatches try the reads. This will likely be hit an out of file descriptors problem.
The best way to fix this, improving performance as well as papering over potential user concurrency limit bugs in user code is to make sure that one-shot I/Os are fully handled in just one I/O offload.
As in, instead of
Offload { open }
Offload { read }
Offload { close }
Do
Offload { open; read; close }
Instead
NIOFS has to offload the blocking I/O calls onto an auxiliary thread pool, away from the Swift Concurrency pools where you mustn't block. Apart from truly async file stuff like IOCP or io_uring that's really the only correct option.
But this has at least two consequences
The best way to fix this, improving performance as well as papering over potential user concurrency limit bugs in user code is to make sure that one-shot I/Os are fully handled in just one I/O offload.
As in, instead of
Do
Instead