• 文盘 Rust -- tokio 绑定 cpu 实践


    tokio 是 rust 生态中流行的异步运行时框架。在实际生产中我们如果希望 tokio 应用程序与特定的 cpu core 绑定该怎么处理呢?这次我们来聊聊这个话题。

    首先我们先写一段简单的多任务程序。

    1. use tokio::runtime;
    2. pub fn main() {
    3. let rt = runtime::Builder::new_multi_thread()
    4. .enable_all()
    5. .build()
    6. .unwrap();
    7. rt.block_on(async {
    8. for i in 0..8 {
    9. println!("num {}", i);
    10. tokio::spawn(async move {
    11. loop {
    12. let mut sum: i32 = 0;
    13. for i in 0..100000000 {
    14. sum = sum.overflowing_add(i).0;
    15. }
    16. println!("sum {}", sum);
    17. }
    18. });
    19. }
    20. });
    21. }

    程序非常简单,首先构造一个 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 操作系统的测试。

    我们把代码修改一下:

    1. use tokio::runtime;
    2. pub fn main() {
    3. let core_ids = core_affinity::get_core_ids().unwrap();
    4. println!("core num {}", core_ids.len());
    5. let core_id = core_ids[1];
    6. let rt = runtime::Builder::new_multi_thread()
    7. .on_thread_start(move || {
    8. core_affinity::set_for_current(core_id.clone());
    9. })
    10. .enable_all()
    11. .build()
    12. .unwrap();
    13. rt.block_on(async {
    14. for i in 0..8 {
    15. println!("num {}", i);
    16. tokio::spawn(async move {
    17. loop {
    18. let mut sum: i32 = 0;
    19. for i in 0..100000000 {
    20. sum = sum.overflowing_add(i).0;
    21. }
    22. println!("sum {}", sum);
    23. }
    24. });
    25. }
    26. });
    27. }

    在构建多线程 runtime 时,在 on_thread_start 设置 cpu 亲和。可以看到负载被绑定到了指定的 core 上。

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

    1. pub fn main() {
    2. let core_ids = core_affinity::get_core_ids().unwrap();
    3. println!("core num {}", core_ids.len());
    4. let rt = runtime::Builder::new_multi_thread()
    5. .enable_all()
    6. .build()
    7. .unwrap();
    8. let mut idx = 2;
    9. rt.block_on(async {
    10. for i in 0..8 {
    11. println!("num {}", i);
    12. let core_id = core_ids[idx];
    13. if idx.eq(&(core_ids.len() - 1)) {
    14. idx = 2;
    15. } else {
    16. idx += 1;
    17. }
    18. tokio::spawn(async move {
    19. let res = core_affinity::set_for_current(core_id);
    20. println!("{}", res);
    21. loop {
    22. let mut sum: i32 = 0;
    23. for i in 0..100000000 {
    24. sum = sum.overflowing_add(i).0;
    25. }
    26. println!("sum {}", sum);
    27. }
    28. });
    29. }
    30. });
    31. }

    代码需要把所有负载绑在 core3 和 core4 上。原理是在派生任务中加入 core_affinity 设置。通过调整 idx,将派生并发平均绑定在指定的 core 上。代码运行的监控如下图。

  • 相关阅读:
    北斗——1、北斗时空智能主机
    化交易之One Piece篇 - onepiece_rsh - reset trading_time.json
    Java基础-----String类
    OpenVINO--初步学习笔记
    车规级电感厂家揭秘共模电感烧了的可能原因
    Windows 安装 Docker Desktop 到其他盘、迁移虚拟硬盘映像文件、压缩虚拟硬盘映像占用空间
    GAN生成哆啦A梦,亲测疯狂训练50000epoch的效果,俺的菜菜电脑吃不消
    智能化“竞赛下半场”,什么才是可量产的最佳实践?
    NoSQL之 Redis配置与优化
    中国市场超阿迪耐克 安踏领衔打响国货反击战
  • 原文地址:https://blog.csdn.net/2301_78385600/article/details/133339490