A thread-per-core Rust runtime with io_uring/epoll/kqueue.
As a runtime based on io_uring/epoll/kqueue, Monoio is designed to be the most efficient and performant thread-per-core Rust runtime with good platform compatibility.
For some use cases, it is not necessary to make task schedulable between threads. For example, if we want to implement a load balancer like nginx, we may want to write it in a thread-per-core way. The thread local data need not to be shared between threads, so the Sync
and Send
will not have to be implemented.
Also, the Monoio is designed to be efficient. To achieve this goal, we enabled many Rust unstable features like GAT; and we designed a whole new IO abstraction to avoid copying, which may cause some compatibility problems.
Our benchmark shows that Monoio has a better performance than other common Rust runtimes.
To use monoio, you need the latest nightly rust toolchain. If you already installed it, please make sure it is the latest version.
To force using nightly, create a file named rust-toolchain
and write nightly
in it. Also, you can use cargo +nightly
to build or run.
Also, if you want to use io_uring, you must make sure your kernel supports it(5.6+). And, memlock is configured as a proper number. If your kernel version does not meet the requirements, you can try to use the legacy driver to start, currently supports Linux and macOS(ref here).
🚧Experimental windows support is on the way, if you want to use windows you must make sure your windows supports it(Windows Build 22000).
Here is a basic example of how to use Monoio.
/// A echo example.
///
/// Run the example and `nc 127.0.0.1 50002` in another shell.
/// All your input will be echoed out.
use monoio::io::{AsyncReadRent, AsyncWriteRentExt};
use monoio::net::{TcpListener, TcpStream};
#[monoio::main]
async fn main() {
let listener = TcpListener::bind("127.0.0.1:50002").unwrap();
println!("listening");
loop {
let incoming = listener.accept().await;
match incoming {
Ok((stream, addr)) => {
println!("accepted a connection from {}", addr);
monoio::spawn(echo(stream));
}
Err(e) => {
println!("accepted connection failed: {}", e);
return;
}
}
}
}
async fn echo(stream: TcpStream) -> std::io::Result<()> {
let mut buf: Vec<u8> = Vec::with_capacity(8 * 1024);
loop {
// read
let (res, _buf) = stream.read(buf).await;
buf = _buf;
let res: usize = res?;
if res == 0 {
return Ok(());
}
// write all
let (res, _buf) = stream.write_all(buf).await;
buf = _buf;
res?;
// clear
buf.clear();
}
}
You can find more example code in examples
of this repository.
- On Linux 5.6 or newer, Monoio can use uring or epoll as io driver. On lower versions of Linux, it can only run in epoll mode. On macOS, kqueue can be used. Other platforms are currently not supported.
- Monoio can not solve all problems. If the workload is very unbalanced, it may cause performance degradation than Tokio since CPU cores may not be fully utilized.
Thanks for their contributions!
Monoio is a subproject of CloudWeGo. We are committed to building a cloud native ecosystem.
- local-sync: A thread local channel.
- monoio-tls: TLS wrapper for Monoio.
- monoio-codec: Codec utility for Monoio.
HTTP framework and RPC framework are on the way.
Monoio is licensed under the MIT license or Apache license.
During developing we referenced a lot from Tokio, Mio, Tokio-uring and other related projects. We would like to thank the authors of these projects.