Rc: 一个数据多个所有者

RefCell: 修改数据

  1. //千万不要引用错包
  2. use std::cell::RefCell;
  3. use std::rc::Rc;
  4. fn main() {
  5. let s = Rc::new(RefCell::new("我很善变,还有多个主人".to_string()));
  6. let s1 = s.clone();
  7. let s2 = s.clone();
  8. s2.borrow_mut().push_str(",oh hh");
  9. println!("{:?}\n {:?} \n {:?} ", s, s1, s2);
  10. }
  11. //no method named `push_str` found for mutable reference `&mut Rc<RefCell<&str>>` in the current scope

性能损耗

性能其实非常高,大致相当于没有线程安全版本的 C++ std::shared_ptr 指针,事实上,C++ 这个指针的主要开销也在于原子性这个并发原语上,毕竟线程安全在哪个语言中开销都不小。

内存损耗

两者结合的数据结构与下面类似

  1. struct Warapper<T>{
  2. //Rc
  3. strong_count:usize,
  4. weak_count:usize,
  5. //RefCell
  6. borrow_count:isize,
  7. //包裹的数据
  8. item:T,
  9. }

也就是多了三个size

CPU损耗

  • 对Rc解引用是免费的(编译器) ,但是*带的间接取值并无免费
  • 克隆Rc需要当前的引用计数跟0和usize::max进行一次比较,然后加1
  • 释放drop Rc需要计数减一 然后跟0进行一次比较
  • 对Refcell进行不可变借用,需要将isze类型的借用计数加1,然后跟0比较
  • 对RefCell的不可变借用进行释放,需要将isize减1
  • 对RefCell的可变借用大致流程跟上面差不多 ,但是需要先跟0比较再减1
  • 对RefCell的可变借用进行释放,需要将size加1

总结Cpu消耗非常低,设置编译器还会对此进行进一步优化

CPU 缓存 Miss

唯一需要担心的可能就是这种组合数据结构对于 CPU 缓存是否亲和,这个我们无法证明,只能提出来存在这个可能性,最终的性能影响还需要在实际场景中进行测试。 总之,分析这两者组合的性能还挺复杂的,大概总结下:
  • 从表面来看,它们带来的内存和 CPU 损耗都不大
  • 但是由于 Rc 额外的引入了一次间接取值(*),在少数场景下可能会造成性能上的显著损失
  • CPU 缓存可能也不够亲和