- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在缓冲进程的 stdout、stderr 和 stdin 的最后 X 行。我想保留最后的 X 行并能够通过其 id(行号)访问一行。因此,如果我们存储 100 行并插入其中的 200 行,您可以访问第 100-200 行。(实际上我们想要存储 ~2000 行。)
性能案例是插入。所以插入本身应该很快。检索偶尔会发生,但可能占用例的 10%。(大多数时候我们不会查看输出。)
旧方法,碎片化
我用了包装 ArrayDeque
然后记下行数,但这意味着我们使用的是 [Vec<u8>;100]
在上面的例子中。一个字符串数组,因此是一个 Vec<u8>
的数组.
新方法,有开放性问题
我的*新想法是将数据存储在一个 u8 数组中,然后记录数组中每个条目的起始位置和长度。这里的问题是我们需要簿记也是某种环形缓冲区,并在我们的数据数组必须换行时删除旧条目。也许还有更好的方法来实现这个?至少这充分利用了环形缓冲区并防止了内存碎片。
*也感谢 Rust 社区的 sebk
当前的简单方法
const MAX: usize = 5;
pub struct LineRingBuffer {
counter: Option<usize>,
data: ArrayDeque<[String; MAX], Wrapping>,
min_line: usize,
}
impl LineRingBuffer {
pub fn new() -> Self {
Self {
counter: None,
data: ArrayDeque::new(),
min_line: 0,
}
}
pub fn get<'a>(&'a self,pos: usize) -> Option<&String> {
if let Some(max) = self.counter {
if pos >= self.min_line && pos <= max {
return self.data.get(pos - self.min_line);
}
}
None
}
pub fn insert(&mut self, line: String) {
self.data.push_back(line);
if let Some(ref mut v) = self.counter {
*v += 1;
if *v - self.min_line >= MAX {
self.min_line += 1;
}
} else {
self.counter = Some(0);
}
}
}
质疑的新想法草案:
pub struct SliceRingbuffer {
counter: Option<usize>,
min_line: usize,
data: Box<[u8;250_000]>,
index: ArrayDeque<Entry,Wrapping>,
}
struct Entry {
start: usize,
length: usize,
}
无论出于何种原因,当前的方法仍然非常快,尽管我预计会有很多不同大小的分配(取决于行)并因此产生碎片。
最佳答案
让我们回到基础。
循环缓冲区通常保证没有碎片,因为它不是按您存储的内容分类的,而是按大小分类的。例如,您可以定义一个 1MB 的循环缓冲区。对于固定长度类型,这为您提供了可以存储的固定数量的元素。
你显然没有这样做。通过存储 Vec<u8>
作为一个元素,即使总体数组是固定长度的,但内容不是。数组中存储的每个元素都是一个指向 Vec
的胖指针。 (起点和长度)。
当然,当您插入时,您必须:
Vec
(这是您正在考虑的碎片,但实际上并没有看到,因为 Rust 分配器在这类事情上非常有效)您的第二个选择是实际 循环缓冲区。如果操作正确,您将获得固定大小和零分配,您将失去存储 整行 的能力,并 100% 保证在缓冲区的开头有整行。
在我们进入 DYI 的广阔领域之前,快速指向 VecDeque
一切顺利。这是您实现的优化得多的版本,尽管有一些(完全有保证的)unsafe
部分。
我们将做出一系列假设并为此设置一些要求:
u8
Vec
;实际上,您根本不会重新实现整个结构,该数组纯粹用于演示这些选择的结果是以下元素结构:
| Element size | Data |
|--------------|----------|
| 4 bytes | N bytes |
因此,我们在每条消息之前丢失 4 个字节,以便能够获得对下一个元素的明确指针/跳过引用(最大大小相当于 u32
)。
一个简单的实现示例如下(playground link):
use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
pub struct CircularBuffer {
data: Vec<u8>,
tail: usize,
elements: usize,
}
impl CircularBuffer {
pub fn new(max: usize) -> Self {
CircularBuffer {
data: Vec::with_capacity(max),
elements: 0,
tail: 0,
}
}
/// Amount of elements in buffer
pub fn elements(&self) -> usize {
self.elements
}
/// Amount of used bytes in buffer, including metadata
pub fn len(&self) -> usize {
self.tail
}
/// Length of first element in ringbuffer
pub fn next_element_len(&self) -> Option<usize> {
self.data
.get(0..4)
.and_then(|mut v| v.read_u32::<NativeEndian>().ok().map(|r| r as usize))
}
/// Remove first element in ringbuffer (wrap)
pub fn pop(&mut self) -> Option<Vec<u8>> {
self.next_element_len().map(|chunk_size| {
self.tail -= chunk_size + 4;
self.elements -= 1;
self.data
.splice(..(chunk_size + 4), vec![])
.skip(4)
.collect()
})
}
pub fn get(&self, idx: usize) -> Option<&[u8]> {
if self.elements <= idx {
return None;
}
let mut current_head = 0;
let mut current_element = 0;
while current_head < self.len() - 4 {
// Get the length of the next block
let element_size = self
.data
.get(0..4)
.and_then(|mut v| v.read_u32::<NativeEndian>().ok().map(|r| r as usize))
.unwrap();
if current_element == idx {
return self
.data
.get((current_head + 4)..(current_head + element_size + 4));
}
current_element += 1;
current_head += 4 + element_size;
}
return None;
}
pub fn insert(&mut self, mut element: Vec<u8>) {
let e_len = element.len();
let capacity = self.data.capacity();
while self.len() + e_len + 4 > capacity {
self.pop();
}
self.data.write_u32::<NativeEndian>(e_len as u32).unwrap();
self.data.append(&mut element);
self.tail += 4 + e_len;
self.elements += 1;
println!("{:?}", self.data);
}
}
请再次注意,这是一个天真的实现,旨在向您展示如何绕过缓冲区中的字符串剪裁问题。 “真正的”最佳实现将 unsafe
移动和删除元素。
关于data-structures - 字符串的循环缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57904011/
这个问题在这里已经有了答案: Possible to make an event handler wait until async / Promise-based code is done? (2
我经常有多个运行的进程(R,Python,eshell/shell),对于每个进程,我经常都有一个相关的脚本,可以从中发送摘要。为此,我通常将每个框架垂直地分成两个窗口,以便脚本文件(例如.py)位于
如何修改 emacs 在关闭缓冲区后选择要显示的缓冲区的方式? 当我有多个列显示相同的缓冲区,然后在其中一个缓冲区中打开另一个文件,然后关闭新打开的缓冲区时,它不会切换回前一个缓冲区,而是切换到另一个
如何将 ex 命令复制到剪贴板或粘贴到缓冲区? 在 Windows 上使用 gvim。 最佳答案 windows剪贴板可以通过the buffer + 访问.因此,可以使用 + 将剪贴板粘贴为前命令。
在 javascript 中如何以比以下更简单的方式获取 b 缓冲区? var num=6553599 var a = new Buffer(4); a.writeInt32LE(num)
每次我在 Google 上搜索有关 OpenGL 编程的文章时,我都会找到一些文章,但似乎所有文章都提到了着色器和缓冲区。那些是什么?你能解释其中的一些吗: 深度缓冲区 模板缓冲区 像素着色器 帧缓冲
我有java考试,当我学习时,我看到了这个练习,我尝试解决它,但我发现一些困难,所以请帮助我考虑实用程序中方法的以下注释、 header 和部分代码名为 Atbash 的加密类。 /**
每次我在 Google 上搜索有关 OpenGL 编程的文章时,我都会找到一些文章,但似乎所有文章都提到了着色器和缓冲区。那些是什么?你能解释其中的一些吗: 深度缓冲区 模板缓冲区 像素着色器 帧缓冲
对于每个属性使用跨步顶点缓冲区与紧密打包缓冲区有何优缺点?我的意思是例如: 步幅:xyzrgb xyzrgb xyzrgb 紧:xyzxyzxyz rgbrgbrgb 乍一看,使用步幅时您似乎可以轻松
我正在尝试将文本文件中每行的数字读取到 ArrayList 中。当我执行以下函数时,它总是跳过最后一个元素。有人可以帮我吗?因为我在这里没有遇到问题,因为它读取直到缓冲区为空,所以他应该在到达 Fil
#include #include int main () { time_t time_raw_format; struct tm * ptr_time; char *buff
基本上我有一个包含不同类型数据的自定义结构。例如: typedef struct example_structure{ uint8_t* example_1[4]; int example_2[4];
我之前的列表实现是一个简单的 LinearLayout,位于一个装满我的项目的 ScrollView 中。 我切换到 ListView 的 Android 实现以简单地使用 CursorAdapter
我想创建一个可变长度的输入事件窗口/缓冲区,当它接收到额外的事件时会变长。 这是为了实现“键入时搜索”功能。我想捕获点击,但为了不给服务器造成压力,我想明智地进行服务调用。 我想到的逻辑是缓冲击键,从
我想将 yuv420P 像素写入缓冲区而不是二进制文件。假设我在指针中存储了 luma 、 Cb 和 Cr。 luma = output_pixel.luma; cb = output_pixel.c
我想在 Go 中构建一个支持多个并发读取器和一个写入器的缓冲区。所有写入缓冲区的内容都应由所有读者读取。允许新读者随时加入,这意味着已经写入的数据必须能够为迟到的读者回放。 缓冲区应满足以下接口(in
本文转载自微信公众号「小明菜市场」,作者小明菜市场。转载本文请联系小明菜市场公众号。 前言 Java NIO 需要理解的主要有缓冲区,通道,选择器,这三个主要的部分。 基础
一 点睛 NIO,可以称为 New IO 或 Non Blocking IO,是在 JDK 1.4 后提供的新 API。传统的I/O 是阻塞式的 I/O、面向流的操作;而 NIO 是非阻塞 I/O 、
我正在寻找一种切换到包含搜索文本的缓冲区的方法。 例如。如果我打开了 100 个缓冲区,我想切换到一个包含 'fooBar = 1' 的缓冲区 最佳答案 我写了一个 Vim 插件来做到这一点:buff
我正在尝试将提取的视频帧(我使用 ffmpeg)推送到 FFMPEG 缓冲区中。我已经查看了 ffmpeg 的缓冲区源文件,例如 buffersrc.c 和 fifo.c,以确定我是否可以这样做,但我
我是一名优秀的程序员,十分优秀!