【问题标题】:Why do my Futures not max out the CPU?为什么我的 Futures 没有最大化 CPU?
【发布时间】:2018-03-14 08:52:00
【问题描述】:

我正在创建数百个请求来下载同一个文件(这是一个玩具示例)。当我使用 Go 运行等效逻辑时,我得到 200% 的 CPU 使用率,并在约 5 秒内返回(w/800 个请求)。在只有 100 个请求的 Rust 中,它需要将近 5 秒并产生 16 个操作系统线程,CPU 利用率为 37%。

为什么会有这样的差异?

据我了解,如果我有一个 CpuPoolN 个内核管理 Futures,这在功能上就是 Go 运行时/goroutine 组合正在做的事情,只是通过光纤而不是期货.

从性能数据来看,尽管ThreadPoolExecutor,我似乎只使用了 1 个核心。

extern crate curl;
extern crate fibers;
extern crate futures;
extern crate futures_cpupool;

use std::io::{Write, BufWriter};
use curl::easy::Easy;
use futures::future::*;
use std::fs::File;
use futures_cpupool::CpuPool;


fn make_file(x: i32, data: &mut Vec<u8>) {
    let f = File::create(format!("./data/{}.txt", x)).expect("Unable to open file");
    let mut writer = BufWriter::new(&f);
    writer.write_all(data.as_mut_slice()).unwrap();
}

fn collect_request(x: i32, url: &str) -> Result<i32, ()> {
    let mut data = Vec::new();
    let mut easy = Easy::new();
    easy.url(url).unwrap();
    {
        let mut transfer = easy.transfer();
        transfer
            .write_function(|d| {
                data.extend_from_slice(d);
                Ok(d.len())
            })
            .unwrap();
        transfer.perform().unwrap();

    }
    make_file(x, &mut data);
    Ok(x)
}

fn main() {
    let url = "https://en.wikipedia.org/wiki/Immanuel_Kant";
    let pool = CpuPool::new(16);
    let output_futures: Vec<_> = (0..100)
        .into_iter()
        .map(|ind| {
            pool.spawn_fn(move || {
                let output = collect_request(ind, url);
                output
            })
        })
        .collect();

    // println!("{:?}", output_futures.Item());
    for i in output_futures {
        i.wait().unwrap();
    }
}

My equivalent Go code

【问题讨论】:

  • 您是否使用 --release 标志编译和运行程序?
  • @Shepmaster 是,优化级别为 3

标签: multithreading rust multiprocessing threadpool fibers


【解决方案1】:

据我了解,如果我有一个 CpuPoolN 个内核管理 Futures,这在功能上就是 Go 运行时/goroutine 组合正在做的事情,只是通过光纤而不是期货.

这是不正确的。 documentation for CpuPool states,重点是我的:

用于运行 CPU 密集型工作的线程池。

下载文件不是 CPU 密集型的,它是 IO 密集型的。您所做的只是启动许多线程,然后告诉每个线程在等待 IO 完成时阻塞。

改为使用tokio-curl,它将curl 库适应Future 抽象。然后,您可以完全删除线程池。这应该会大大提高您的吞吐量。

【讨论】:

  • 谢谢!大部分开销不是在等待网络,所以如果我为每个请求生成了一个未来,它不应该 not 阻塞直到响应可用并且我可以写入文件? tokio 通常是否适合其他 io 绑定任务,例如写入许多文件等?
  • @Maximus12793 大部分开销不是等待网络 — 是的,这就是为什么我说“下载文件是 [...] IO-bound” . 它不应该阻塞 - 是的,除非您执行了阻塞调用 (.perform())。 tokio 通常非常适合其他 io 绑定 — 是的,我很确定这就是它被称为 Tokio 的原因,而 AFAIK 是 Tokio 存在的全部原因。 喜欢写很多文件——很遗憾,有basically no good async disk IO
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-04
  • 1970-01-01
  • 2022-01-12
  • 2021-07-09
  • 2013-08-29
  • 1970-01-01
相关资源
最近更新 更多