Should I worry about the "Rc" overhead?

Can we assume that the only thing that “slows down” Rc is that it checks whether the object should be freed when it is deleted? Also, how much is the overhead of dereferencing a Rc , that is, should I be concerned about this?
Are these two functions almost equally fast? Or is there a noticeable difference in speed?

 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; } 

Since the reference object in test_with_rc() always has only one link and behaves like a Box in this function (apparently from the outside, and not from the inside, of course).

I suspect that Rc is actually faster than I think.

PS: When it comes to “fast”, I mean both dereferencing and allocation / release.

+7
rust
source share
2 answers

Rc<T> very, very cheap. Its not as cheap as T quite a lot (boxing values ​​are relatively expensive in terms of micro-optimization), but it is less efficient than Box<T> .

It is just like Box , but with extra word pairs for strong and weak reference counting and the only things to be touched, create Rc (initializes the values), clone Rc (increase the value of refcount) and discard Rc (decrease the conversion factor and start the destructor if necessary). In the unstable code, theres also downgrades / upgrades from Weak (similarly trivial stuff).

Highlighting names is a simple memory operation, just like with Box .

+7
source share

To answer your question, you can refer to the test box, which contains a solution for benchmarking (this requires nightly):

 #![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) } 

Now compile and run this:

 $ 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 

What happened? Obviously, the RC was completely composed. As it should be, because we did not clone . Therefore, changing the corresponding fn to:

 fn test_with_rc() { let rc = Rc::new(1.0); test::black_box(*rc.clone() * 2.0); } 

We get the following:

 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 

So, suffice it to say that you will probably have to worry about other things before looking at the RC-induced overhead.

+7
source share

All Articles