为什么我的期货没有最大化 CPU?
Why do my Futures not max out the CPU?
我正在创建数百个下载同一个文件的请求(这是一个玩具示例)。当我 运行 与 Go 的等效逻辑时,我得到 200% CPU 使用率和 return 在 ~5 秒内 w/800 请求。在只有 100 个请求的 Rust 中,它花费了近 5 秒并产生了 16 个 OS 线程,利用率为 37% CPU。
为什么会有这样的差异?
据我了解,如果我有一个 CpuPool
跨 N 个核心管理 Future
,这在功能上就是 Go runtime/goroutine 组合正在做,只是通过光纤而不是期货。
从 perf 数据来看,尽管 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
From what I understand, if I have a CpuPool
managing Future
s across N cores, this is functionally what the Go runtime/goroutine combo is doing, just via fibers instead of futures.
这是不正确的。 documentation for CpuPool
states,强调我的:
A thread pool intended to run CPU intensive work.
下载文件 不是 CPU 绑定的,它是 IO 绑定的。您所做的就是启动许多线程,然后告诉每个线程在等待 IO 完成时阻塞。
相反,使用 tokio-curl,它使 curl 库适应 Future
抽象。然后您可以完全删除线程池。这应该会大大提高您的吞吐量。
我正在创建数百个下载同一个文件的请求(这是一个玩具示例)。当我 运行 与 Go 的等效逻辑时,我得到 200% CPU 使用率和 return 在 ~5 秒内 w/800 请求。在只有 100 个请求的 Rust 中,它花费了近 5 秒并产生了 16 个 OS 线程,利用率为 37% CPU。
为什么会有这样的差异?
据我了解,如果我有一个 CpuPool
跨 N 个核心管理 Future
,这在功能上就是 Go runtime/goroutine 组合正在做,只是通过光纤而不是期货。
从 perf 数据来看,尽管 ThreadPoolExecutor
.
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
From what I understand, if I have a
CpuPool
managingFuture
s across N cores, this is functionally what the Go runtime/goroutine combo is doing, just via fibers instead of futures.
这是不正确的。 documentation for CpuPool
states,强调我的:
A thread pool intended to run CPU intensive work.
下载文件 不是 CPU 绑定的,它是 IO 绑定的。您所做的就是启动许多线程,然后告诉每个线程在等待 IO 完成时阻塞。
相反,使用 tokio-curl,它使 curl 库适应 Future
抽象。然后您可以完全删除线程池。这应该会大大提高您的吞吐量。