消除冗余的 Markdown 解析:通常提升 2‑10 倍的 AI Streaming

发布: (2025年12月16日 GMT+8 11:44)
8 min read
原文: Dev.to

Source: Dev.to

(未提供需要翻译的正文内容,无法进行翻译。)

问题

当 AI 流式输出新的文本块时,传统的 markdown 解析器会 从头重新解析整个文档,在已经渲染的内容上浪费 CPU 周期。Incremark 通过仅解析文档的 新部分 来解决此问题。

Source:

基准测试结果 – 眼见为实

短 Markdown 文档

Short document benchmark

较长的 Markdown 文档

Long document benchmark

注意: 由于分块策略的不同,性能提升倍率在不同运行之间可能会有所变化。
演示页面使用随机分块长度:

const chunks = content.match(/[\s\S]{1,20}/g) || []

这模拟了真实场景——一个块可能包含前一个或后一个块的内容。无论内容如何分块,性能提升都是有保障的。演示 使用人为分块来夸大结果。

实时演示

  • Vue 演示:
  • React 演示:
  • 文档:

对于极长的 Markdown 文档,提升更为显著。一个 20 KB 的 Markdown 基准测试实现了 46 倍 的速度提升。内容越长,提速越大——理论上没有上限。

关键优势

优势
在 AI 流式场景下通常 2–10× 更快
🚀对于更长的文档 提升更显著(已测试最高 46×
🎯零冗余解析 – 每个字符仅解析一次
为 AI 流式优化的 增量更新
💪同样支持 普通 markdown
🔧框架支持 – 可直接使用的 React 和 Vue 组件

为什么这么快?

传统解析器的问题

在构建 AI 聊天应用时,AI 会以小块的形式流式输出内容。每当一个块到达后,整个 markdown 字符串都会被送入解析器(例如 remark、marked.js、markdown‑it)。这些解析器 每次都会重新解析整个文档,即使是已经渲染好的稳定部分也会被重新解析。这会导致巨大的性能浪费。

vue‑stream‑markdown 之类的工具通过复用稳定组件提升了 渲染 层,但它们 并没有消除 markdown 文本的重复解析——这才是 CPU 的真正瓶颈。

Incremark 的核心性能优化

Incremark 的突破在于 解析阶段:它 只解析不稳定的 markdown 块,永不重新解析已经稳定的块。这将解析复杂度从 O(n²) 降低到 O(n),也就是说输出越长,性能提升越明显。

1. 增量解析 – 从 O(n²) 到 O(n)

传统解析器在每次更新时都会重新解析整个文档,导致二次方的工作量增长。Incremark 的 IncremarkParser 类实现了增量策略(见 IncremarkParser.ts):

// Design Philosophy:
// 1. Maintain a text buffer to receive streaming input
// 2. Identify "stable boundaries" and mark completed blocks as 'completed'
// 3. For blocks currently being received, re-parse only that block's content
// 4. Complex nested nodes are treated as a whole until confirmed complete

2. 智能边界检测

append() 方法内部的 findStableBoundary() 是关键优化点:

append(chunk: string): IncrementalUpdate {
  this.buffer += chunk
  this.updateLines()

  const { line: stableBoundary, contextAtLine } = this.findStableBoundary()

  if (stableBoundary >= this.pendingStartLine && stableBoundary >= 0) {
    // Only parse newly completed blocks, never re-parse already completed content
    const stableText = this.lines
      .slice(this.pendingStartLine, stableBoundary + 1)
      .join('\n')
    const ast = this.parse(stableText)
    // …
  }
}

3. 状态管理以避免冗余计算

解析器维护了多项状态,用来防止重复工作:

状态目的
buffer累积的未解析内容
completedBlocks永不重新解析的块集合
lineOffsets行长度的前缀和,实现 O(1) 的行‑到‑偏移查找
pendingStartLine当前接收块的第一行索引
stableBoundary已确定为稳定的最后一行

通过仅更新新出现的稳定区域,Incremark 实现了 线性时间的增量解析

入门

# Install the core library
npm i incremark

# For Vue
npm i incremark-vue

# For React
npm i incremark-react
// React example
import { IncremarkRenderer } from 'incremark-react'

function ChatMessage({ stream }) {
  return <IncremarkRenderer markdown={stream} />
}
<script setup>
import { IncremarkRenderer } from 'incremark-vue'
defineProps({ stream: String })
</script>

<template>
  <IncremarkRenderer :markdown="stream" />
</template>

许可证

Incremark 在 MIT 许可证 下发布。欢迎使用、修改和贡献!

增量计算

context:跟踪代码块、列表等的嵌套状态。

4. 增量行更新优化

updateLines() 方法仅处理新内容,避免完整的拆分操作:

private updateLines(): void {
  // Find the last incomplete line (which may be continued by a new chunk)
  const lastLineStart = this.lineOffsets[prevLineCount - 1];
  const textFromLastLine = this.buffer.slice(lastLineStart);

  // Re‑split only the last line and subsequent content
  const newLines = textFromLastLine.split('\n');
  // Only update the changed portions
}

性能比较

文档大小传统解析器(字符)Incremark(字符)减少率
1 KB1,010,00020,00098 %
5 KB25,050,000100,00099.6 %
20 KB400,200,000400,00099.9 %

关键不变量

Incremark 的性能优势源于一个关键不变量:一旦块被标记为完成,就永不重新解析。这确保每个字符最多只被解析一次,实现 O(n) 的时间复杂度。

🚀 立即开始

停止在冗余解析上浪费 CPU 周期。今天就试试 Incremark:

快速安装

npm install @incremark/core
# For React
npm install @incremark/react
# For Vue
npm install @incremark/vue

资源

  • 📚 文档
  • 🎮 在线演示 (Vue)
  • 🎮 在线演示 (React)
  • 💻 GitHub 仓库

用例

  • 🤖 带流式响应的 AI 聊天应用
  • ✍️ 实时 Markdown 编辑器
  • 📝 实时协作文档
  • 📊 带 Markdown 内容的流式数据仪表盘
  • 🎓 互动学习平台

无论是构建 AI 界面,还是仅仅想要更快的 Markdown 渲染,Incremark 都能提供您所需的性能。

💬 试用并展示您的支持

我非常希望您 在项目中尝试 Incremark,亲自感受性能差异!实时演示是体验速度提升的最佳方式。

如果您觉得 Incremark 有用,请考虑 在 GitHub 上为它点个 ⭐ 星——这真的能帮助项目获得更多关注,也能激励我继续改进。欢迎您提供反馈、提交问题以及贡献代码!

  • 🌟 在 GitHub 上加星
  • 💡 报告问题或提出想法
  • 🤝 贡献代码:欢迎提交 Pull Request!

感谢您对 Incremark 的关注!让我们一起让 Markdown 渲染更快 🚀

Back to Blog

相关文章

阅读更多 »

在 LLM 聊天 UI 中追求 240 FPS

TL;DR 我构建了一个 benchmark suite 来测试在 React UI 中 streaming LLM responses 的各种优化。关键要点:1. 首先构建合适的 state,然后再进行优化……