tokio 是 rust 生态中风行的异步运行时框架。在理论生产中咱们如果心愿 tokio 应用程序与特定的 cpu core 绑定该怎么解决呢?这次咱们来聊聊这个话题。

首先咱们先写一段简略的多任务程序。

use tokio::runtime;pub fn main() {    let rt = runtime::Builder::new_multi_thread()        .enable_all()        .build()        .unwrap();    rt.block_on(async {        for i in 0..8 {            println!("num {}", i);            tokio::spawn(async move {                loop {                    let mut sum: i32 = 0;                    for i in 0..100000000 {                        sum = sum.overflowing_add(i).0;                    }                    println!("sum {}", sum);                }            });        }    });}

程序非常简单,首先结构一个tokio runtime 环境,而后派生多个 tokio 并发,每个并发执行一个有限循环做overflowing\_add。overflowing\_add函数返回一个加法的元组以及一个示意是否会产生算术溢出的布尔值。如果会产生溢出,那么将返回包装好的值。而后取元祖的第一个元素打印。

这个程序运行在 Ubuntu 20 OS,4 core cpu。通过nmon的监控如下:

能够看到每个 core 都有负载。

要想把负载绑定在某一 core 上,须要应用core\_affinity\_rs。core\_affinity\_rs是一个用于治理CPU亲和力的Rust crate。目前反对Linux、Mac OSX和Windows。官网声称反对多平台,自己只做了linux 操作系统的测试。

咱们把代码批改一下:

use tokio::runtime;pub fn main() {    let core_ids = core_affinity::get_core_ids().unwrap();    println!("core num {}", core_ids.len());    let core_id = core_ids[1];    let rt = runtime::Builder::new_multi_thread()        .on_thread_start(move || {            core_affinity::set_for_current(core_id.clone());        })        .enable_all()        .build()        .unwrap();    rt.block_on(async {        for i in 0..8 {            println!("num {}", i);            tokio::spawn(async move {                 loop {                    let mut sum: i32 = 0;                    for i in 0..100000000 {                        sum = sum.overflowing_add(i).0;                    }                    println!("sum {}", sum);                           }            });        }    });}

在构建多线程runtime时,在on\_thread\_start 设置cpu亲和。能够看到负载被绑定到了指定的core上。

下面的代码只是把负载绑定到了一个core上,那么要绑定多个核怎么办呢?
咱们看看上面的代码

pub fn main() {    let core_ids = core_affinity::get_core_ids().unwrap();    println!("core num {}", core_ids.len());    let rt = runtime::Builder::new_multi_thread()        .enable_all()        .build()        .unwrap();    let mut idx = 2;    rt.block_on(async {        for i in 0..8 {            println!("num {}", i);            let core_id = core_ids[idx];            if idx.eq(&(core_ids.len() - 1)) {                idx = 2;            } else {                idx += 1;            }            tokio::spawn(async move {                let res = core_affinity::set_for_current(core_id);                println!("{}", res);                loop {                    let mut sum: i32 = 0;                    for i in 0..100000000 {                        sum = sum.overflowing_add(i).0;                    }                    println!("sum {}", sum);                    }            });        }    });}

代码须要把所有负载绑在 core3和core4上。原理是在派生工作中退出 core_affinity 设置.通过调整idx,将派生并发均匀绑定在指定的core上。代码运行的监控如下图。

本期对于cpu亲和的话题就聊到这儿,下期见

作者:京东科技 贾世闻

起源:京东云开发者社区