Data structures 字符串的循环缓冲区
我正在缓冲进程的stdout、stderr和stdin的最后X行。 我想保留最后的X行,并能够通过其id(行号)访问一行。 因此,如果我们存储100行并插入其中200行,您可以访问100-200行。 (实际上,我们希望存储约2000行。) 性能案例是插入。所以插入本身应该很快。检索偶尔会发生,但可能占用例的10%。 (我们大部分时间不会查看输出。) 旧方法,碎片化Data structures 字符串的循环缓冲区,data-structures,rust,circular-buffer,Data Structures,Rust,Circular Buffer,我正在缓冲进程的stdout、stderr和stdin的最后X行。 我想保留最后的X行,并能够通过其id(行号)访问一行。 因此,如果我们存储100行并插入其中200行,您可以访问100-200行。 (实际上,我们希望存储约2000行。) 性能案例是插入。所以插入本身应该很快。检索偶尔会发生,但可能占用例的10%。 (我们大部分时间不会查看输出。) 旧方法,碎片化 我使用了一个包装ArrayDeque,然后在行计数上保留了一本书,但这意味着在上面的示例中我们使用的是[Vec;100]。字符串数组
我使用了一个包装
ArrayDeque
,然后在行计数上保留了一本书,但这意味着在上面的示例中我们使用的是[Vec;100]
。字符串数组,因此是Vec
数组
新方法,带开放性问题我的*新想法是将数据存储在一个u8数组中,然后将book保留在数组中每个条目的起始位置和长度上。这里的问题是,我们需要簿记也成为某种环形缓冲区,并在数据数组必须包装时擦除旧条目。也许还有更好的方法来实现这一点?至少这充分利用了ringbuffer并防止了内存碎片 *还要感谢rust社区的sebk 当前简易方法
const MAX:usize=5;
pub结构LineRingBuffer{
柜台:期权,
资料来源:ArrayDeque,
min_line:使用,
}
impl LineRingBuffer{
pub fn new()->Self{
自我{
柜台:没有,
数据:ArrayDeque::new(),
最小线:0,
}
}
pub-fn-get选项{
如果让一些(最大)=自我计数器{
如果pos>=self.min\u行和pos=MAX{
self.min_line+=1;
}
}否则{
self.counter=Some(0);
}
}
}
对新想法草案提出质疑:
pub结构切片缓冲区{
柜台:期权,
min_line:使用,
数据框:,
索引:ArrayDeque,
}
结构条目{
开始:使用,
长度:usize,
}
不管出于什么原因,当前的方法仍然非常快,尽管我预计会有很多不同大小的分配(取决于行数)并因此导致碎片化。让我们回到基础 循环缓冲区通常保证没有碎片,因为它不是按存储的内容键入的,而是按大小键入的。例如,您可以定义1MB循环缓冲区。对于固定长度类型,这为您提供了可以存储的固定数量的元素 你显然没有这样做。通过将
Vec
存储为一个元素,即使整个数组的长度是固定的,内容也不是固定的。存储在数组中的每个元素都是一个胖指针,指向Vec
(起点和长度)
当然,当您插入时,您必须:
Vec
(这是您正在考虑的碎片,但并没有真正看到,因为rust分配器在这方面非常有效)不安全的部分
实现我们自己的循环缓冲区
我们将做出一系列假设,并为此设定一些要求:
- 我们希望能够存储大字符串
- 我们的缓冲区存储字节。因此,整个堆栈处理的是所拥有的
u8
- 我们将使用一个简单的
Vec
;实际上,您根本不会重新实现整个结构,阵列只是为了演示
这些选择的结果是以下元素结构:
| Element size | Data |
|--------------|----------|
| 4 bytes | N bytes |
因此,我们在每条消息之前丢失了4个字节,以便能够获得指向下一个元素的清晰指针/跳过引用(最大大小与u32
相当)
一个简单的实现示例如下():
使用字节顺序::{NativeEndian,readbytetext,writebytextest};
发布结构循环缓冲区{
资料来源:Vec,
尾巴:用,
元素:usize,
}
impl循环缓冲器{
pub fn new(最大值:usize)->Self{
循环缓冲器{
数据:Vec::具有最大容量,
元素:0,
尾:0,
}
}
///缓冲区中元素的数量
发布fn元素(&self)->使用{
自我要素
}
///缓冲区中使用的字节数,包括元数据
pub fn len(&self)->使用{
尾巴
}
///ringbuffer中第一个元素的长度
pub fn next_element_len(&self)->选项{
自我数据
.get(0..4)
.然后(| mut v | v.read_u32::().ok().map(| r | r as usize))
}
///移除ringbuffer中的第一个元素(换行)
pub fn pop(&mut self)->选项{
self.next_element_len().map(| chunk_size|{
self.tail-=块大小+4;
self.elements-=1;
自我数据
.拼接(..(块大小+4),向量![])
.skip(4)
.collect()
})
}
pub fn get(&self,idx:usize)->选项{
如果自选元素的容量{
self.pop();
}
self.data.write_u32::(e_len as u32.unwrap();
self.data.append(&mut元素);
self.tail+=4+e_len;
自身元素+=1;
println!(“{:?}”,self.data);
}
}
请再次注意,这是一个针对show的幼稚的实现