.. /index

Tokio

Tokio

Tokio is Rust's dominant async runtime and the foundation of its networking ecosystem — axum, tonic, hyper, and tower all build on it. As of 2026, no other runtime comes close to matching its ecosystem breadth, and it remains the correct default for async Rust.

Performance tuning

The default multi-threaded work-stealing scheduler is convenient but leaves performance on the table. For maximum throughput, run Tokio's current_thread runtime in a thread-per-core pattern — TechEmpower benchmarks show 1.5–2x throughput over the default scheduler due to better cache locality and zero cross-thread synchronization.

The "poor person's thread-per-core" pattern: run N independent tokio::runtime::Builder::new_current_thread() instances with SO_REUSEPORT. This is validated by TechEmpower's top Rust entries and is the recommended approach for most Rust developers who want shard-per-core benefits without abandoning the Tokio ecosystem.

Tokio vs io_uring runtimes

io-uring-native runtimes like monoio achieve ~3x Tokio throughput at 16 cores, but come with severe trade-offs:

Tokio's readiness-based model (epoll) avoids all of these. For file I/O, Tokio delegates to a blocking thread pool (up to 512 threads), which is 29x slower than io_uring for random reads — but adequate for most workloads.

Structured concurrency with JoinSet

tokio::task::JoinSet is the modern pattern for dynamic task groups — spawn tasks, collect results, and get automatic cancellation on drop:

use tokio::task::JoinSet;

async fn fetch_all(urls: Vec<String>) -> Vec<String> {
    let mut set = JoinSet::new();
    for url in urls {
        set.spawn(async move { reqwest::get(&url).await.unwrap().text().await.unwrap() });
    }
    let mut results = Vec::new();
    while let Some(res) = set.join_next().await {
        results.push(res.unwrap());
    }
    results // JoinSet cancels remaining tasks on drop
}

Use tokio::join! for fixed concurrency and select! for racing. JoinSet's drop-cancellation provides structured concurrency guarantees — no leaked tasks. With async fn in traits (1.75) and async closures (1.85), Tokio-based code is now dramatically less boilerplatey.

Ecosystem integration

The bytes crate is the standard buffer type throughout Tokio's ecosystem. tracing is the recommended diagnostics layer, and tokio-console provides real-time async task debugging. For message passing within Tokio, tokio::sync::mpsc benefits from intra-thread coroutine switching that can outperform standalone channels like kanal when sender and receiver share a worker thread.

The ecosystem moat

Tokio's deepest advantage isn't performance — it's that the entire Rust async ecosystem builds on its traits and runtime. Migrating to compio, monoio, or glommio means abandoning this ecosystem or writing compatibility shims that negate performance gains. This lock-in is also a feature: any library you pick up just works.

Linked from

Sources