17个版本 (破坏性)

0.14.0 2022年9月13日
0.12.0 2022年9月2日
0.6.0 2022年7月31日

#337 in 内存管理

每月下载量43

MIT/Apache

98KB
2.5K SLoC

描述

Onsen提供热池对象。在大多数情况下,从这样的池进行分配比标准分配器更快,并且提供了更好的局部性。对于小型到中型对象,性能提升约为20%或更好。对于大型对象,由于缓存效果趋于平衡,收益会减小。这些改进涵盖了由于局部性操作对象,而不仅仅是加快分配速度。

详情

Onsen池以指数级增长的大小分配块。分配从这些块中提供服务。释放的条目被保留在双链循环空闲列表中。这个空闲列表被弱有序地保持,并且入口点始终指向上次操作发生的地方,以保持缓存热。

Box, Rc 和 Sc

Onsen附带自己的BoxRc/Weak实现,它们以安全的方式包装底层的RcPool。还提供了一种不支持的弱引用的Sc引用计数框,它为小对象提供了优势,因为弱引用会增加一些重量。

对于这些中的每一个,还有一个使用静态全局池的变体。

插槽

从池分配返回Slot处理程序。这些是轻量级的内存地址抽象,它们不保留与它们分配的池的关系。这种设计背后的原理是使它们在使用NaN标记的VM中可用。

插槽策略

插槽由类型状态策略保护,这可以在编译时防止一些错误使用。

插槽和安全

因此,需要小心处理插槽,并需要执行某些合同。该库提供了一些帮助以确保正确性。有些事情无法断言,并由不安全函数保护。高级API(如上述的BoxRcSc)可以轻松以安全的方式执行这些操作。

  1. 插槽必须返回给原始池。
  2. 插槽的寿命不能超过它们分配的池。
    • 当池在仍有活动分配的情况下被丢弃时,在调试模式下将引发恐慌。
    • 当一个包含活动分配的池在发布模式下被丢弃时,它会泄漏其内存。这是不幸的,但确保了程序的安全。
    • 存在一个pool.leak()函数,在丢弃池的同时泄漏其内存块。当不再尝试从该池中释放内存时,可以使用此功能。
    • 这同样适用于u64 NaN标签。
  3. 槽位必须只释放一次。
    • 这始终是断言的。但断言可能在槽位再次分配时失败。
    • 槽位不是'拷贝'的,因此不能安全地两次释放槽位。但是,引用计数实现和NaN标记功能使用显式的'copy()'函数,可以复制'u64'并尝试多次释放。这些函数因为这一点是'不安全的'。
  4. 从槽位获得的引用不能比释放Slot的时间长。
    • 这是使Slot释放函数不安全的主要原因。池无法知道引用是否仍在使用。应该提供或使用安全的抽象来强制执行这一点。

功能

Onsen提供了单线程的Pool、单线程引用计数的RcPool和多线程的TPool。其他功能受功能标志的控制。

  • parking_lotTPool中使用parking_lot(而不是std::sync::Mutex)。当已经使用parking_lot时,这很有意义。在Onsen中,这没有带来显著的性能优势。
  • stpool 提供了使用ThreadCell的单线程池STPool,这比互斥锁保护的池要快得多。这些池可以在线程之间合作地移动,具有获取/释放语义。
  • tboxTBoxTRcTSc添加了API,这些API使用每个类型的全局池。优点是框不需要存储其池的引用,这节省了一点内存,并提高了小对象的局部性。
  • st_tbox 为tbox API使用STPool,这使stpooltbox成为可能。

st_tbox 是默认的。这启用了最完整的API,具有最佳性能。

性能特征

  • Onsen池针对缓存局部性进行了优化,这在某种程度上也针对单线程使用进行了优化。最好为每个线程创建一个类型的池。

  • TPool添加了互斥锁以用于多线程情况,但它的性能比单线程池低得多,但在许多情况下仍比std分配器好。尽管如此,仍然可以从局部性中受益。

  • STPool是单线程的,但可以在线程之间合作地传递,其性能与其他单线程池相当。这在使用TBoxTRcTSc时尤为重要。

基准测试

Onsen使用criterion进行基准测试,由于Onsen是为单线程应用程序制作的,因此最好在单个CPU核心上锁定并锁定到低于最大频率的某个频率,以提供更一致的结果。在高优先级下,这样就不会太多地受到其他程序的影响。在Linux上,您可以这样做:

sudo renice -15 $$
sudo cpupower -c 1 frequenc-set -f 2.8GHz
taskset 2 cargo bench

将生成target/criterion/report/index.html

依赖项

~0–5MB