问题在于
let mut buf = vec![0; 1024];
vs
let mut buf = [0;1024];
的区别, 我理解是[u8;1024]即非vec!,他们有性能差别吗? tokio的task管理里边,当task挂起让出线程权,或者恢复task重新运行。 vec![0;1024] 与 [0;1024]是否有差别? 是否使用[0;1024],会使task任务的体积更加大?影响task 在线程间的转移,或者任务挂起,或者任务恢复?
use tokio::io::{self, AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpListener;
#[tokio::main]
async fn main() -> io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:6142").await?;
loop {
let (mut socket, _) = listener.accept().await?;
tokio::spawn(async move {
let mut buf = vec![0; 1024]; //===疑问行=== 与[0; 1024]差别是否有性能差别?
loop {
match socket.read(&mut buf).await {
Ok(0) => return,
Ok(n) => {
if socket.write_all(&buf[..n]).await.is_err() {return;}
}
Err(_) => {return;}
}
}
});
}
}
突然觉得自己这个问题有点傻,按照rust的设计是要高性能,task在线程间移动应该是以引用的形式移动的, 因此我觉得vec![0;1024] 与 [0;1024]性能上是一样的
1
共 5 条评论, 1 页
评论区
写评论这中间的细节我不是特别清楚,所以也不要以我的说法为准。
准确说应该是“指针”,因为“引用”在 Rust 中特指
&
和&mut
。当然,引用属于“指针类型”。通常把 Futures 放置在堆上,所以这里的
[u8; 1024]
或许不在栈上,而是在堆上内联,类似于Pin<Box<Task1>>
而
Box<[u8; 1024]>
相比于Box<Vec<u8>>
带来的少许优势,比如更少的边界检查(因为长度不可变)、直接性(因为前者的指针直接指向数据,而后者还要经过 Vec 背后的指针)。对原始回答纠正一下: 第一条是“移动”
[u8; 1024]
的成本,而不是 “复制”;Vec 的大小始终为 24 bytes,而不是 bits。。。只针对这个问题:
一个task开始运行以后只能以(Pin<SomePtr>)的形式存在的,所以是的在不同线程间调度是以引用的形式移动的;
谢谢大哥慷慨回复
👇
苦瓜小仔: 异步编程很少把这么大的数组当作缓冲 —— 不是因为数组性能不好,而是因为它没法满足需求。
此外,性能考量来自于你对你的代码的 benchmark。
当然栈上的数据会比堆在缓存上更优,读取和修改更快,但你还有更重要的一些问题没有考虑:
[u8; 1024]
的成本:Rust 的移动语义实质上是位拷贝(当然这可以被优化掉),但无论怎样,携带这么大的数据的任务的体积会更加大(当然有可能任务不被移动),而此时Vec
的大小始终为 24 bits,移动的成本相比而言很便宜: 'static
bound 的情况下,不得不考虑支持多所有权的数据结构(不通过引用来访问同一块数据),比如Arc<[T]>
或者Arc<Mutex<Vec>>
,因为复制它,仅仅增加计数,复制和销毁的成本相比而言很便宜(但修改数据的情况下,锁会增加性能阻碍)bytes
异步编程很少把这么大的数组当作缓冲 —— 不是因为数组性能不好,而是因为它没法满足需求。
此外,性能考量来自于你对你的代码的 benchmark。
当然栈上的数据会比堆在缓存上更优,读取和修改更快,但你还有更重要的一些问题没有考虑:
[u8; 1024]
的成本:Rust 的移动语义实质上是位拷贝(当然这可以被优化掉),但无论怎样,携带这么大的数据的任务的体积会更加大(当然有可能任务不被移动),而此时Vec
的大小始终为 24 bits,移动的成本相比而言很便宜: 'static
bound 的情况下,不得不考虑支持多所有权的数据结构(不通过引用来访问同一块数据),比如Arc<[T]>
或者Arc<Mutex<Vec>>
,因为复制它,仅仅增加计数,复制和销毁的成本相比而言很便宜(但修改数据的情况下,锁会增加性能阻碍)bytes