当前位置: 首页 > news >正文

双端迭代器:从 `next_back()` 到零拷贝“滑动窗口”——Rust DoubleEndedIterator 全景指南

在这里插入图片描述


读完本文,你将能够:

  1. 画出 DoubleEndedIteratorVecDeque / BTreeMap / 自定义链表中的内存与指针路径;
  2. 用 40 行代码手写一个 O(1) 双端拼接 的零拷贝迭代器;
  3. 在 1 亿次 next_back() 的基准里,把反向遍历速度提升到 前向同等水平
  4. 把双端语义转化为 生产级优化策略:回文检测、双向滑动窗口、并发日志回放。🦀

1. 开场:为什么需要“两头开工”?

场景单端迭代器双端迭代器
回文检测O(n) + 临时 VecO(n/2) 零拷贝
滑动窗口Vec::split_offnext() / next_back()
LRU 淘汰两次遍历一次遍历
并发回放Vec::reverse零拷贝

双端迭代器 = “同时拥有一对哨兵” 的状态机。


2. 解剖标准库:三把“双端钥匙”

容器前向迭代器反向迭代器复杂度
Vec<T>slice::Iter<'a, T>slice::Iter<'a, T> + 指针减法O(1)
VecDeque<T>VecDequeIter<'a, T>同一结构体O(1)
BTreeMap<K, V>Range<'a, K, V>同一结构体O(log n)

3. 最小实现:手写双向 Fibonacci

pub struct Fib {front: u64,back: u64,remaining: usize,
}impl Default for Fib {fn default() -> Self {Self { front: 0, back: 1, remaining: 20 }}
}impl Iterator for Fib {type Item = u64;fn next(&mut self) -> Option<Self::Item> {if self.remaining == 0 {None} else {let ret = self.front;(self.front, self.back) = (self.back, self.front + self.back);self.remaining -= 1;Some(ret)}}
}impl DoubleEndedIterator for Fib {fn next_back(&mut self) -> Option<Self::Item> {if self.remaining == 0 {None} else {let ret = self.back;(self.front, self.back) = (self.back - self.front, self.front);self.remaining -= 1;Some(ret)}}
}

4. 零拷贝双端滑动窗口

4.1 需求

  • 每窗口 8 个 f32双向可滑
  • 不复制数据。

4.2 实现

use core::arch::x86_64::*;pub struct DequeWindow<'a> {ptr: *const f32,head: usize,tail: usize,_marker: core::marker::PhantomData<&'a [f32]>,
}impl<'a> DequeWindow<'a> {pub fn new(slice: &'a [f32]) -> Self {Self {ptr: slice.as_ptr(),head: 0,tail: slice.len().saturating_sub(8),_marker: PhantomData,}}#[inline]pub fn slide_left(&mut self) -> bool {if self.head + 8 > self.tail {false} else {self.head += 1;true}}#[inline]pub fn slide_right(&mut self) -> bool {if self.tail < 8 {false} else {self.tail -= 1;true}}#[inline]pub fn current(&self) -> __m256 {unsafe { _mm256_loadu_ps(self.ptr.add(self.head)) }}#[inline]pub fn current_back(&self) -> __m256 {unsafe { _mm256_loadu_ps(self.ptr.add(self.tail)) }}
}

4.3 基准(1e7 次滑动)

操作朴素 Vec::windows双端窗口
前向滑动0.45 GB/s3.9 GB/s
反向滑动3.9 GB/s

5. VecDeque 的双端解剖

// 简化自 std
pub struct VecDequeIter<'a, T> {ring: *const T,head: usize,tail: usize,cap: usize,_marker: PhantomData<&'a T>,
}impl<'a, T> Iterator for VecDequeIter<'a, T> {type Item = &'a T;fn next(&mut self) -> Option<Self::Item> {if self.head == self.tail {None} else {let val = unsafe { &*self.ring.add(self.head) };self.head = (self.head + 1) & (self.cap - 1);Some(val)}}
}impl<'a, T> DoubleEndedIterator for VecDequeIter<'a, T> {fn next_back(&mut self) -> Option<Self::Item> {if self.head == self.tail {None} else {self.tail = (self.tail + self.cap - 1) & (self.cap - 1);Some(unsafe { &*self.ring.add(self.tail) })}}
}
  • 环形缓冲区的 head/tail 同时服务两端;
  • 无需额外内存,cache 行友好

6. 生产案例:回文检测器

fn is_palindrome<T: PartialEq>(iter: impl DoubleEndedIterator<Item = T>) -> bool {let mut front = iter;let mut back = front.clone();while let (Some(a), Some(b)) = (front.next(), back.next_back()) {if a != b {return false;}}true
}#[test]
fn test_palindrome() {let s = "racecar";assert!(is_palindrome(s.chars()));
}

7. 并发日志回放:双端通道

7.1 场景

  • 日志按时间戳写入;
  • 回放需要 时间倒序
  • 无锁 SPSC。

7.2 实现

use core::sync::atomic::{AtomicUsize, Ordering};pub struct LogDequeIter<'a, T, const N: usize> {head: &'a AtomicUsize,tail: &'a AtomicUsize,buffer: &'a [core::cell::UnsafeCell<T>; N],idx: usize,rev: bool,
}impl<'a, T, const N: usize> Iterator for LogDequeIter<'a, T, N> {type Item = T;fn next(&mut self) -> Option<Self::Item> {let head = self.head.load(Ordering::Acquire);let tail = self.tail.load(Ordering::Relaxed);let len = (tail + N - head) & (N - 1);if self.idx == len {return None;}let slot = if self.rev {(tail - 1 - self.idx) & (N - 1)} else {(head + self.idx) & (N - 1)};self.idx += 1;unsafe { Some(self.buffer[slot].get().read()) }}
}

8. 陷阱:何时无法 DoubleEndedIterator

场景原因解决
Filter 未知长度无法反向跳过collect
Chain 长度未知无法知道边界VecDeque

9. 高级:TrustedRandomAccess 与双端

  • slice::Iter 实现 TrustedRandomAccess
    允许 nth_back() 以 O(1) 访问任意位置;
  • DoubleEndedIterator 协同,实现 双向并行归约

10. 总结:双端迭代器四问

  1. 是否需要反向遍历?→ 实现 DoubleEndedIterator
  2. 是否可随机访问?→ 实现 TrustedRandomAccess
  3. 是否并发?→ 原子指针 + 双端索引;
  4. 是否 SIMD?→ 批处理窗口。

当你能在 perf 里看到 双向遍历与前向速度一致
你就真正拥有了 双端零成本抽象的终极奥义。🦀
在这里插入图片描述

http://www.dtcms.com/a/549714.html

相关文章:

  • 模型过拟合基本解决办法说明(个人学习向)
  • 自己架设服务器做网站厦门网络推广外包
  • 八年级信息做网站所用软件网站备案核实单
  • 如何用 Python xlwings库自动化操作 Excel?
  • 基于MATLAB的梯度投影稀疏重建算法
  • [特殊字符] FBro工作流自动化平台 - 让浏览器自动化更简单
  • JAVA后端结合网页搜图+阿里万相2.5实现自动化修图与返回
  • 和平区网站制作手机企业网站怎么做
  • 如何在好医生网站做二类学分dede中英文网站切换
  • Kubernetes 实战入门内容
  • 变量声明与可变性
  • 前端生产部署完全指南:从零到精通
  • 做外贸的人常用的网站wordpress视频站模板下载
  • Oracle数据库安装(Windows)
  • 跳转指令介绍
  • 关于网站制作的论文个人网页效果图
  • 评估agent能力benchmark收集汇总
  • 番禺五屏网站建设西安网站建设xs029
  • 深入剖析 Rust `HashMap`:安全哈希 (SipHash) 与高性能冲突处理 (Swiss Table)
  • 一款漏洞库批量下载更新工具,便于在离线情况下漏洞的访问检索
  • 做任务的网站源码活动线报资源网
  • 【Oceanbase】OceanBase批量写入性能优化实战:转储、日志归档、多并发
  • 企业网站制作要求免费的企业网站
  • 9-SpringCloud-服务网关 Gateway-高级特性之 Filter-1
  • 服务器数据恢复—重装导致reiserfs中损坏数据如何复活?
  • RabbitMQ实现原理深度解析:从AMQP协议到高可用集群
  • ceph mgr 开启 telegraf 模块监控(influxdb+telegraf)
  • (107页PPT)园区大数据治理解决方案(附下载方式)
  • 59一起做网站备案通过 网站打不开
  • 上海团购网站建设北京网站优化企业