惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Jina AI
Jina AI
博客园 - Franky
Apple Machine Learning Research
Apple Machine Learning Research
酷 壳 – CoolShell
酷 壳 – CoolShell
阮一峰的网络日志
阮一峰的网络日志
量子位
雷峰网
雷峰网
宝玉的分享
宝玉的分享
V
Visual Studio Blog
博客园_首页
小众软件
小众软件
The Cloudflare Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
S
SegmentFault 最新的问题
博客园 - 【当耐特】
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
月光博客
月光博客
博客园 - 三生石上(FineUI控件)
人人都是产品经理
人人都是产品经理
WordPress大学
WordPress大学

V2EX

我用 AI 写代码,但终端管理反而成了累赘——于是我做了 codux [调研] 各位在公司都用什么 ide 和 agent 写代码? 老运维 share 一个运维平台 看到有公司考核 token 指标,很好奇大家上个月的 AI 账单是多少 GLM-Coding 调用持续报错: z.ai 的 Lite 套餐几乎无法使用,官方 Pro/Max 是否稳定? 现在还有什么渠道可以稳定安全地使用 Claude 吗? 上海漕河泾内推,本组有 2 个 hc,一个后端,一个前端,预算都是 20k 左右,不打卡,氛围好 如果 V2EX 上有一组不永久保存聊天记录(比如只保存 7 天或者 24 小时)的聊天室,那么会开启哪些有用或者有趣的可能? gemini cli 貌似挂了,一直返回 403 第一次在自媒体上赚到钱 收集了最近在使用的低价 GPT, Gemini,邮箱等 AI 会员的小店合集 讨论个大实话:现在企业还在说 AI 编程提效 20%, 30%的,真的太落后,没用懂 AI。因为包括很多前沿公司,已经狂奔到提效 200%-500%的情况 [招聘][远程][币安] 前端/后端/QA/iOS/Android 至少 3 年以上经验 目前有大量 HC 欢迎投递 Chatgpt Pro 用量用不完的可以开这些设置 面试的时候好像遇到钓鱼了,给各位避个坑 cursor 年续费 22 号到期, 自动续费是否还是老的计次套餐呢 被两件破事毁掉的一下午,琐碎的内耗消磨人的精力 使用 Planet 存储 Codex 的会话或者重要信息 如果业务部门领导不要你开发功能,而是要求你教会它用 claude code 开发功能,你会怎么做? 分享一个 MacOS 接绿联 CM818 USB 转 DP 转接器使用感受 我的 HR 朋友 10 年老 Java ,非全大专,大家帮忙看看简历 开源了一个 AI 口语练习工具,音素级发音评分,完全免费可自部署 V2EX 上有哪些你觉得很有趣、印象深刻的妹纸? 字节为啥不出个国内版 Vercel? 有在大马的朋友吗? 问个运营商问题 你们在有领导的公司大群发过的最大胆的消息是什么 公司裁员,目前没有工作。想试试摆摊,做一个移动鲜啤打酒车 我的硬盘 Memblaze Pblaze 5 Linux 下不识别,给 Linux 内核提交了补丁, AI 说有望被合并 只有我一个人觉得 codex 不好用?
为了解决 AI 流式输出的重复解析问题,我发布了 incremark:...
1244943563 · 2025-12-16 · via V2EX

昨天,我发布了周末开发的 incremark。实际性能远超预期——在 AI 流式场景中通常实现了 2-10 倍的速度提升,对于更长的文档提升更大。虽然最初打算作为自己产品的内部工具,但我意识到开源可能是一个更好的方向。

问题所在

每次 AI 流式输出新的文本块时,传统的 markdown 解析器都会从头开始重新解析整个文档——在已经渲染的内容上浪费 CPU 资源。Incremark 通过只解析新增内容来解决这个问题。

基准测试结果

较短的 Markdown 文档:

image.png

较长的 Markdown 文档:

image.png

说明:由于分块策略的影响,每次基准测试的性能提升倍数可能有所不同。演示页面使用随机块长度:const chunks = content.match(/[\s\S]{1,20}/g) || []。这种分块方式会影响稳定块的生成,更好地模拟真实场景(一个块可能包含前一个或后一个块的内容)。无论如何分块,性能提升都是有保证的。演示网站没有使用任何有利于偏向自身性能展示的分块策略来夸大结果。

在线演示:

对于超长的 markdown 文档,性能提升更加惊人。20KB 的 markdown 基准测试实现了令人难以置信的 46 倍速度提升。内容越长,提速越显著——理论上没有上限。

核心优势

通常 2-10 倍提速 - 针对 AI 流式场景
🚀 更大的提速 - 对于更长的文档(测试最高达 46 倍)
🎯 零冗余解析 - 每个字符最多只解析一次
完美适配 AI 流式 - 专为增量更新优化
💪 也适用于普通 markdown - 不仅限于 AI 场景
🔧 框架支持 - 包含 React 和 Vue 组件

为什么这么快?

传统解析器的问题

开发过 AI 聊天应用的小伙伴都知道,AI 流式输出会将内容分成小块传输到前端。每次接收到新块后,整个 markdown 字符串都必须喂给 markdown 解析器(无论是 remark 、marked.js 还是 markdown-it )。这些解析器每次都会重新解析整个 markdown 文档,即使是那些已经渲染且稳定的部分。这造成了很多不必要的的性能浪费。

像 vue-stream-markdown 这样的工具在渲染层做了努力,将稳定的 token 渲染为稳定的组件,只更新不稳定的组件,从而在 UI 层实现流畅的流式输出。

然而,这仍然无法解决根本的性能问题:markdown 文本的重复解析。这才是真正吞噬 CPU 性能的元凶。输出文档越长,性能浪费越严重。

Incremark 的核心性能优化

除了在 UI 渲染层实现组件复用和流畅更新外,incremark 的关键创新在于 markdown 解析只解析不稳定的 markdown 块,永不重新解析稳定的块。这将解析复杂度从 **O(n²) 降低到 O(n)**。理论上,输出越长,性能提升越大。

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

传统解析器每次都重新解析整个文档,导致解析工作量呈二次方增长。Incremark 的 IncremarkParser 类采用增量解析策略(参见 IncremarkParser.ts):

// 设计思路:
// 1. 维护一个文本缓冲区来接收流式输入
// 2. 识别"稳定边界"并将已完成的块标记为 'completed'
// 3. 对于正在接收的块,只重新解析该块的内容
// 4. 复杂的嵌套节点作为一个整体处理,直到确认完成

2. 智能边界检测

append 函数中的 findStableBoundary() 方法是关键优化点:

append(chunk: string): IncrementalUpdate {
  this.buffer += chunk
  this.updateLines()
  
  const { line: stableBoundary, contextAtLine } = this.findStableBoundary()
  
  if (stableBoundary >= this.pendingStartLine && stableBoundary >= 0) {
    // 只解析新完成的块,永不重新解析已完成的内容
    const stableText = this.lines.slice(this.pendingStartLine, stableBoundary + 1).join('\n')
    const ast = this.parse(stableText)
    // ...
  }
}

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

解析器维护几个关键状态来消除重复工作:

  • buffer:累积的未解析内容
  • completedBlocks:已完成且永不重新解析的块数组
  • lineOffsets:行偏移量前缀和,支持 O(1) 行位置计算
  • context:跟踪代码块、列表等的嵌套状态

4. 增量行更新优化

updateLines() 方法只处理新内容,避免全量 split 操作:

private updateLines(): void {
  // 找到最后一个不完整的行(可能被新块续上)
  const lastLineStart = this.lineOffsets[prevLineCount - 1]
  const textFromLastLine = this.buffer.slice(lastLineStart)
  
  // 只重新 split 最后一行及其后续内容
  const newLines = textFromLastLine.split('\n')
  // 只更新变化的部分
}

性能对比

这种设计在实际测试中表现卓越:

文档大小 传统解析器(字符数) Incremark (字符数) 减少比例
1KB 1,010,000 20,000 98%
5KB 25,050,000 100,000 99.6%
20KB 400,200,000 400,000 99.9%

关键不变量

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


🚀 立即开始

停止在冗余解析上浪费 CPU 资源。立即尝试 incremark:

快速安装

npm install @incremark/core
# React 版本
npm install @incremark/react
# Vue 版本
npm install @incremark/vue

资源链接

使用场景

完美适用于:

  • 🤖 带流式响应的 AI 聊天应用
  • ✍️ 实时 markdown 编辑器
  • 📝 实时协作文档
  • 📊 带 markdown 内容的流式数据看板
  • 🎓 交互式学习平台

无论你是在构建 AI 界面还是只是想要更快的 markdown 渲染,incremark 都能提供你需要的性能。


欢迎体验与支持

非常欢迎尝试与体验,在线演示是感受速度提升最直观的方式:

如果你觉得 incremark 有用并想要参与改进,也欢迎提交 issue 与独特想法!GitHub Issues