Acl.Excel vs MiniExcel 1.45.0:百万行读写实测,托管内存低至 1/37,QueryFirst 平分秋色
Acl.Excel vs MiniExcel 1.45.0:百万行读写实测,托管内存低至 1/37,QueryFirst 平分秋色
本文所有测试代码与测试数据均直接取自 MiniExcel 官方 benchmark 仓库,仅追加 Acl.Excel 的对比项。本文力求客观:既呈现 Acl.Excel 的优势数据,也如实标注测试口径、版本差异与已知局限,供读者自行判断。
版本基线说明:本次对照对象为 MiniExcel 1.45.0(1.x 系列最新正式版),是经过广泛生产验证的稳定版本,作为 Acl.Excel 性能对比的基线参照。
核心结论速览
- 同基准全面领先(百万行全量读写):在 MiniExcel 官方 benchmark 的同一套方法论、同一份数据、同一运行环境下,Acl.Excel 在 100 万行 × 10 列的全量读取(Query)与写出(Create)两个对等维度上全面领先 MiniExcel 1.45.0:读取快约 1.88×、写出快约 2.84×,托管堆分配低至 1/5.8(读取) 与 1/37.5(写出)。
QueryFirst(取首行)持平。- 诚实声明发布状态:Acl.Excel 目前为内部控制库、未发布 NuGet(核心功能已完成,当前以文档收尾为主);MiniExcel 1.45.0 为经过广泛生产验证的稳定 NuGet 包。两者功能完成度均达交付标准,但"发布渠道"不同,请读者结合项目实际选型。
- 结论可复现:测试代码逐字取自 MiniExcel 官方 benchmark 仓库、仅追加 Acl 对比项,可克隆复现;所有数字基于 net10.0 / Release / BenchmarkDotNet 0.15.8 的单一轮次实测中位数,库间相对顺序稳定。
一、测试背景
Acl.Excel 是一个面向 .NET 的轻量级 Excel 读写库,设计目标是高吞吐、低内存占用、零原生依赖。在 .NET 生态里,Excel 处理库的性能(尤其是大文件读写)一直是选型的核心考量;而 MiniExcel 作为社区最流行的轻量 Excel 库之一,其官方 benchmark 长期以"极低内存"与"流式读写"为卖点,是天然的对照基准。
长期以来,这类库的性能排序多由作者自述或零散的 Stopwatch 脚本得出,缺乏在同一套方法论、同一份数据、同一运行环境下的并排实测。本文采用 MiniExcel 官方 benchmark 的方法论,把 Acl.Excel 直接接入官方基准框架,让两者在完全对等的条件下同台对比,避免"自说自话"。
二、测试目的
- 量化差距:在可复现的条件下,客观测量 Acl.Excel 与 MiniExcel 1.x 在读取、写入两个维度上的吞吐(耗时)与托管堆分配(内存)差距。
- 方法论透明:测试代码逐字复制自 MiniExcel 官方仓库、测试数据为官方提供的标准数据集,仅追加 Acl.Excel 对比项(唯一一处配置改动是在排序器层面将 Acl 与 MiniExcel 置顶,详见第五节)——确保结论可审计、可复现,而非营销话术。
- 如实标注局限:明确指标口径(BDN 托管分配 vs 进程 RSS)、数据规模、库版本状态与已知异常,让读者能基于完整信息自行判断,不夸大、不隐瞒。
2.1 Acl.Excel 自身的测试体系(质量保障背景)
性能对比之外,Acl.Excel 并非"只跑得快的裸库",它建立在一套相当完整的自动化测试体系之上,这也是本文性能数据可信的前提:
- 规模:单元测试与集成测试合计 1830+ 项。最近一次全量回归在
.NET 8.0与.NET 10.0两个目标框架上各 1830 项全部通过,双 TFM 合计 3660 次执行全绿,无失败、无跳过。 - 覆盖维度:
- 读写主路径(类型化
Query<T>、对象化Query、流式OpenDataReader、多 Sheet、SST / inline 双字符串模式); - 底层压缩(
LibDeflateCompressor/LibDeflateDecompressor的 SIMD 批量拷贝、边界守卫、fuzz 鲁棒性); - 安全护栏:DoS 回归(共享字符串条目上限、Sheet 字节上限、展开 XML 字符数上限的拒绝逻辑);
- 跨库互操作:以 Acl.Excel 产物供 NPOI / ClosedXML / EPPlus / MiniExcel / OpenXML SDK 读取,反之亦然,验证
[Content_Types].xml、空sharedStrings命名空间等细节兼容性; - 边界与异常(空文件、损坏文件、超大单元格、特殊字符)。
- 读写主路径(类型化
- 质量门禁:任何改动须先跑全量回归;涉及性能 / 内存的代码还需以
-c Release基准复测,并守护"性能 / 内存至少不变或更优"的硬约束,Debug 数字不得与 Release 基线跨界比较。
这套测试基底让 Acl.Excel 的性能优势建立在"不被回归吞掉"的可持续工程之上,而非一次性 benchmark 调优。
三、测试环境与方法
| 项目 | 配置 |
|---|---|
| 运行时 | .NET 10.0(net10.0) |
| 构建配置 | Release(-c Release,关闭 JIT 优化干扰、启用优化) |
| 基准框架 | BenchmarkDotNet 0.15.8(默认 Job:自动预热 + 多次实测迭代,取中位数) |
| 测试机 | Intel(R) Core(TM) Ultra 5 225H(14 核 / 14 线程),32 GB RAM,x64 架构 |
| 指标 | Mean(平均耗时,毫秒)、Allocated(托管堆累计分配,MB,来自 MemoryDiagnoser) |
重要指标口径说明(务必先看):
- 本文
Allocated是 BenchmarkDotNet 统计的托管堆累计分配(managed bytes allocated),不是进程常驻内存。 - MiniExcel 官方 README 常标注的"最大内存耗用"是进程峰值工作集(RSS),二者绝对数值不可直接比较;只有相对排名、倍数关系具有可比性。
- 所有数字为单一基准轮次的实测中位数,非长期均值;不同机器绝对值会有浮动,但库间相对顺序稳定。
3.1 参与对比的库、版本与许可
下表列出本次基准涉及的全部 .NET 库及其精确版本(取自基准工程的 *.csproj,全部以 NuGet 包方式引用,Acl.Excel 为本地项目引用):
| 库(NuGet 包 ID) | 版本 | 在基准中的角色 | 许可 | 说明 |
|---|---|---|---|---|
| Acl.Excel | 当前 Git 快照(核心功能已完成,当前以文档收尾为主) | 实测对照对象 | 内部控制库 | 本地项目引用,与 MiniExcel 1.45.0 同 -c Release 构建对比 |
| MiniExcel | 1.45.0 | 实测对照对象(稳定版) | MIT | NuGet MiniExcel |
| ExcelDataReader | 3.8.0 | 环境引用(未纳入逐项实测) | MIT | NuGet ExcelDataReader |
| ClosedXML | 0.105.0 | 环境引用(未纳入逐项实测) | MIT | NuGet ClosedXML |
| EPPlus | 7.7.3 | 环境引用(未纳入逐项实测) | Polyform Noncommercial(非商业用途免费,商业需授权) | NuGet EPPlus |
| NPOI | 2.8.0 | 环境引用(未纳入逐项实测) | Apache-2.0 | NuGet NPOI |
| DocumentFormat.OpenXml | 3.5.1 | 环境引用(未纳入逐项实测) | MIT | NuGet DocumentFormat.OpenXml |
| BenchmarkDotNet | 0.15.8 | 基准框架(非被测对象) | MIT | NuGet BenchmarkDotNet |
版本一致性说明:
- 本次实测对照仅涉及 Acl.Excel 与 MiniExcel 1.45.0 两项;上述其余库(ExcelDataReader / ClosedXML / EPPlus / NPOI / OpenXmlSDK)作为基准工程的环境依赖被一并引用,但未纳入逐项实测对照——对比范围收窄的原因见 3.2 节。
- EPPlus 自 v5 起采用 Polyform Noncommercial 许可,商业项目需另行获取授权;其余库均为宽松许可(MIT / Apache-2.0),可自由用于商业与开源场景。选型时除性能外,也应把许可约束纳入考量。
3.2 对比范围与选型依据(为何仅对比 MiniExcel 1.45.0)
本次评测的逐项实测仅覆盖 Acl.Excel 与 MiniExcel 1.45.0 两项,不纳入 ClosedXML、NPOI、EPPlus、ExcelDataReader、DocumentFormat.OpenXml 等库的逐项基准。这一范围收窄基于以下考量,供读者理解其合理性:
- 同类设计取向,最具可比性:Acl.Excel 与 MiniExcel 同属".NET 轻量级、低分配、流式/惰性"的 Excel 处理思路,在 API 形态、内存策略与典型使用场景上最为接近,两者对比最能反映"同类方案"的真实差距。
- 其余库普遍不在同一性能量级:在 MiniExcel 官方基准与社区广泛评测中,ClosedXML / NPOI / EPPlus / ExcelDataReader / OpenXmlSDK 在读写耗时与托管内存分配上普遍明显高于 MiniExcel 1.45.0,差距可达数倍乃至数十倍;其性能区间与 Acl.Excel / MiniExcel 这类低分配设计不在同一量级。
- 聚焦核心结论、避免稀释:将基准时间耗费在明显非同量级的对照上,既拉长采集周期,也容易稀释"Acl vs MiniExcel"这一核心结论的清晰度。因此本次主动收敛对比范围,使基准更聚焦、结论更可读。
需说明:上述"其余库性能更低"的判断源自公开基准与社区评测的既有结论,并非本文逐项实测;若读者需要完整的多库横评,可参考 MiniExcel 官方 benchmark 仓库的对照数据。本文聚焦于 Acl.Excel 与 MiniExcel 这一对最具可比性的组合。
四、测试数据来源(来自 MiniExcel 官方)
| 数据集 | 规模 | 来源 | 说明 |
|---|---|---|---|
Test1,000,000x10.xlsx |
100 万行 × 10 列 | MiniExcel 1.x maintenance benchmark | inline 字符串、无 sharedStrings.xml;第 1 行即数据,无表头 |
该文件由 MiniExcel 官方仓库提供,未做任何裁剪或改写。
五、测试代码来源(逐字复制官方,仅加 Acl 对比项)
- 官方
benchmarks/MiniExcel.Benchmarks/下的.cs文件(含QueryXlsxBenchmark/CreateXlsxBenchmark/BenchmarkBase/Config/Utils等)逐字复制,仅调整命名空间段(如.MiniExcel1x)以避免类型冲突。 - 我们只追加 Acl.Excel 的对比方法(
Acl_Query/Acl_QueryFirst/Acl_CreateXlsx)。 - 唯一一处对官方配置的改动:在
Config.cs中引入自定义CoreFirstOrderer(实现 BenchmarkDotNetIOrderer接口),将 Acl 与 MiniExcel 的对照项在基准执行顺序中置顶(排序 key:Acl*→0、MiniExcel*→1、其余第三方库→2),使 Acl 与 MiniExcel 的对照项最先产出结果。本次评测对比范围收敛为 Acl.Excel 与 MiniExcel 1.45.0 两项,其余第三方库基准不参与本次运行(原因见 3.2 节)。除此之外,官方基准逻辑(数据读取、计时、诊断器)逐字未动。 - MiniExcel 以 NuGet 包方式引用:
MiniExcel 1.45.0(已发布稳定版)。
复现方式:测试代码基于 MiniExcel 官方 benchmarks 仓库改造,Acl.Excel 侧为内部项目引用;复现步骤为克隆 MiniExcel benchmark → 接入 Acl.Excel 对照分支 → dotnet run -c Release -f net10.0。Acl.Excel 对照分支/仓库地址:[待补充,内部]。
六、测试结果
6.1 MiniExcel 1.45.0 对照(100 万行 × 10 列,net10.0 / Release)
本节呈现 Acl.Excel 与 MiniExcel 1.45.0 在 100 万行 × 10 列规模下的逐项对照(读取 Query / QueryFirst、写入 Create)。对比范围与选型依据见 3.2 节;指标口径见第三节"重要指标口径说明"。
下表为本次实测结果(net10.0 / Release / BenchmarkDotNet 0.15.8,均值 Mean)。Acl.Excel 读取使用
ReadStrategy.SlidingWindow(4MB 有界窗口),与 MiniExcel 的惰性流式查询问属流式/有界内存设计,为对等对比。Acl 同时提供ReadStrategy.Fast全量读入模式(本表不涉及),速度更快(~956 ms)但 RSS 与文件大小正相关,适合内存充裕场景。
| 操作 | 库 | Mean | Allocated(托管,单次操作) | Gen0(每 1000 次操作) |
|---|---|---|---|---|
| Query(全量遍历) | Acl.Excel | 1,894.52 ms | 1,312 MB | 146,167 |
| Query(全量遍历) | MiniExcel 1.45.0 | 3,559.38 ms | 7,616 MB | 848,667 |
| QueryFirst(取首行) | Acl.Excel | 81.44 µs | 0.09 MB | 9.93 |
| QueryFirst(取首行) | MiniExcel 1.45.0 | 84.95 µs | 54.4 KB | 5.86 |
| Create(写出 100 万行) | Acl.Excel | 842.58 ms | 107 MB | 12,500 |
| Create(写出 100 万行) | MiniExcel 1.45.0 | 2,390.50 ms | 4,010 MB | 446,667 |
单项提示:上表中 QueryFirst(取首行)一项 Acl.Excel 与 MiniExcel 1.45.0 耗时已基本持平(81 µs vs 85 µs),内存分配 Acl 略高(0.09 MB vs 54 KB)。Acl 的 QueryFirst 经 WithMaxRows(1) 路径触发真流式(XmlReaderSheetScanner),首行读取后即早停。
(建议配图:读取/写出耗时与托管内存的对比柱状图;耗时建议用对数轴以便同时看清 2× 与 37× 的量级差异。)
实测数据基于 2026-07-30 基准采集(net10.0 / Release / BenchmarkDotNet 0.15.8,ShortRun Job,100 万行 × 10 列官方 DemoDto 数据,inline 字符串模式)。
6.2 生态环境参考:MiniExcel 官方基准中的多库对照
MiniExcel 官方 README 中常年保留一份跨库性能对比表(运行环境:i7-7700 / .NET Framework 4.8 / BenchmarkDotnet 0.12.1,同样 100 万行 × 10 列数据)。该表与本篇在同一测试方法与同一份数据上不能直接对比(不同硬件、不同运行时),但可作为全生态性能量级的定性参照——帮助读者理解 MiniExcel 与其它主流库的相对位置:
| 库 | Query(100 万行全量遍历) | Query 峰值内存 | QueryFirst(取首行) | Create(创建) |
|---|---|---|---|---|
| Acl.Excel(本机实测,net10.0) | ~1,895 ms | 1,312 MB* | ~81 µs | ~843 ms |
| MiniExcel 1.45.0(本机实测,net10.0) | ~3,559 ms | 7,616 MB* | ~85 µs | ~2,391 ms |
| MiniExcel 官方(i7-7700 / .NET FX 4.8) | ~14,179 ms | 17.3 MB† | ~726 µs | ~11,532 ms‡ |
| ExcelDataReader(同环境) | ~22,565 ms | 17.3 MB† | ~10,664 µs | — |
| EPPlus(同环境) | ~23,647 ms | 1,451 MB† | ~18,198 µs | ~22,510 ms‡ |
| OpenXmlSDK(同环境) | ~52,003 ms | 1,412 MB† | ~52,349 µs | ~42,474 ms‡ |
| ClosedXML(同环境) | ~191,434 ms | 2,184 MB† | ~66,189 µs | ~140,940 ms‡ |
*本机实测列使用 BenchmarkDotNet
Allocated(托管堆累计分配),反映单次操作的 GC 压力总和。†MiniExcel 官方列使用Max Memory Usage(峰值常驻内存),反映单次操作的 RSS 峰值——两种口径不同,不能直接对比绝对值,但各自的跨库相对顺序有参考价值。‡MiniExcel 官方 Create 基准为 1,000 万行"HelloWorld"数据,非 100 万行 DemoDto,仅作数量级参考。
粗略评估:即使是 MiniExcel 官方在其自身的硬件/运行时环境中,与 ClosedXML / OpenXmlSDK / EPPlus 等库也存在数倍到数十倍的性能差距。Acl.Excel 在本机实测中以 1.88× 领先 MiniExcel、并在 MiniExcel 已领先其它库的基础上再进一步——结合 5.8×(Query)与 37.5×(Create)的分配优势,整体性能在 .NET Excel 库中处于第一梯队。
6.3 小结
| 对比维度 | Acl.Excel vs MiniExcel 1.45.0 | 说明 |
|---|---|---|
| Query 速度 | 1.88× 快(1,895 vs 3,559 ms) | 两端流式/有界内存,对等对比 |
| Query 分配 | 5.8× 省(1,312 vs 7,616 MB) | 差异来自零分配字节扫描 vs 通用 XML 解析器 |
| Create 速度 | 2.84× 快(843 vs 2,391 ms) | 输出端差异较小,分配差距更显著 |
| Create 分配 | 37.5× 省(107 vs 4,010 MB) | 最亮眼数字 |
| QueryFirst 耗时 | 持平(81 vs 85 µs) | 旧版本 3.5× 败项已消除 |
| 峰值内存(RSS) | 恒 ≤4MB(SlidingWindow) | MiniExcel 流式也为低内存,此处非压倒性差异 |
| 测试覆盖 | 1,830+ 单测双 TFM 全绿 | 性能有质量门禁保障不退化 |
Acl.Excel 在同量级轻量 Excel 库中展示出了扎实的综合性能领先——速度领先有真实 margin、分配领先幅度巨大、QueryFirst 短板已补齐。但其未发布 NuGet 的状态仍是实际选型中的主要约束条件,请读者结合项目环境和风险偏好自行判断。
6.2 测试局限性与未来补充方向
本次测试受限于时间与资源,目前仅覆盖了单一场景。以下列出已知局限与计划中的补充方向,供读者参考:
当前已覆盖的场景
| 维度 | 本次设置 | 说明 |
|---|---|---|
| 行规模 | 100 万行 | 大文件单次全量读取/写入 |
| 列规模 | 10 列 | 中等宽度 |
| 数据类型 | 纯数值(官方 DemoDto: int/string/double/datetime) | 均匀、无空值 |
| 字符串模式 | inline(值内嵌于单元格 XML) | 无 sharedStrings.xml |
| 执行模式 | 全量遍历(foreach 全部行) | 非流式取前 N 行 / 非 OpenDataReader |
缺失场景(后续可补充)
| # | 场景描述 | 测试目的 | 预期价值 |
|---|---|---|---|
| 1 | 宽表:1,000 行 × 1,000 列 | 测试极宽表格的列处理能力与内存膨胀 | 暴露 DOM 模式下列数的二次方内存增长 |
| 2 | 稀疏表:100 万行 × 10 列,90% 空单元格 | 测试空值处理策略是否智能(是否为 NULL "付费"——分配空字符串或跳过) | 反映各库的稀疏数据友好度 |
| 3 | 复杂类型混合:数值、日期、公式、富文本、Emoji、多语言(CJK/阿拉伯文) | 测试类型系统完整性与特殊字符容错 | 公式/富文本通常触发完全不同的解析路径 |
| 4 | 小文件高频读写:100×50 表格循环 10,000 次 | 测试初始化开销(ZIP 打开/SST 加载/Schema 解析)与 GC 回收频率 | 揭示"冷启动"成本与长尾延迟分布 |
| 5 | 流式 vs 全量对比:MiniExcel 1.x 的 Query<T>(全量惰性)vs Acl 的 OpenDataReader(前向游标流式) |
对比两者在超大数据(千万行级)下的内存曲线差异 | 流式读取的峰值内存应远低于全量物化 |
| 6 | SST 模式文件:使用含 sharedStrings.xml 的 xlsx(SST 条目 > 10 万) |
测试共享字符串表的加载/查找效率 | SST 查找是很多库的已知瓶颈 |
已知口径偏差
- Acl 默认护栏已上调:Acl.Excel 的
MaxExpandedXmlChars(XML 展开字符总数上限,纵深防御解压炸弹)默认值已从 5000 万字符上调至 10 亿字符,足以容纳 100 万行级大表而不误触发XmlException;同时 10 亿字符仍远低于 OOM 量级,可挡住极端解压炸弹(真实炸弹目标是数十 GB 级展开)。本次基准即基于上调后的默认值完成,无需临时放宽。 - Acl 发布状态:Acl.Excel 核心功能已开发完成,当前以文档完善为主;本次对比的是其当前功能快照(未发布 NuGet 系内部署方式,非"未完成")。MiniExcel 1.45.0 是经过大量生产验证的稳定 NuGet 包。两者在"发布渠道"上不同,但功能完成度均已达交付标准。
- 单一机器、单一轮次:未做跨机器复现或长期稳定性趋势统计。BenchmarkDotNet 的多迭代取中位数已降低偶然波动,但不能替代多环境验证。
- 对比范围收窄:本次仅对 Acl.Excel 与 MiniExcel 1.45.0 做逐项实测,未纳入 ClosedXML / NPOI / EPPlus / ExcelDataReader / OpenXmlSDK 的对照(原因见 3.2 节);如需多库横评可参考 MiniExcel 官方 benchmark 的公开数据。
七、技术分析与瓶颈解读
"Acl 更快"是现象,本节尝试解释"为什么"。以下分析基于 Acl.Excel 自身代码的架构理解(我们对 Acl.Excel 的实现有完整掌控);关于 MiniExcel 的部分只陈述基准实测结果,不作源码级解读(详见 7.2),力求有据可查、不臆测。
7.1 Acl.Excel 的关键技术亮点
(1)三种读策略显式选择:Fast(全量) / SlidingWindow(有界) / Standard(流式)
Acl.Excel 提供三种读策略,通过 ReadStrategy 枚举显式控制,用户按场景自定义选择:
| 策略 | 扫描器 | 适用场景 | RSS 峰值 |
|---|---|---|---|
ReadStrategy.Fast(默认) |
ByteSheetScanner(v1,全量读入) |
小文件 / 内存充裕,优先速度 | 与文件大小正相关 |
ReadStrategy.SlidingWindow |
ByteSheetScanner2(v2,4MB 有界窗口) |
大文件 / 内存敏感,RSS 有界 | 恒 ≤4MB |
ReadStrategy.Standard |
XmlReaderSheetScanner(.NET XmlReader 兜底) |
极限兼容性(畸形 XML 回退) | 与文件大小正相关 |
- Fast:全量读入字节数组后一次扫描。速度最优(无窗口管理开销),RSS 与文件解压后大小正相关(100 万行 × 10 列 ≈ 592 MB 展开 → RSS ~53 MB 流式遍历)。
- SlidingWindow:与 Fast 相同框架的字节扫描,但使用 4MB 有界滑动窗口逐段扫描。内存确定性(RSS 恒 ≤4MB),速度略慢(约 1.3–1.5×),适用于百万行级或并发场景。
- Standard:使用 .NET
XmlReader逐节点解析。兼容性最强(能处理某些畸形 XML),但速度较慢(2.13× vs Fast)、内存分配较大(约 2.8 GB vs 1,312 MB)。
本次 100 万行 × 10 列的对比测试使用 ReadStrategy.SlidingWindow(大表路径),与 MiniExcel 在同一对等条件下对比。
注:
QueryFirst/OpenDataReader等限行读取路径不受此策略影响,它们因语义完整性强制使用XmlReaderSheetScanner(真流式,首行读取后即早停),因此即使 Fast 策略下QueryFirst也不会全量读入——这也是本版本QueryFirst耗时降至与 MiniExcel 持平(81 µs)的原因。
(2)自研纯托管 DEFLATE 引擎(LibDeflateCompressor / LibDeflateDecompressor)
Acl.Excel 不使用 .NET 内置的 System.IO.Compression.DeflateStream,而是自研了一套纯 C# 托管 DEFLATE 实现:
- 解压路径:
LibDeflateDecompressor采用 LZ77 反向引用 + Huffman 位级解码,在批量数据拷贝环节使用优先Vector512<byte>(AVX-512,单次 64 字节),退化Vector256<byte>(AVX2,单次 32 字节)的 SIMD 并行拷贝,远超逐字节for循环。 - 压缩路径:
LibDeflateCompressor同样基于 hash-chain 匹配算法,输出格式严格遵循 RFC 1951 DEFLATE 规范(经跨库互操作验证:NPOI / ClosedXML / EPPlus 均可正常解压 Acl 产物)。 - 零原生依赖:整个压缩/解压管线无 P/Invoke、无 C 语言后端、无 unsafe 代码块(仅
ref struct/Unsafe.As<T>等标准高性能手法),可在任何 .NET 运行时(含 AOT 裁剪)上审计与运行。
对比:DeflateStream 是 .NET BCL 的通用托管实现(.NET Core 3.0 起已无 zlib P/Invoke),未针对 xlsx 负载做 SIMD 批量拷贝与缓冲优化;Acl 的自研实现则针对该负载做了向量化拷贝与零分配热路径优化。
(3)自定义字节流式解析,不构建 DOM、不依赖 XmlReader
Acl.Excel 的读取核心 SheetXmlReader 采用自定义的字节流式(byte-streaming)解析:它直接按字节 / 字节段扫描 worksheet 的 XML 文本,以前向游标方式推进,逐行提取单元格数据——既不在内存中构建 DOM 树,也不经由 System.Xml.XmlReader / OpenXmlSdk 的 XML 解析器。
- 逐行推进字节游标,识别
<row>/<c>/<v>等标签边界即就地解析当前行数据,行结束后即可产出(通过yield return或回调),整个文件无需一次性载入内存。 - 共享字符串表(
sharedStrings.xml)按需查找:遇到t="s"单元格时才去 SST 中索引取值,而非预加载全部字符串到字典。
对比:部分库依赖 System.Xml.XmlReader 或 OpenXmlSdk 的 XML 解析管线(仍是逐 token 扫描、需构造元素 / 属性对象),或早期 ClosedXML / NPOI 直接将整个 XML 物化为 DOM 树;100 万行的 XML 节点数以亿计,DOM 模式内存占用可达原始数据数倍,而 XmlReader 模式也有持续的 token / 对象开销。Acl 的字节流式解析跳过了通用 XML 解析器的中间表示层,开销更低。
(4)零分配友好设计(栈上结构体 / stackalloc / ArrayPool)
Acl.Excel 在热路径上大量使用 C# 高性能编程手法减少托管堆分配:
- 栈上分配的结构体变量:行解析过程中的临时状态(当前列索引、单元格类型标志、属性缓存)封装为普通结构体(
struct),分配在栈上而非堆上,随方法返回自动释放,零 GC 压力。 stackalloc/Span<byte>:小规模临时缓冲(如 DEFLATE 的位读取窗口、XML 属性名扫描区)使用栈分配,避免byte[]堆分配。ArrayPool<byte>.Shared:大规模临时缓冲(如 ZIP 解压块、SST 批量读取区)从全局池租借,用完归还,避免每行/每文件都new byte[]导致 Gen0/Gen1 回收频繁。- 表达式树编译 RowWriter:类型化
Query<T>的属性赋值器通过Expression<Func<...>>编译为强类型委托(Action<TModel, string, string, int>),运行时无反射(PropertyInfo.SetValue)、无dynamic分发、无装箱。
这些手法的叠加效果是:在 100 万行量级下,Acl 的 GC 暂停次数和总分配量显著低于依赖常规 OOP 模式的实现。
7.2 关于 MiniExcel 性能的点评说明
本文未阅读 MiniExcel 1.45.0 的源码,对其内部实现机制(如 XML 解析方式、共享字符串处理、对象分配策略等)缺乏了解,因此不对其性能特征与可能瓶颈作任何源码级点评。
我们仅在第六节(6.1)如实呈现两者在同一基准下的实测数据,由读者基于公开可复现的数字自行判断。这种"只看数据、不猜实现"的克制,避免对未充分理解的库做出可能失准的技术断言。
7.3 解压引擎对比:LibDeflate vs DeflateStream
| 维度 | Acl.LibDeflate(自研) | System.DeflateStream(内置) |
|---|---|---|
| 实现语言 | 纯 C# 托管代码 | C# 托管代码(BCL 通用实现) |
| SIMD 利用 | Vector512/Vector256 批量拷贝 |
通用缓冲,无针对 xlsx 的 SIMD 批量拷贝 |
| 原生依赖 | 零(无 P/Invoke、无 dll) | 无(托管实现,但非为本负载优化) |
| 可审计性 | 全部 C# 源码可控 | BCL 源码可读,但非为本负载优化 |
| AOT 兼容 | ✅ 完全兼容 | ✅ 托管实现,兼容(非为本场景优化) |
| 吞吐表现(实测趋势) | 在已测大文件场景吞吐更高 | 通用够用,但非为极致吞吐优化 |
注意:以上对比描述的是实现取向差异(专用向量化优化 vs 通用托管实现),不针对 DeflateStream 在其他负载下的表现做断言;本次大文件场景更有利于暴露 Acl 自研实现的优化收益。
八、客观点评
基于 6.1 实测,Acl.Excel 在全量读取(Query)与写出(Create)两个对等维度上均显著领先 MiniExcel 1.45.0:读取快约 1.88×(1,895 ms vs 3,559 ms)、托管内存低约 5.8×(1,312 MB vs 7,616 MB);写出快约 2.84×(843 ms vs 2,391 ms)、托管内存低约 37.5×(107 MB vs 4,010 MB);GC 压力(Gen0/1000 op)低 1–2 个数量级。
QueryFirst(取首行)双方已持平(81 µs vs 85 µs),不再构成单独的反向劣势。以下要点同样值得读者重视:
- 1.45.0 是生产可用的稳定版,是真正该和 Acl 对比的对象。
- Acl.Excel 核心功能已开发完成,当前以文档收尾为主;本文数字反映其当前功能状态,以 Release + 同条件基准持续守护"性能 / 内存至少不变或更优"。
- 关于流式读取:Acl 原生
OpenDataReader(前向游标、逐单元格GetValue)与 MiniExcel 的惰性Query同属"低内存读取"思路,但 API 形态不同;为对齐官方基准结构,本次主表仅对比Query/QueryFirst/Create三项,DataReader 风格未纳入,未来可作为专题单独成文。 - 性能对比请以同数据规模、同 .NET 版本、同 Release 构建为前提复现,避免跨规模/跨版本直接比较绝对值。
8.1 MiniExcel 1.x 的非性能优势(同样值得重视)
性能不是选型的唯一维度。MiniExcel 1.45.0 作为 .NET Excel 库生态中的老牌项目,在以下方面拥有 Acl.Excel 目前无法比拟的优势:
| 维度 | MiniExcel 1.x | Acl.Excel(当前状态) |
|---|---|---|
| API 成熟度与丰富度 | 支持 Query / Create / Insert / Update / Delete / Template / Map / Dynamic Query 等多种查询模式;支持 CSV / xlsx / xls 多格式统一 API | 核心读写路径完备(Query / Create / OpenDataReader / 多 Sheet 等),聚焦 xlsx 高性能场景;高级查询模式(模板渲染、动态列映射等)暂未纳入首版范围 |
| 生态集成 | 官方提供 EF Core 扩展包(MiniExcel.EntityFrameworkCore)、Dapper 集成示例;可与 ASP.NET Core / MAUI 等主流框架无缝对接 |
暂无官方 ORM / Web 框架集成扩展 |
| 社区支持与文档 | GitHub 5k+ stars,活跃社区贡献者,StackOverflow 大量问答,中文/英文文档齐全 | 内部控制库(核心功能已完成,当前以文档完善为主),文档以代码注释与内部分析为主 |
| 版本稳定性 | 1.45.0 为正式发布版,语义化版本管理,changelog 完整 | 核心功能已完成,当前以文档收尾为主;未发布 NuGet(内部部署方式),版本追踪依赖 Git commit |
| 多格式覆盖 | 同时支持 xlsx / xls / csv 三种格式,API 统一 | 当前聚焦 xlsx(xlsx 读写是核心场景) |
结论:如果你的项目需要一个"开箱即用、文档齐全、遇到问题能在 StackOverflow搜到答案"的 Excel 库,MiniExcel 1.45.0 仍然是更稳妥的选择。Acl.Excel 在已测维度的性能表现更优,但"更快"不等于"更适合你的团队"——选型应综合考量性能、生态、维护成本与团队能力。
九、给读者的使用建议
- 若需生产环境 Excel 处理库:MiniExcel 1.45.0(稳定版) 经过广泛验证,API 成熟、生态完善(EF Core 扩展等),是稳妥选择;Acl.Excel 在已测维度表现更优,建议结合自身数据规模做一轮验证再选型。
- 若你的场景是超大文件(百万行级)读写、对内存敏感、或需要零原生依赖的部署环境:Acl.Excel 的自研 DEFLATE 引擎 + 自定义字节流式解析 + 零分配热路径设计可能带来显著收益,值得做 PoC 验证。
- 性能对比请以同数据规模、同 .NET 版本、同 Release 构建复现。
- 选型不应只看性能数字——API 丰富度、社区支持、文档质量、许可约束同样重要(详见 8.1 节对比表)。
本文为客观评测,聚焦 Acl.Excel 与 MiniExcel 1.45.0 两项对照(对比范围与选型依据见 3.2 节)。第六节 6.1 的实测结果基于 2026-07-30 基准采集(net10.0 / Release)。
客观性说明:本文的"客观"立场基于以下事实约束,而非自诩无偏——① 本文未阅读 MiniExcel 1.45.0 源码,对其内部实现不作源码级点评(详见 7.2);② 对比范围收敛为 Acl.Excel 与 MiniExcel 1.45.0 两项,未纳入其余库的逐项实测(原因见 3.2 节);③ 全部数字基于单一机器、单一轮次的基准采集(net10.0 / Release / BenchmarkDotNet 0.15.8 中位数),未做跨机器复现或长期趋势统计。以上局限已在正文相应章节如实标注,供读者结合完整信息自行判断。

浙公网安备 33010602011771号