#智能指针 #指针 #缓存

pierce

避免嵌套智能指针中的双重间接引用

3 个版本 (破坏性)

0.3.0 2021 年 8 月 2 日
0.2.0 2021 年 8 月 2 日
0.1.0 2021 年 8 月 2 日

#127缓存

MIT 许可证

28KB
177 代码行

刺穿

crates.io crates.io

避免嵌套智能指针中的双重间接引用。

Pierce 结构允许您缓存双重嵌套智能指针的解引用结果。

快速示例

use std::sync::Arc;
use pierce::Pierce;
let vec: Vec<i32> = vec![1, 2, 3];
let arc_vec = Arc::new(vec);
let pierce = Pierce::new(arc_vec);

// Here, the execution jumps directly to the slice to call `.get(...)`.
// Without Pierce it would have to jump to the Vec first,
// than from the Vec to the slice.
pierce.get(0).unwrap();

嵌套智能指针

智能指针可以嵌套以组合其功能。例如,使用 Arc<Vec<i32>>,一个 i32 的切片由外部的 Vec 管理,该 Vec 又被 Arc 包装。

然而,嵌套会带来 双重间接引用 的代价:当我们想要访问底层数据时,我们必须首先跟随外部指针到内部指针所在的位置,然后跟随内部指针到底层数据所在的位置。两次 deref。两次跳跃。

use std::sync::Arc;
let vec: Vec<i32> = vec![1, 2, 3];
let arc_vec = Arc::new(vec);

// Here, the `Arc<Vec<i32>>` is first dereferenced to the `Vec<i32>`,
// then the Vec is dereferenced to the underlying i32 slice,
// on which `.get(...)` is called.
arc_vec.get(0).unwrap();

刺穿

此 crate 提供的 Pierce 结构可以减少嵌套智能指针的性能成本,通过 缓存解引用结果。我们最初对嵌套智能指针进行双重解引用,存储内部指针指向的地址。然后我们可以通过仅跳转到存储的地址来访问底层数据。一次跳跃。

以下是可能看起来是这样的图示。

             ┌───────────────────────────┬───────────────────────────────┬──────────────────────────────────────────┐
             │ Stack                     │ Heap                          │ Heap                                     │
┌────────────┼───────────────────────────┼───────────────────────────────┼──────────────────────────────────────────┤
│ T          │                           │                               │                                          │
│            │  ┌──────────────────┐     │     ┌───────────────────┐     │    ┌──────────────────────────────────┐  │
│            │  │Outer Pointer     │     │     │Inner Pointer      │     │    │Target                            │  │
│            │  │                  │     │     │                   │     │    │                                  │  │
│            │  │        T ────────────────────────► T::Target ─────────────────► <T::Target as Deref>::Target   │  │
│            │  │                  │     │     │                   │     │    │                                  │  │
│            │  └──────────────────┘     │     └───────────────────┘     │    └──────────────────────────────────┘  │
│            │                           │                               │                                          │
├────────────┼───────────────────────────┼───────────────────────────────┼──────────────────────────────────────────┤
│ Pierce<T>  │                           │                               │                                          │
│            │  ┌──────────────────┐     │     ┌───────────────────┐     │    ┌──────────────────────────────────┐  │
│            │  │Outer Pointer     │     │     │Inner Pointer      │     │    │Target                            │  │
│            │  │                  │     │     │                   │     │    │                                  │  │
│            │  │        T ────────────────────────► T::Target ─────────────────► <T::Target as Deref>::Target   │  │
│            │  │                  │     │     │                   │     │    │                ▲                 │  │
│            │  ├──────────────────┤     │     └───────────────────┘     │    └────────────────│─────────────────┘  │
│            │  │Cache             │     │                               │                     │                    │
│            │  │                  │     │                               │                     │                    │
│            │  │       ptr ───────────────────────────────────────────────────────────────────┘                    │
│            │  │                  │     │                               │                                          │
│            │  └──────────────────┘     │                               │                                          │
│            │                           │                               │                                          │
└────────────┴───────────────────────────┴───────────────────────────────┴──────────────────────────────────────────┘

用法

Pierce<T> 可以通过 Pierce::new(...) 创建。 T 应该是一个双重指针(例如 Arc<Vec<_>>Box<Box<_>>)。

derefPierce<T> 进行解引用返回 &T::Target<Deref>::Target,即 T 的解引用目标的解引用目标(由 Pierce 包裹的外部指针),即内部指针的解引用目标。

您还可以使用 borrow_inner 获取 T(外部指针)的借用。

有关详细信息,请参阅 Pierce 的文档。

更深的嵌套

Pierce 将两个跳跃减少到一个。如果您有更深的嵌套,可以多次包裹它。

use pierce::Pierce;
let triply_nested: Box<Box<Box<i32>>> = Box::new(Box::new(Box::new(42)));
assert_eq!(***triply_nested, 42); // <- Three jumps!
let pierce_twice = Pierce::new(Pierce::new(triply_nested));
assert_eq!(*pierce_twice, 42); // <- Just one jump!

基准测试

这些基准测试可能根本不能代表您的用例,因为

  • 它们被设计得让 Pierce 看起来很棒。
  • 编译器优化难以控制。
  • CPU 缓存和预测难以控制。(我敢打赌,您的 CPU 上的数字会有很大不同。)
  • 无数其他原因,说明您不应该信任合成基准测试。

在实际应用中进行自己的基准测试.

话虽如此,以下是我的结果

基准测试 1:从具有模拟内存碎片的 Box<Vec 中读取项目。

基准测试 2:从 SlowBox<Vec 中读取项目。SlowBox 故意减慢 deref 调用。

基准测试 3:读取几个 Box<Box

Pierce<T> 版本与 T 版本的耗时比较。

运行 基准测试 1 基准测试 2 基准测试 3
1 -40.23% -99.69% -5.68%
2 -40.59% -99.69% -5.16%
3 -40.70% -99.68% +2.69%
4 -39.85% -99.68% -5.35%
5 -38.90% -99.71% -5.02%
6 -39.12% -99.69% -5.53%
7 -40.51% -99.69% -6.09%
8 -26.99% -99.71% -6.43%

请在此处查看基准测试的代码 here.

局限性

仅支持不可变类型

Pierce 仅与不可变数据一起使用。不支持可变性,因为我相当确信这几乎是不可能的。 (如果您有想法,请分享。)

需要 StableDeref

Pierce 包装的指针必须是 StableDeref。如果您的指针类型满足所需条件,您可以在它上实现 unsafe impl StableDeref for T。该特性在 pierce::StableDeref 中重新导出。

绝大多数指针是 StableDeref 类型,包括 BoxVecStringRcArc

依赖项

~12KB