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

鸿蒙 PC Markdown 编辑器千文件工作区压力测试

鸿蒙 PC Markdown 编辑器千文件工作区压力测试

仓库地址:https://gitcode.com/VON-/codex_md_oh

代码基线:工作区搜索功能基线2ca99e9,G3-10 千文件设备回归941a1dc,当前状态6c9d88f

千文件测试不是制造一千条结果

工作区全文搜索在十几个演示文件上运行很快,并不能说明它适合真实知识库、项目文档和技术仓库。目录枚举、扩展名过滤、符号链接、UTF-8 解码、正文匹配、结果上限、取消、排序与 ArkUI 列表渲染会随着文件数量放大。G3-10 因此在鸿蒙 PC 模拟器中建立一千文件原生压力语料。

最容易写错的压力测试,是让一千个文件都包含同一个查询词。OhMarkdown 的工作区结果上限是 500,一旦达到上限,控制器可以提前停止。测试虽然返回很快,却可能只扫描前几百个文件,完全没有覆盖“一千文件工作区”。

本轮采用尾部唯一命中:report-0000.mdreport-0998.md只有普通内容,排序最后的report-0999.md才包含“唯一压力命中”。控制器必须发现一千个文件、扫描一千个文件、跳过零个,才能得到唯一结果。性能数字因此建立在完整遍历上,而不是结果上限带来的假快。

语料生成必须确定且可清理

ohosTest 在应用测试沙箱中动态创建目录和文件,不把一千个样本提交进仓库。文件名使用四位补零,保证词法排序与数值顺序一致;正文短小固定,只有最后一个文件出现目标词。

awaitfileIo.mkdir(searchWorkspace);for(letindex=0;index<1000;index+=1){constsequence=index.toString().padStart(4,'0');constcontent=index===999?'# 压力文档\n\n唯一压力命中\n':`# 文档${sequence}\n\n普通内容\n`;awaitwriteRawText(`${searchWorkspace}/report-${sequence}.md`,content);}

确定性语料有三个价值。第一,失败时可以直接推导目标路径,不依赖随机种子。第二,每个文件大小接近,性能变化更容易归因于枚举和调度。第三,内容完全合成,不会把真实用户路径、文件名和正文带进日志或测试报告。

测试的beforeEachafterEach负责递归清理,下一轮开始前仍先清理一次,避免上次异常中断留下旧文件。压力数据属于测试产物,不应污染版本库,也不应成为应用首次启动的数据负担。

工作区边界先于搜索算法

搜索服务不是对任意目录无限递归。当前代码明确限制单目录子项 2000、目录数量 2000、Markdown 文档 5000、单文件可搜索字节 4 MiB、总结果 500、单文件结果 50、快速打开结果 50。支持的后缀包括.md.markdown.mdown.mkd.txt,并排除.git.hg.svnnode_modules

constMAX_DIRECTORY_CHILDREN:number=2000;constMAX_WORKSPACE_DIRECTORIES:number=2000;constMAX_WORKSPACE_DOCUMENTS:number=5000;constMAX_SEARCHABLE_DOCUMENT_BYTES:number=4*1024*1024;constMAX_WORKSPACE_SEARCH_RESULTS:number=500;constMAX_RESULTS_PER_DOCUMENT:number=50;constMAX_QUICK_OPEN_RESULTS:number=50;constREAD_CHUNK_BYTES:number=64*1024;

边界不是为了把复杂度藏起来,而是让时间、内存和结果列表保持可解释。达到上限时摘要必须标记truncated,界面应告诉用户结果被截断。沉默丢弃比明确限制更危险,因为用户会误以为没有其他匹配。

目录枚举遵守授权范围

OhMarkdown 从用户授权的工作区 URI 开始广度遍历,不扫描工作区之外的磁盘。条目名先拒绝空名、...、斜杠、反斜杠和 NUL;lstat遇到符号链接直接跳过,避免链接把遍历带出授权树或形成循环。

constpending:Array<PendingWorkspaceDirectory>=[{uri:rootUri,relativePath:''}];while(pending.length>0&&documents.length<MAX_WORKSPACE_DOCUMENTS&&directoryCount<MAX_WORKSPACE_DIRECTORIES){constcurrent=pending.shift();constlistedNames=awaitfileIo.listFile(getPathFromUri(current.uri),{recursion:false,listNum:MAX_DIRECTORY_CHILDREN+1});constvisibleNames=listedNames.slice(0,MAX_DIRECTORY_CHILDREN).filter(isSafeEntryName);// 对每个条目执行 lstat;符号链接跳过,目录入队,Markdown 入表。}

每轮枚举请求上限加一,用第 2001 个条目判断目录是否被截断,再只处理预算内的前 2000 个。最终文档按相对路径排序,使相同工作区的搜索顺序可重复,也让尾部命中语料真正位于扫描末端。

Core File Kit 读取需要防变化

全文搜索打开文件时使用只读与NOFOLLOW,随后读取stat.size,每次最多读取 64 KiB。解码器使用 UTF-8 fatal 模式,非法字节不会被静默替换成乱码。读完后必须确认总读取字节等于开始时的大小,否则把文件视为搜索期间发生变化。

constfile=awaitfileIo.open(document.uri,fileIo.OpenMode.READ_ONLY|fileIo.OpenMode.NOFOLLOW);conststat=awaitfileIo.stat(file.fd);if(stat.size>MAX_SEARCHABLE_DOCUMENT_BYTES){thrownewError('The workspace document exceeds the search limit.');}constdecoder=util.TextDecoder.create('utf-8',{fatal:true,ignoreBOM:false});while(totalBytesRead<stat.size){constrequestedBytes=Math.min(READ_CHUNK_BYTES,stat.size-totalBytesRead);// 分块读取并在每个 await 后检查本轮搜索仍有效。}

分块读取的意义不是让整个文件永不进入内存,当前实现最终仍会chunks.join('');它主要控制单次原生缓冲区,并为取消提供检查点。四兆单文件上限则阻止少数巨大文档把工作区搜索拖入大文件渲染同类风险。

TaskPool 隔离正文匹配

目录枚举和文件读取使用异步 Core File Kit,正则或长文本匹配属于 CPU 工作。OhMarkdown 将单篇searchDocumentContent放进低优先级 TaskPool,避免在 ArkUI 主线程同步遍历正文。任务返回结果偏移、行列、预览和是否截断,原生控制器再合并到本轮摘要。

consttask=newtaskpool.Task(searchDocumentContent,document,content,query,options,Math.min(MAX_RESULTS_PER_DOCUMENT,remainingResults));this.activeTask=task;constoutput=awaittaskpool.execute(task,taskpool.Priority.LOW)asWorkspaceDocumentSearchOutput;

当前实现按文件顺序读取并逐个等待 TaskPool,而不是一次创建一千个任务。这种保守模型限制并发内存,也保持结果顺序稳定。后续若做并行窗口,必须同时约束打开句柄、解码缓冲、任务数量与取消回收,不能只追求更小毫秒数。

中文整词边界不能照搬 ASCII

工作区搜索支持区分大小写、整词和正则。整词判断如果只使用 JavaScript\b,中文标题和正文会出现不符合用户预期的边界。服务显式把数字、拉丁字母、下划线、CJK 扩展 A、CJK 统一汉字与兼容汉字视为词字符。

这意味着查询“鸿蒙”时,紧邻另一个汉字的内容不会被错误当成完整词;标点与空白则形成边界。压力语料使用精确中文词组,不仅验证文件规模,也经过中文匹配路径。真正的语言质量仍应扩展到日文假名、韩文、全角符号和组合字符,当前范围不能被宣传成完整 Unicode 分词。

正则零长度匹配需要推进

用户正则可能产生零长度匹配。如果循环只把lastIndex保持原值,TaskPool 会无限执行。当前匹配器检测match[0].length === 0后主动将lastIndex加一,并在每轮查询中检查任务是否被取消。

结果上限也在匹配函数内部执行。单篇达到配额后设置foundMore并停止,避免生成大量结果再由 UI 丢弃。性能优化与正确性在这里是一件事:只计算产品能够展示和解释的数据。

代际取消阻止旧结果覆盖新查询

用户在 PC 搜索框中连续输入时,上一轮可能还在枚举目录或匹配正文。若旧查询后完成并覆盖面板,界面会显示与当前输入不一致的结果。WorkspaceSearchController 为每轮维护 generation;新搜索先调用cancel,generation 加一并取消当前 TaskPool,所有异步断点随后通过assertActive检查代际。

cancel():void{this.generation+=1;if(this.activeTask){try{taskpool.cancel(this.activeTask);}catch(_){}this.activeTask=undefined;}}privateassertActive(generation:number):void{if(generation!==this.generation){thrownewError('Workspace search canceled.');}}

取消不是简单隐藏加载动画。读取循环、目录枚举、任务返回和最终汇总都检查代际,旧结果无法在新查询之后提交。基础 ohosTest 还会发起搜索后立即取消,并断言观察到 canceled 错误。

压力断言覆盖准确与规模

千文件用例没有只断言“耗时小于十秒”。它同时验证发现数、扫描数、跳过数、结果数量与目标路径。性能门槛依赖正确性门槛,否则返回空数组也会成为最快实现。

constsearchSummary=awaitcontroller.search(searchWorkspace,'唯一压力命中',{caseSensitive:true,wholeWord:false,regularExpression:false});expect(searchSummary.documentsDiscovered).assertEqual(1000);expect(searchSummary.documentsScanned).assertEqual(1000);expect(searchSummary.documentsSkipped).assertEqual(0);expect(searchSummary.results.length).assertEqual(1);expect(searchSummary.results[0].relativePath).assertEqual('report-0999.md');expect(searchSummary.elapsedMilliseconds<10000).assertTrue();

当前 MateBook Pro 2in1 模拟器记录全文搜索 419 ms。十秒是跨环境退化保险,不是当前性能目标。它给调试构建、模拟器调度和不同开发机留出余量,同时仍能拦截从亚秒退化到数量级异常的实现。

快速打开必须独立测量

快速打开只依赖文档元数据和文件名模糊排序,不读取一千份正文。它与全文搜索共享目录收集,但产品任务不同:前者是“我大致知道文件名”,后者是“我只知道正文内容”。把两者混成一个性能数字,会让全文搜索看起来虚快,或者让快速打开显得不必要地慢。

constquickOpenSummary=awaitcontroller.quickOpen(searchWorkspace,'report-0999',[]);expect(quickOpenSummary.documentsDiscovered).assertEqual(1000);expect(quickOpenSummary.results.length>0).assertTrue();expect(quickOpenSummary.results[0].relativePath).assertEqual('report-0999.md');expect(quickOpenSummary.elapsedMilliseconds<5000).assertTrue();

最近文件数组故意传空,避免会话历史给目标额外加权。当前模拟器记录 50 ms,证明一千文件元数据收集与精确文件名排序没有出现秒级停顿。五秒同样是回归上限,不是把 50 ms 承诺给全部硬件和目录层级。

模糊排序兼顾路径与最近使用

文件名完全相等获得最高分,前缀匹配次之,路径包含再次之,最后才做子序列匹配。连续字符、路径段开头、短路径会得到更高分;最近打开 URI 只增加权重,不改变文件是否存在这一事实来源。

排序最后使用相对路径稳定决胜,保证同分结果可重复。返回前 50 条可以让面板保持轻量,但也意味着空查询或宽泛查询会截断。界面需要保留“结果有限”语义,不能让用户误解为工作区只有这些文件。

应用内部搜索界面

下图来自 OhMarkdown 应用内部工作区搜索面板,展示搜索输入、范围与选项在鸿蒙 PC 桌面布局中的承载方式。压力用例调用同一WorkspaceSearchController和 Core File Kit 路径,不是 Node.js 中脱离应用的纯字符串跑分。

截图证明应用具备真实搜索入口和结果承载面,但不能显示 419 ms。时间来自 ohosTest 与 hilog,文件规模来自断言,界面布局来自应用截图。三类证据各自回答不同问题,文章不会用静态画面替代性能日志。

快速打开的桌面交互价值

鸿蒙 PC 用户使用键盘在大量文档之间跳转时,快速打开通常比展开目录树更高效。文件名模糊匹配、最近使用加权、上下键选择、Enter 打开和 Esc 返回编辑器共同组成完整任务。服务层 50 ms 只是其中一部分,焦点路由与面板渲染仍需要设备交互验证。

产品优势不应只比较算法耗时。标准任务还要计入打开面板的操作数、首次结果稳定时间、误选次数和打开后光标位置。后续与 VS Code、Obsidian、Typora 等竞品比较时,必须使用相同语料、冷暖缓存和键盘任务,不能拿服务日志与竞品人工感受直接相减。

419 毫秒的证据边界

419 ms 来自 MateBook Pro 2in1 模拟器、Debug 测试 HAP 和一千个同目录小文件。它包含目录收集、文件读取、TaskPool 匹配和汇总,但不包含用户从按快捷键到看见结果列表的所有渲染时长,也不是 Release 真机 P95。

同目录短文件对元数据访问友好;多层目录、网络同步盘、冷缓存、较大正文、复杂正则和大量命中都会改变结果。当前数字可以证明实现没有明显的线性常数灾难,也可以作为未来回归基线,但不足以宣称比竞品快某个百分比。

完整十项 ohosTest 在本轮为10/10,Failure 与 Error 都为零。测试 HAP 与 Debug HAP 均记录 SHA-256,使日志可绑定产物。即便如此,模拟器依然不能替代真实鸿蒙 PC 的文件系统、存储、内存压力和物理键盘体验。

压力测试还缺少哪些维度

当前样本主要验证文件数量,不验证大总字节量。下一组语料应采用多层目录、中文长路径、4 MiB 边界文件、非法 UTF-8、无权限子目录、编辑中变化、同名文件和符号链接。查询矩阵加入大小写、中文整词、复杂正则、零长度正则、大量结果和连续取消。

性能维度需要冷/热缓存分组、P50/P95、峰值 PSS、CPU、打开句柄与取消后的资源归零。界面维度要测一百条结果渲染、滚动、键盘选择、准确跳转和窄侧栏。只有服务、系统和交互三层都通过,才能把“千文件可用”升级为稳定产品承诺。

从一千文件走向增量索引

当前本地直接扫描符合“无数据库、低复杂度、内容实时”的 G3 边界。一千个小文件 419 ms 说明现阶段不需要提前引入常驻索引。索引会带来文件监听、失效、迁移、磁盘占用、隐私、加密和损坏恢复等长期成本。

当真实用户工作区达到数万文件或正文总量显著上升,才应以数据触发架构升级。可选方向包括按文件指纹缓存、倒排索引、增量监听和分片持久化,但必须保留无索引回退与重建。性能优化不能让搜索结果比磁盘事实更旧,也不能把本地正文上传到云端。

隐私与日志原则

压力语料使用固定中文和序号,日志只记录发现数、扫描数、跳过数、耗时与合成相对路径。生产日志不应记录用户查询词、正文预览、完整路径或文件名。搜索结果只存在当前应用会话,工作区授权由系统文件能力控制。

后续匿名质量数据也只需要数量区间、耗时桶、是否取消和错误类型。为了分析性能而上传“最慢文件路径”会泄露项目结构;为了分析搜索准确性而上传查询词,可能泄露客户、密码或内部代号。本地优先产品的优势需要在测量阶段继续成立。

可复现测试清单

复现时应固定代码提交、测试 HAP 哈希、HarmonyOS 镜像、设备配置和语料生成代码。先清理测试目录,安装最新 ohosTest HAP,执行指定套件,保存 hilog;结束后确认目录删除。若只重跑单项,还要再执行全量10/10,防止测试隔离依赖被掩盖。

结果报告至少包含发现、扫描、跳过、命中、目标路径、全文搜索耗时和快速打开耗时。任何一个正确性断言失败,性能数字作废;若缓存状态不明,标记为未分类,不与历史基线直接比较。这样的记录才能让不同开发机和后续真机数据进入同一张表。

对鸿蒙 PC 编辑器优势的贡献

文件树解决“浏览”,快速打开解决“知道名字”,全文搜索解决“只记得内容”。三种导航方式并存,才适合桌面上的重复工作。千文件压力把工作区搜索从功能演示推进到规模证据,也让结果上限、取消、安全边界和 UTF-8 失败路径具备明确预算。

OhMarkdown 当前优势不是宣称拥有最复杂的索引,而是在无需联网和常驻数据库的条件下,一千文件仍能准确完整扫描,并在用户继续输入时取消旧任务。架构保持简单,证据保持可复现,后续只有在真实规模推动时才增加索引复杂度。

结论

鸿蒙 PC Markdown 编辑器的千文件测试必须同时验证规模、准确、取消、资源边界和任务语义。OhMarkdown 使用尾部唯一命中避免结果上限造成假快,在模拟器中确认发现一千、扫描一千、跳过零、唯一命中report-0999.md,全文搜索 419 ms,快速打开 50 ms。

这些结果绑定941a1dc代码与原生 ohosTest,足以建立 G3-10 内部压力基线,但不替代 Release 真机、多层大语料、冷缓存、UI P95 和统一竞品测试。可靠的性能文章不是只给出一个漂亮数字,而是说明这个数字经过了哪些路径、排除了哪些捷径,以及下一步还必须验证什么。

http://www.jsqmd.com/news/1249688/

相关文章:

  • 具身智能进入“干活时代“:从展台演示到真实部署,数据才是核心瓶颈
  • 2026年GEO选型必看:全栈自研与媒体分发路径大揭秘
  • 大模型输出不确定性的工程解决方案
  • 2026年上海普陀区管道疏通避坑指南:快达师傅上门快不乱收费 - 余生黄金回收
  • Gino同传带练小伙伴Dora第12天,介入无稿同传练习第1天。Gino同传带练,每个小伙伴的基础不同情况不一样,有针对性地安排先练哪一项。有的可能先练视译,有的可能先练有稿同传,有的可能先练无稿
  • 蜗牛星际+OpenWrt实战:双网口软路由秒变AP,局域网设备无缝互通指南
  • 医疗设备全生命周期管理系统+学前领先+智慧医院高效运营的核心助力​
  • Profinet--TIAPortal V19安装与项目实战指南
  • 别让科研 Agent 直接从 Chunk 跳到结论:为什么 metadata layer 才是第一道防线
  • 0 基础入门React Native鸿蒙跨平台开发:体重单位转换器功能实战
  • TM4C129XNCZAD GPIO深度解析:从寄存器配置到实战避坑指南
  • 软路由玩家必看:在PVE7.3上实现iKuai+OpenWRT双路由+NAS+Win10的5个性能优化技巧
  • Python计算机毕设之基于 Python 的校园二手社交交易平台实现校园二手物品问答互动与交易系统设计 (完整前后端代码+说明文档+LW,调试定制等)
  • 【C++】多态(虚函数/虚函数表/菱形)
  • 树莓派4B变身软路由全攻略:从OpenWrt刷机到双网卡配置避坑指南
  • 2026 海南海口 OPC 公司注册收费标准,封关后地址、代办、税务全成本明细 - 米諾
  • 本地隐私 AI 工具 OpenClaw ,Windows/Mac 新手全流程安装 + 故障排查汇总
  • 脑筋急转弯API调用边界与QPS限制解析:20并发下的稳定性实践
  • 大连名表回收哪家价高?本地商家实测对比 - 大牌深度测评
  • Banana Pi BPi-R3 Mini 刷机指南:ImmortalWrt 固件从下载到安装全流程
  • YOLO26的SPASPP模块:提升红外小目标检测精度
  • 无痕粘接赛道存活指南:活得好的品牌都做对了什么 - 米諾
  • WebAssembly AI 数据隐私:用户数据不出浏览器,推理在本地完成的架构
  • 斐讯N1盒子刷软路由OpenWrt
  • 深入解析以太网MAC DMA操作模式寄存器:嵌入式网络性能调优核心
  • 金融问答机器人中的知识蒸馏技术实践
  • 4 位还是 8 位:ASC4T245S 与 ASC8T245S 的选型图谱
  • 易拉罐正反面识别数据集下载,支持yolo,coco json,pasical voc xml格式的标注信息,平均正确识别率为99.5%,训练集2373张图片
  • MySQL锁
  • 公众号账号转移有哪些用处?线上申办实操指南 - 跑政通