2-30 倍性能:Acl.Excel 设计哲学与零依赖
2-30 倍性能:Acl.Excel 设计哲学与零依赖
本文力求客观:既呈现 Acl.Excel 的实测数据,也如实标注测试口径、版本差异与已知局限,供读者自行判断。
口径说明:本文性能数据基于 10K 行 × 7 列基准(.NET 8 Release、BenchmarkDotNet 实测);局限:测试规模非百万行级,且未逐项对齐各竞品最新版本。
本系列第 4 篇 / 共 11 篇(Acl.Excel 深度解析系列第 4 篇,技术内幕第 1 篇)
前情回顾:
- 第 1 篇:从零实现 DEFLATE 压缩器:纯 C# 移植版如何在 Excel 场景超越 .NET 内置?
- 第 2 篇:优化纯 C# DEFLATE 压缩器:从 0 到超越 .NET 内置的完整历程
- 第 3 篇:Acl.Excel vs MiniExcel 1.45.0:百万行读写实测,托管内存低至 1/37
系列导航:下一篇:读取引擎:百万行如何恒定内存 →
处理 Excel 文件,.NET 开发者常常陷入两难:选择功能全面的重量级库(ClosedXML、EPPlus),就要承受内存膨胀与性能损耗;选择轻量方案(MiniExcel),又不得不在性能与灵活性之间折中。Acl.Excel 给出了第三条路——从 ZIP 解压、XML 解析、DEFLATE 压缩到类型映射,全链路从零自建:零第三方依赖、纯托管,在 10K 行 × 7 列基准下实现 2-30 倍的性能领先。
核心结论(TL;DR)
- 零第三方依赖:仅依赖 .NET 运行时内置组件(System.IO.Compression、System.Xml),不引入 NPOI、EPPlus、ClosedXML,亦无原生 P/Invoke。
- 2-30 倍性能领先:10K 行 × 7 列基准下,写入快于 MiniExcel 2.1 倍、ClosedXML 13.0 倍、NPOI 13.8 倍;读取快于 MiniExcel 5.6 倍、NPOI 23.1 倍、ClosedXML 32.8 倍。
- 流式优先、恒定内存:单遍流式扫描 + yield return 延迟消费 + ArrayPool 缓冲复用,内存不随数据量线性增长。
- 微内核 4 原语:
IWorkbook仅暴露 3 读 1 写共 4 个原语,扩展能力全部构建于原语之上,核心接口极度稳定。- 双策略正交:读侧(ReadStrategy / DecompressionStrategy)与写侧(CompressionStrategy / SharedStringsMode)独立组合、互不耦合。
一、核心设计目标
Acl.Excel 的设计并非对标某个特定库,而是围绕三个核心约束展开:
零第三方依赖。整个库仅依赖 .NET 运行时内置组件(System.IO.Compression、System.Xml),不引入 NPOI、EPPlus、ClosedXML 或任何原生 P/Invoke。这意味着部署时无需携带额外 DLL,也无需担心平台兼容性。
流式优先,恒定内存。无论是读取百万行还是写入千万行数据,内存占用都应保持在一个可控范围内,不随数据量线性增长。Acl.Excel 通过单遍流式扫描、yield return 延迟消费、ArrayPool 缓冲复用实现这一目标。
全链路性能优化。从 ZIP 层(自研 LibDeflate 压缩/解压引擎)到 XML 层(ByteSheetScanner 字节扫描器、Utf8RawWriter 字节直写)再到映射层(表达式树编译、源生成器),每个环节都经过 BenchmarkDotNet 基准测试验证。
二、微内核架构
Acl.Excel 采用微内核 + 扩展方法的架构设计。IWorkbook 接口仅暴露 4 个:
读取原语(3 个):
Query<TModel>(options)— 泛型查询,返回IEnumerable<TModel>QueryCellsInto(options, buffer)— 原始行读取,零装箱,返回IEnumerable<CellValue[]>OpenDataReader(options)— 前向 DbDataReader,零装箱
写入原语(1 个):
Write<TModel>(data, sheet, builder, append)— 泛型写入
调用层 (Public API)
ExcelFile (静态门面) | IWorkbook (微内核) | FluentQuery (流式查询)
|
┌──────────────────┼──────────────────┐
▼ ▼ ▼
读取引擎 写入引擎 映射引擎
SheetRowScanner XlsxWriter CellSetter
├─ ByteSheetScanner (Fast) CompiledRowMapper
├─ ByteSheetScanner2 (SlidingWindow)
└─ XmlReaderSheetScanner (Standard)
│ │ │
▼ ▼ ▼
CellDecoder Utf8RawWriter CellValue
(零装箱单元格解码) (字节直写) (24B 值类型)
│ │
▼ ▼
LibDeflateDecompressor LibDeflateCompressor
(纯 C# DEFLATE 解压) (纯 C# DEFLATE 压缩)
所有扩展方法(QueryAdaptive、QueryRaw、QueryDynamic、Fill 等)都基于这 4 个原语构建,不侵入核心。这种设计的好处是:核心接口极度稳定,性能优化集中在原语内部,不影响上层 API;扩展方法可以灵活增加而不引入破坏性变更。
三、读取管线:从字节到模型
读取一条数据的完整路径是:
- ZIP 层:
LibDeflateDecompressor(约 .NET DeflateStream 的 2 倍吞吐)直接解压 worksheet XML 条目 - XML 扫描层:
SheetRowScanner按ReadStrategy选择扫描器 ——ByteSheetScanner(全量字节扫描,零分配)、ByteSheetScanner2(4MB 滑动窗口,恒定内存)或XmlReaderSheetScanner(标准 XmlReader 流式) - 单元格解码层:
CellDecoder将 XML 字符串解码为CellValue(24B 值类型结构体,11 种类型判别) - 映射层:
CellSetter<TModel>(表达式树编译或源生成器预编译)将CellValue[]直接赋值到模型属性,消除中间object[]缓冲
四、写入管线:从模型到文件
写入路径同样极致优化:
- 映射层:
CompiledRowMapper将模型属性值编译为CellValue[]写入缓冲区 - XML 写入层:
Utf8RawWriter直接写入 UTF-8 字节,绕过XmlWriter和StreamWriter的多层分配。所有固定 XML 结构预编译为byte[]常量 - 压缩层:
IZipEntrySink按CompressionStrategy分发 —— Throughput(.NET 内置)、Balanced(libdeflate 均衡)、Compression(libdeflate 最高压缩比) - 持久化层:多 Sheet 并行写入(
Parallel.For),先写.tmp文件,成功后原子替换
五、双策略正交控制面
Acl.Excel 的读侧和写侧由两个独立策略控制:
| 维度 | 策略 | 选项 |
|---|---|---|
| 读侧解析 | ReadStrategy |
Standard(XmlReader)/ Fast(ByteSheetScanner v1)/ SlidingWindow(ByteSheetScanner2 v2) |
| 读侧解压 | DecompressionStrategy |
Deflate(.NET 内置)/ LibDeflate(纯托管,默认) |
| 写龧压缩 | CompressionStrategy |
Throughput(默认)/ Balanced / Compression |
| 写龧字符串 | SharedStringsMode |
Inline(默认)/ Shared / Auto(自适应 30% 重复率阈值) |
两组策略正交、互不耦合。读侧选择不影响写侧,反之亦然。这让开发者可以按场景灵活组合:例如,超大文件读取可以选 SlidingWindow + LibDeflate(恒定 4MB RSS),而小文件快速读取可以选 Fast + LibDeflate(无量入内存,极致吞吐)。
六、关键数据:跨库性能对比
在 10K 行 x 7 列的基准测试中(.NET 8 Release,BenchmarkDotNet 实测):
写入:
| 库 | 耗时 | 分配 |
|---|---|---|
| Acl.Excel | 13.35 ms | 462 KB |
| MiniExcel | 27.49 ms (2.1x) | 43,498 KB (94x) |
| ClosedXML | 173.27 ms (13.0x) | 165,895 KB (359x) |
| NPOI | 184.72 ms (13.8x) | 108,165 KB (234x) |
读取:
| 库 | 耗时 | 分配 |
|---|---|---|
| Acl.Excel | 10.84 ms | 2,350 KB |
| MiniExcel | 60.49 ms (5.6x) | 57,788 KB (24.6x) |
| NPOI | 250.91 ms (23.1x) | 111,192 KB (47.3x) |
| ClosedXML | 355.36 ms (32.8x) | 254,542 KB (108x) |
七、适用场景与边哌
Acl.Excel 专为高吞吐、低内存、大批量数据处理场景设计:ETL 管道、报表生成、日志导入导凸、数据迁移。它不适用需要押术单元格级读写、富栛式、图表或公式引擎的场景 — — 这些场景更适合 EPPlus 或 ClosedXML 等内存工作簿库。
免责声明:本文基于 Acl.Excel 4.0.0(2026 年 7 月撰写日期前后实测)在 10K 行 × 7 列基准(.NET 8 Release、BenchmarkDotNet)下的实测数据撰写,测试环境与口径见正文;数据可能随版本演进变化,重要决策请自行复测核验。
客观性说明:本文的结论基于以下事实约束,而非自诩无偏——① 性能数据来自单一基准(10K 行 × 7 列、.NET 8 Release、BenchmarkDotNet 实测),未覆盖百万行级规模;② 竞品对比未逐项对齐各库最新版本,倍率数字以正文表格为准;③ 本文聚焦核心读写 API 与性能维度,富样式、图表、公式引擎不在讨论范围。以上局限已在正文相应位置如实标注,供读者结合完整信息自行判断。
系列导航
Acl.Excel 系列文章(共 11 篇)
序号 文章 重点 ① 从零实现 DEFLATE 压缩器 纯 C# 移植 libdeflate ② 优化 DEFLATE 压缩器 14 轮优化超越 .NET 内置 ③ Acl.Excel vs MiniExcel 实测 百万行读写对比 ④ 概述与设计哲学:2-30 倍性能与零依赖 微内核架构、4 原语、双策略正交(本文) ⑤ 读取引擎:百万行如何恒定内存 四层流式管线、4MB 滑动窗口 ⑥ 零装箱设计:24B CellValue 值类型联合体清零上亿次装箱 ⑦ 写入引擎:字节直写 分配降 63%、并行写入、原子替换 ⑧ DEFLATE 引擎:2916 行移植 纯 C# 移植 libdeflate、14 轮优化 ⑨ 安全设计:8 层防御 解压炸弹/XXE/ZipSlip 全链路护栏 ⑩ 性能基准与跨库对比 读取最高快 32.8 倍、内存 1/359 ⑪ 实战指南:八大场景与避坑 生产落地、调优清单、选型表 建议按 ①→⑪ 顺序阅读,第 ①-③ 篇为 DEFLATE 引擎与性能对比,第 ④-⑪ 篇为 Acl.Excel 技术内幕深度解析。(当前本篇为第 4 篇,已以 粗体 标记。)

浙公网安备 33010602011771号