我正在Rust中编写一个解释器,用于特定于域的语言,该语言应该允许高性能的实现 . 堆的相关属性是:
-
程序很短(但很多都是执行的)
-
我知道,在执行开始之前,需要的最大内存量
-
所需的内存很小
-
在程序终止之前不需要取消分配
-
创建后,所有数据都是不可变的
-
语言是静态类型检查的,因此没有访问未初始化内存的风险
-
语言是单线程的,因此没有共享内存
我在堆中的元素有一个固定的结构:
pub enum Data {
Adt(u16, Vec<u64>),
Prim(Vec<u64>),
}
Vec<u64>
包含指向其他元素或原始数据的指针 . 我首先考虑用枚举直接表示它 .
这需要在运行时进行大量分配,并且需要 Vec
的不必要的间接 . 它还浪费了容量和大小字段的空间(因为数据是不可变的,并且大小是静态的) .
我对Rust来说相对较新,并且让自己远离不安全的代码,但我认为它可以帮到这里 . 我首先尝试表达我想要的功能:
#[derive(Copy, Clone)]
struct Data(u64);
struct DataStore(Vec<u64>);
impl DataStore {
fn alloc(max_size: u64) -> Self {
DataStore(Vec::with_capacity(max_size as usize))
}
fn new_adt(&mut self, tag: u16) -> Data {
let start = self.0.len() as u64;
let header = tag as u64;
self.0.push(header);
Data(start)
}
fn new_primitive(&mut self, data: &[u64]) -> Data {
let start = self.0.len() as u64;
for e in data {
self.0.push(*e)
}
Data(start)
}
fn init_adt_field(&mut self, Data(ptr): Data) {
self.0.push(ptr);
}
fn get_field(&self, Data(start): Data, index: u32) -> Data {
Data(self.0[(start + 1 + (index as u64)) as usize])
}
fn get_tag(&self, Data(start): Data) -> u16 {
self.0[start as usize] as u16
}
}
这可以以某种方式比 Vec
更有效地实施吗?我看了 TypedArena
,但不知道在这种情况下是否以及如何使用它 . 它会比 Vec
更快吗? Vec
将永远不会增长,因为它被初始化为静态证明足够的容量 .
是否可以只分配一个内存blob,然后使用不安全的指针?我有什么选择?