尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Rerun 的 `[rerun(own_chunk)]` 属性:让视频关键帧标记独占 Chunk,彻底告别无谓下载

Rerun 的 `[rerun(own_chunk)]` 属性:让视频关键帧标记独占 Chunk,彻底告别无谓下载 Rerun 的#[rerun(own_chunk)]属性让视频关键帧标记独占 Chunk彻底告别无谓下载【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun导读在 Rerun 的 0.37 版本中VideoStream:is_keyframe视频关键帧标记被重构为永远独占一个 Chunk 的独立数据块读取端只需拉取这份极小的标记数据即可完成关键帧扫描完全不需要下载任何一帧视频样本。本文围绕 changeset-0-37.md 中「Keyframe markers are kept out of the video chunks」小节展开结合仓库源码深入讲解这一机制背后的新型类型定义属性#[rerun(own_chunk)]它如何被解析、如何参与 Chunk 拆分与合并保护、在rerun rrd optimize、Python 优化流水线与 RustChunkStore压实中的触发位置以及它对视频查询、训练数据加载器带来的实际收益。背景为什么关键帧标记必须从视频样本 Chunk 中拆出来Chunk 是 Rerun 数据的最小工作单元Rerun 把一切数据存放在ChunkArrow 编码的数据表中。Chunk 是日志、摄取、存储、查询和可视化的原子工作单元系统的性能开销大致随 Chunk 数量线性增长忽略缓存与索引优化大量小 Chunk 远比少量大 Chunk 低效。把多个小 Chunk 合并成更少、更大的 Chunk 的过程称为compaction压实/合并它贯穿数据生命周期的多个阶段——SDK 端 micro-batching、Viewer 端 Chunk Store 在线压实、以及 CLI 的离线优化。详见 Optimize chunk count。视频场景的特殊矛盾视频流数据里有一个典型的「大小悬殊」组合VideoStream:sample编码后的视频样本一帧往往有几十 KB 甚至更大VideoStream:is_keyframe一个布尔标记用来指示该样本是否为关键帧sync sample / IDR是解码器可以独立从此样本起播的同步点。在 0.36 及更早版本中is_keyframe一直「搭便车」待在样本 Chunk 里与VideoStream:sample共享同一 Chunk。这导致一个尴尬局面任何只想扫描关键帧位置的读取端例如数据加载器定位解码锚点都必须把整份视频样本数据一起下载下来代价高昂。0.37 的解决方案#[rerun(own_chunk)]0.37 引入了一种新的类型定义属性#[rerun(own_chunk)]其语义是标记一个组件为「永远不与另一个组件共享 Chunk」的组件。VideoStream:is_keyframe成为该属性的首个使用者。一旦被标记拆分逻辑会在所有 Chunk 优化的场景下把它独立成一个专属 Chunk并且 Chunk Store 拒绝把它再合并回任何别的 Chunk从而让录制文件一旦形成该布局就保持下去。属性定义在类型定义文件中如何声明Rerun 的类型定义文件.def.rs是 SDK 绑定的唯一事实来源由re_types_builder解析后生成 Rust、Python、C 三种语言的绑定。IsKeyframe的定义位于 is_keyframe.def.rs关键声明如下// This is a Rerun type definition for the SDK, not executable code. // It is parsed by re_types_builder to generate the Rust, Python and C bindings. /// Whether a [rerun::components::VideoSample] contains a keyframe (also known as a sync sample or IDR). #[rerun::rerun_type] #[python(aliases bool)] #[rerun(state stable)] // This marker is tiny, but the video samples it sits next to are not. // Keeping it in a chunk of its own lets a reader scan keyframes without fetching any video data. #[rerun(own_chunk)] pub struct IsKeyframe { pub is_keyframe: rerun::encodings::Bool, }这段定义同时揭示了两层信息类型定义文件中的属性系统#[rerun(own_chunk)]与#[rerun::rerun_type]、#[rerun(state stable)]、#[python(aliases bool)]等并列是 Rerun 代码生成器可识别的属性之一。属性解析逻辑位于 attributes.rs并被 reflection.rs 消费最终注入到各 SDK 的反射信息中如 reflection/mod.rs 中的own_chunk_components()集合。设计动机就在注释里“This marker is tiny, but the video samples it sits next to are not.”——标记极小但它旁边的视频样本极大让标记独占 Chunk读取端扫描关键帧时就无需获取任何视频数据。此外is_keyframe.def.rs的文档注释还强调了关键帧的严格定义必须解码器可重入——解码器仅凭该样本即可从流中间开始解码不依赖任何先前解码器状态并非所有帧内编码帧都符合条件某些编解码器的帧内帧仍可能引用既有解码器状态因此不是合法的同步点。核心实现own_chunk拆分与合并保护#[rerun(own_chunk)]的实现位于 own_chunk.rs模块注释开宗明义Split out components that always belong in a chunk of their own. These are components marked#[rerun(own_chunk)]in their type definition. They are typically small, and queried alone. This overrides the rule that keeps an archetype together (seesuper::thick_thin):VideoStream:is_keyframeleaves the rest ofVideoStreambehind.也就是说常规的 thick/thin 拆分会努力保持同一 archetype 的组件在一起因为 archetype 的组件只有作为整体才有意义例如EncodedImage:blob没有EncodedImage:media_type就无法解码而own_chunk是这条规则的唯一例外——它优先于 archetype 内聚性先把标记组件切出去。split把想要独占 Chunk 的组件切出来pub fn split(chunk: Chunk, options: SplitColumnsOptions) - OptionVecChunk { let (wants_own_chunk, rest): (Vec_, Vec_) chunk .components() .values() .map(|column| column.descriptor) .partition_map(|descriptor| { if wants_own_chunk(descriptor, own_chunks) { Either::Left(descriptor.component) } else { Either::Right(descriptor.component) } }); if wants_own_chunk.is_empty() || (wants_own_chunk.len() 1 rest.is_empty()) { return None; // Nothing to split out, or already a chunk of its own. } let mut splits: VecChunk wants_own_chunk .iter() .map(|component| chunk.components_sliced([*component])) .collect(); if !rest.is_empty() { splits.push(chunk.components_sliced(rest)); } ... }实现要点依据列描述符ComponentDescriptor中的component_type查询own_chunks集合ComponentTypeSet判断该列是否想要独占 Chunk每个想要独占的组件被单独components_sliced成一个 Chunk其余组件留在最后一个 Chunk 中等待后续交给 thick/thin 继续拆分若原本就是「单一组件且无其余组件」的专属 Chunk则返回None不做重复拆分没有component_type的无类型列无法在反射中查询保持原样不动见leaves_untyped_columns_alone测试静态 Chunk 同样会被拆分——读取端要关键帧标记依然不应被迫获取样本见splits_from_a_static_chunk_too测试。may_merge防止 Store 把拆分结果合并回去拆分只是第一步。如果后续压实逻辑又把标记 Chunk 和样本 Chunk 合并布局就前功尽弃。Chunk::concatenable本身并不足以保护这一点——它忽略两个 Chunk 不共享的列因此会「愉快地」把一个专属标记 Chunk 合并进携带样本的 Chunk。为此own_chunk.rs提供了专门的合并守卫may_mergepub fn may_merge(lhs: Chunk, rhs: Chunk, own_chunks: ComponentTypeSet) - bool { if !is_dedicated_own_chunk(lhs, own_chunks) !is_dedicated_own_chunk(rhs, own_chunks) { return true; } has_exactly_same_components(lhs, rhs) }语义要点只有「专属 own_chunk Chunk」受到保护且只保护它不新增列只有当两个 Chunk 恰好拥有完全相同的组件集合时才允许合并has_exactly_same_components两个专属标记 Chunk 之间仍然可以合并——这正是多个关键帧标记最终收敛到单个 marker Chunk 的途径直接从 SDK 日志进来的、标记与样本同处一 Chunk 的普通VideoStreamChunk 不受影响分离它们是split_columns的职责此处拒绝合并反而会阻碍录制过程中的正常压实见allows_merging_chunks_that_hold_the_same_components测试。对应的单元测试refuses_to_merge_a_marker_chunk_with_anything_else明确断言了concatenable返回true而may_merge返回false的对比以及双向markermixed、markersample的合并拒绝。触发位置在哪些环节生效文档明确指出own_chunk拆分在「只要发生 Chunk 优化」的地方都会运行且不可关闭——与 thick/thin 拆分不同没有任何设置能把它关掉。触发点包括触发点说明rerun rrd optimizeCLI 离线优化命令见 merge_optimize.rsrerun.experimental.OptimizationProfilePython 端命名优化档位以及接受它的 Python chunk 处理流水线ChunkStore::compacted/ChunkStore::finalize_compactionRust 端 Chunk Store 压实 API在 compact.rs 中可以看到压实配置会加载re_sdk_types::reflection::own_chunk_components()作为own_chunks集合并注明#[rerun(own_chunk)]是 archetype 内聚规则的唯一例外、在常规拆分之前先行处理。同时 compaction_election.rs 在选举合并候选时也调用may_merge从源头避免把专属 Chunk 拉回混合布局。在 Python 侧OptimizationProfile提供了LIVE面向实时 Viewer 的小 Chunk与OBJECT_STORE面向对象存储/目录服务器的更大 Chunk两个预设其中split_size_ratio参数专门控制「大小悬殊的 archetype 组不要同处一 Chunk」其文档注释同样明确指出VideoStream:is_keyframe这类own_chunk组件会在该设置生效之前就无条件分离见_optimization_profile.py中split_size_ratio的说明。最终ChunkStore也拒绝把这样的专属 Chunk 与任何其他内容合并因此录制一旦形成该布局就会一直保持。实际效果Mp4Reader的稀疏 marker Chunk 布局由于上述机制0.37 中Mp4Reader的流式输出从「每个样本 Chunk 都携带is_keyframe的稠密 true/false 列」改为「单个尾部 marker Chunk 只含is_keyframe且每行只记录关键帧全为true」# 0.35 [0] static - VideoStream:codec [1] GOP 0 - VideoStream:is_keyframe, VideoStream:sample [2] GOP 1 - VideoStream:is_keyframe, VideoStream:sample # 0.36 [0] static - VideoStream:codec [1] GOP 0 - VideoStream:sample [2] GOP 1 - VideoStream:sample [3] marker - VideoStream:is_keyframe (only true rows)连带修复了 GoP rebatching 的隐性开关变更日志还揭示了这带来的一个此前隐藏的问题GoP rebatching 会拒绝is_keyframefalse的行因此在旧布局下稠密的关键帧列会导致collect(optimize…)完全跳过视频 rebatching除非显式传入fix_keyframeTrue。现在关键帧标记独立成稀疏 marker Chunk 后GoP rebatching 无需任何额外标志即可正常工作且 marker 被原样保留而不是被重建。相关逻辑见 rebatch_videos.rs它专门收集不含 sample 列的专属is_keyframeChunk并把它们视为已经是规范形态canonical而单独处理。关键帧专属查询的收益现在「只查关键帧」的查询例如数据加载器确定帧锚点可以直接跳过 sample 列——因为二者已不再共享 Chunk。这与 query_video_keyframes.py 以及 Query videos 中描述的关键帧查询场景直接对应VideoFrameReference配合VideoStream的帧查找正是依赖这类高效的索引读取。实践注意事项1. 区分 marker Chunk 与样本 Chunkis_keyframemarker Chunk 是**时间性temporal**的但它不携带任何样本列。如果代码用not chunk.is_static来判定「这是样本 Chunk」会把 marker Chunk 误判为样本。更稳妥的判定方式是按键VideoStream:sample列是否存在is_sample_chunk VideoStream:sample in chunk.to_record_batch().schema.names2. 重写时间列的map要小心游标位置这个问题在重写时间列例如把 mp4 的 PTS 重标到 wall-clock 时间线的map里最明显位置游标会推进经过 marker Chunk并给它盖上错误的时间戳。因此处理Mp4Reader流的后处理代码必须能识别并单独跳过 marker Chunk。3. 数据加载器的关键帧依赖实验性 PyTorch 数据加载器的压缩视频字段现在要求其兄弟组件VideoStream:is_keyframe存在以便解码范围能可靠地从关键帧开始见 dataloader.md 中关于window与max_staleness的说明。而清单manifest生成也改为使用稀疏的is_keyframe时间戳来校验压缩视频字段并锚定解码范围不再扫描VideoStream:sample时间戳从而避免在构建 manifest 时传输昂贵的编码视频数据。配套诊断rerun rrd stats的 Chunk index analysis要判断一份录制是否值得优化0.37 起rerun rrd stats会在输出末尾追加Chunk index analysis小节。它只依据 chunk index不解析任何 chunk 数据按 store 计算对比当前时间性 Chunk 数量与在 object-store 优化档位下合并能达到的理论下界Chunk index analysis -------------------- Store StoreId(Recording, droid, WEIRD_5047dd9a_2024_01_21_23h_22m_28s) chunk index columns: 66 Optimization check based on a 2.0 MiB chunk size target (--profile object-store) - theoretical lower bound: 1 149 chunks - effective: 584 409 chunks (508.6×) - excess: 583 260 chunks ⚠️ This recording may be unoptimized — consider running rerun rrd optimizetheoretical lower bound在该目标 Chunk 大小下合并理论上最少能产生的临时 Chunk 数effective录制实际拥有的 Chunk 数excess与倍数为二者差距当相对与绝对超额同时越过阈值时输出此警告该分析要求录制携带 chunk index旧文件需先运行rerun rrd migratestats.rs 中也提示了这一点实现位于 stats.rs并注明该检查目前不包含 GoP batching 等其他优化项。小结#[rerun(own_chunk)]是 Rerun 类型系统提供的一种「以列为单位的物理布局约束」把体积小、常被单独查询的组件当前即VideoStream:is_keyframe声明为永不与别的组件共享 Chunk并通过「拆分时优先切出 合并时严格守卫」两条机制保证布局在优化流水线的任何环节都不会被破坏。它的直接收益是让关键帧扫描从「必须携带视频样本」变为「只拉一份极小的布尔列」同时顺带修复了 GoP rebatching 被稠密关键帧列意外禁用的老问题。这一机制的应用面也相当明确只要触发 Chunk 优化——无论是 CLI 的rerun rrd optimize、Python 的OptimizationProfile还是 Rust 的ChunkStore压实——该属性都会无条件生效是 Rerun 存储布局中一处小而关键的默认保证。【免费下载链接】rerunVisualize, query, and stream to train on multimodal robotics data.项目地址: https://gitcode.com/GitHub_Trending/re/rerun创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表