27
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Rust の非同期ランタイムの実行速度を比較してみる

Last updated at Posted at 2020-02-01

環境

stable-x86_64-unknown-linux-gnu
rustc 1.41.0 (5e1a79984 2020-01-27)

動機

This Week in Rustこんな記事を見つけたので Rust の非同期ランタイムの実行速度が気になって比較してみました。
tokioactix-rt(内部的には tokio)、futuresasync-std の4つを比較します。

計測

Cargo.toml
[dependencies]
tokio = "0.2"
actix-rt = "1.0"
futures = "0.3"
async-std = "1.4"
main.rs
use tokio;
use actix_rt;
use futures;
use async_std;

use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Instant;

fn main() {
    let mut rt = tokio::runtime::Runtime::new().unwrap();
    let now = Instant::now();
    rt.block_on(Yields(100));
    println!("tokio: {:?}", now.elapsed());

    let mut rt = actix_rt::Runtime::new().unwrap();
    let now = Instant::now();
    rt.block_on(Yields(100));
    println!("actix_rt: {:?}", now.elapsed());

    let now = Instant::now();
    futures::executor::block_on(Yields(100));
    println!("futures: {:?}", now.elapsed());

    let now = Instant::now();
    async_std::task::block_on(Yields(100));
    println!("async_std: {:?}", now.elapsed());
}

struct Yields(i32);

impl Future for Yields {
    type Output = ();
    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
        if self.0 == 0 {
            Poll::Ready(())
        } else {
            self.0 -= 1;
            cx.waker().wake_by_ref();
            Poll::Pending
        }
    }
}

結果

crate time
tokio 126.455µs
actix-rt 138.074µs
futures 7.504µs
async-std 84.609µs
futures が圧倒的に早い。

おまけ

async-std のスケジューラが新しくなるようなので、試してみます。

Cargo.toml
[dependencies]
async-std = { git = 'https://github.com/stjepang/async-std', branch = 'new-scheduler' }
crate time
async-std (new-scheduler) 7.541µs
futures と同じぐらいですね。以上。

追記

tokio とその他 crates のパフォーマンスにあまりに差があるので調べました。
tokio のランライムは Thread Pool を使用するので、そのオーバーヘッドが大きいようです下記参照)。Single Thread の executor を使用するには下記のようになります。(詳細は Runtime Configurations を参照)

let mut basic_rt = tokio::runtime::Builder::new()
        .basic_scheduler().build().unwrap();
let now = Instant::now();
basic_rt.block_on(Yields(100));
println!("tokio (single thread): {:?}", now.elapsed());
crate time
tokio (single thread) 10.5µs
用途に合わせて適切にランタイムを使い分ける必要がありますね。

もうちょっとだけ続く

ちゃんと Feature Flag を立てないと Thread Pool は有効にならないのでした…。

Cargo.toml
[dependencies]
tokio = { version = "0.2", features = ["rt-threaded"] }
futures = { version = "0.3", features = ["thread-pool"] }
let mut rt = tokio::runtime::Runtime::new().unwrap();
let now = Instant::now();
rt.block_on(Yields(100));
println!("tokio (thread pool): {:?}", now.elapsed());

let mut basic_rt = tokio::runtime::Builder::new()
    .basic_scheduler().build().unwrap();
let now = Instant::now();
basic_rt.block_on(Yields(100));
println!("tokio (single thread): {:?}", now.elapsed());

use futures::task::SpawnExt;
let pool = futures::executor::ThreadPool::new().unwrap();
let handle = pool.spawn_with_handle(Yields(100)).unwrap();
let now = Instant::now();
futures::executor::block_on(handle);
println!("futures (thread pool): {:?}", now.elapsed());

let now = Instant::now();
futures::executor::block_on(Yields(100));
println!("futures (single thread): {:?}", now.elapsed());
crate time
tokio (thread pool) 6.633µs
tokio (single thread) 10.551µs
futures (thread pool) 46.969µs
futures (single thread) 5.824µs
う、う〜ん:thinking:.。oO(この比較意味あるの?)

思いつきで書き始めた記事だけど、ちょっとだけ Rust 非同期ランタイムに詳しくなれたような気がした。

27
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?