#数组 # #数组-vec # #堆分配 #元素 #重排

无std combo_vec

使用栈(以及可选的堆以处理溢出)实现的一个极快的无std向量-like ADT

13个不稳定版本 (6个重大更改)

0.7.2 2024年3月24日
0.7.1 2023年11月29日
0.6.0 2023年11月22日
0.5.1 2023年1月10日
0.5.0 2022年11月18日

#712 in 数据结构


用于 rl_ball_sym

MIT 许可证

63KB
657

combo_vec

unsafe forbidden

ComboVec 用于创建一个 "组合栈数组-堆向量",或者简单地说是一个可调整大小的数组,其中包含一个用于额外分配的向量。

不仅如此,如果只想使用可调整大小的数组部分,这个库还提供了 ReArr

使用 combo_vec! 宏创建一个新的 ComboVec,并使用 re_arr! 宏创建一个新的 ReArr

这是通过在栈上分配一个 T 数组来实现的,然后使用堆上的 Vec 处理溢出。

即使数组被调整大小,栈分配的数组也始终用于存储前 N 个元素。

不需要T 实现 DefaultCopyClone 特性;但如果 T 实现了其中任何一个,则 ComboVecReArr 也会实现它们。这也适用于 PartialEqPartialOrdEqOrdHashDebugDisplay

为什么使用ComboVec

这主要用于当您知道99%的情况下将要存储的元素的最大数量,但又不想在最后1%时引起错误,同时也不会放弃使用栈而不是堆的大部分性能。

我在 ComboVec 上获得了性能提升,与类似的类型 SmallVec(带有和没有它的 union 功能)相比。

在测试推送2048(预分配)个元素时,性能提高了近54%

  • ComboVec:4.54 µs
  • SmallVec:9.33 µs

combo_vec! 宏非常方便使用,甚至在 const 上下文中也很方便。

use combo_vec::{combo_vec, ComboVec};

const SOME_ITEMS: ComboVec<i8, 3> = combo_vec![1, 2, 3];
const MANY_ITEMS: ComboVec<u16, 90> = combo_vec![5; 90];
const EXTRA_ITEMS: ComboVec<&str, 5> = combo_vec!["Hello", "world", "!"; None, None];

// Infer the type and size of the ComboVec
const NO_STACK_F32: ComboVec<f32, 0> = combo_vec![];

// No const-initialization is needed to create a ComboVec with allocated elements on the stack
use std::collections::HashMap;
const EMPTY_HASHMAP_ALLOC: ComboVec<HashMap<&str, i32>, 3> = combo_vec![];

// Creating a new ComboVec at compile time and doing this does have performance benefits
let my_combo_vec = EMPTY_HASHMAP_ALLOC;

ComboVec 还实现了许多仅限于 Vec 的方法,例如 extendtruncatepushjoin 等。

为什么使用 ReArr

在测试推送2048(预分配)个元素时,其性能与 ArrayVec 相当

  • ReArr:4.07 µs
  • ArrayVec:4.00 µs

re_arr! 宏非常方便使用,即使在 const 上下文中也很方便。

use combo_vec::{re_arr, ReArr};

const SOME_ITEMS: ReArr<i8, 3> = re_arr![1, 2, 3];
const MANY_ITEMS: ReArr<u16, 90> = re_arr![5; 90];
const EXTRA_ITEMS: ReArr<&str, 5> = re_arr!["Hello", "world", "!"; None, None];

// Infer the type and size of the ReArr
const NO_STACK_F32: ReArr<f32, 0> = re_arr![];

// No const-initialization is needed to create a ComboVec with allocated elements on the stack
use std::collections::HashMap;
const EMPTY_HASHMAP_ALLOC: ReArr<HashMap<&str, i32>, 3> = re_arr![];

// Creating a new ReArr at compile time and doing this does have performance benefits
let my_re_arr = EMPTY_HASHMAP_ALLOC;

ReArr 还实现了许多仅限于 Vec 的方法,例如 extendtruncatepushjoin 等。

示例

快速查看一个基本示例和一些可用方法

use combo_vec::combo_vec;

let mut combo_vec = combo_vec![1, 2, 3];
// Allocate an extra element on the heap
combo_vec.push(4);
// Truncate to a length of 2
combo_vec.truncate(2);
// Fill the last element on the stack, then allocate the next items on the heap
combo_vec.extend([3, 4, 5]);

在栈上分配空内存

您可以在栈上分配内存供以后使用,而无需设置其值!

不需要 Copy 或 Default 特性。

use combo_vec::{combo_vec, re_arr};

// Allocate a new space to store 17 elements on the stack.
let empty_combo_vec = ComboVec::<f32, 17>::new();
let empty_re_arr = ReArr::<f32, 17>::new();

在 const 上下文中在栈上分配内存

使用 combo_vec!/re_arr! 宏的主要优点是,它可以用于 const 上下文中。

这允许您在程序开始时在 Mutex 或 RwLock 中分配一个 ComboVec,从而具有最小的运行时开销。

use combo_vec::{combo_vec, ComboVec, re_arr, ReArr};

// Create a global variable for the various program states for a semi-unspecified length
use std::{collections::HashMap, sync::RwLock};
static PROGRAM_STATES: RwLock<ComboVec<HashMap<String, i32>, 20>> = RwLock::new(combo_vec![]);

// If we know the stack will never be larger than 20 elements,
// we can get a performance boost by using ReArr instead of ComboVec
let mut runtime_stack = ReArr::<i32, 20>::new();

使用 const & copy 快速运行

我们可以通过创建一个 const 上下文然后将其复制到我们的运行时变量中,来利用 ComboVecReArr。这比在运行时创建一个新的 ComboVec 快得多,而且 T 不需要是 Copy

以下是这个示例的基本看法

use combo_vec::{combo_vec, ComboVec};

const SOME_ITEMS: ComboVec<String, 2> = combo_vec![];

for _ in 0..50 {
    let mut empty_combo_vec = SOME_ITEMS;
    empty_combo_vec.push("Hello".to_string());
    empty_combo_vec.push("world".to_string());
    println!("{}!", empty_combo_vec.join(" "));
}

无运行时依赖