我是否正确地假设“减慢”Rc
的唯一事情是它检查是否在对象掉落时解除分配?除此之外,取消引用Rc
的开销“多少”,即我应该关注它吗?
这两个功能几乎同样快吗?或者速度有明显差异吗?
fn test_with_box() {
let b = Box::new(1.0);
let x = b * 2;
}
fn test_with_rc() {
let rc = Rc::new(1.0);
let x = rc * 2;
}
由于test_with_rc()
中引用的对象始终只有一个引用,并且在该函数中的行为类似于Box
(当然,从外部查看,而不是在内部查看)。
我怀疑Rc
实际上比我想象的要快。
PS:在谈论“快速”时,我的意思是解除引用和分配/解除分配。
答案 0 :(得分:9)
Rc<T>
非常非常便宜。它并不像T
那么便宜(拳击值在微优化术语中比较昂贵),但效率几乎没有Box<T>
。
就像Box
一样,但是对于强弱引用计数还有一些单词,而且需要触及的唯一事情是创建Rc
(初始化值),克隆Rc
(递增引用计数)并删除Rc
(如果合适,递减引用计数并运行析构函数)。在不稳定的代码中,还会从Weak
降级/升级(同样琐碎的东西)。
解除引用是一种简单的内存操作,就像使用Box
一样。
答案 1 :(得分:8)
要回答您的问题,您可以转到test
包,其中包含基准测试解决方案(这需要每晚一次):
#![feature(test)]
extern crate test;
use std::rc::Rc;
fn test_with_box() {
let b = Box::new(1.0);
test::black_box(*b * 2.0);
}
fn test_with_rc() {
let rc = Rc::new(1.0);
test::black_box(*rc * 2.0);
}
#[bench]
fn bench_box(b: &mut test::Bencher) {
b.iter(test_with_box)
}
#[bench]
fn bench_rc(b: &mut test::Bencher) {
b.iter(test_with_rc)
}
现在正在编译&amp;运行这个:
$ rustc --test -O rc.rs && ./rc --bench
running 2 tests
test bench_box ... bench: 22 ns/iter (+/- 0)
test bench_rc ... bench: 22 ns/iter (+/- 0)
test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured
发生什么事了? RC显然是完全编译出来的。应该是这样,因为我们没有克隆它。因此,将相应的fn
更改为:
fn test_with_rc() {
let rc = Rc::new(1.0);
test::black_box(*rc.clone() * 2.0);
}
我们得到以下信息:
running 2 tests
test bench_box ... bench: 23 ns/iter (+/- 1)
test bench_rc ... bench: 25 ns/iter (+/- 1)
test result: ok. 0 passed; 0 failed; 0 ignored; 2 measured
因此,足以说明,在考虑RC引起的开销之前,您可能需要担心其他事情。