摘要
本文是 Acl.Excel 系列的开篇,详细记录了将 C 版 libdeflate 算法移植为纯 C# 的完整过程。我们将 LibDeflateCompressor 与 Native C libdeflate 和 .NET DeflateStream 做了全面三方对比,涵盖 Excel Sheet XML 压缩、随机数据、重复数据等场景。核心结论:C# 移植版内存分配仅为 Native C 的 1/12,在 Optimal 模式下吞吐率是 .NET DeflateStream 的 2 倍以上,且完全零平台依赖。
引言
在 Excel 库的开发过程中,Excel Sheet XML 的压缩性能是影响写入效率的关键瓶颈。我们基于 C 版 libdeflate 实现了一个纯 C# 移植的 DEFLATE 压缩器 LibDeflateCompressor,支持多种压缩策略。本文将其与业界标杆 C 版 libdeflate 和 .NET 内置 DeflateStream 进行全面对比分析。
术语说明
为避免混淆,本文统一使用以下术语:
| 术语 |
含义 |
实现语言 |
| Native C libdeflate |
业界标杆,原生 C 实现的 libdeflate 库 |
C |
| C# LibDeflateCompressor |
我们的纯 C# 移植版,基于 libdeflate 1.25 算法 |
C# |
| .NET DeflateStream |
.NET 内置压缩器,底层封装 zlib(原生 C++) |
API: C#, 核心: C++ |
测试环境
- 测试平台:Windows x64, .NET 10
- 测试数据:
- ExcelSheetXML:模拟真实 Excel sheet XML 结构(含行标签、单元格、共享字符串、数值等典型模式),规模 1K ~ 300K 行
- Random:随机字节数据(1MB, 9MB)
- Repeating:重复字符串数据(1MB, 9MB)
- 测试方法:每种引擎多次迭代取平均值,预热后测量
三方压缩引擎
1. Native C libdeflate
- 业界标杆,原生 C 实现
- Level 1 专注于吞吐率优化
- 内存分配较大(每次调用 ~138MB 分配)
2. C# LibDeflateCompressor(我们的移植版)
- 本次实现,基于 libdeflate 1.25 算法的纯 C# 移植
- 支持三种策略:
- SpeedFirst:吞吐优先,仅 1 候选匹配查找,动态 Huffman 编码
- RatioFirst:压缩比优先,4 候选匹配查找
- Optimal(默认):混合策略,短匹配 4 候选,中匹配 2 候选
- 低内存分配(每次调用 ~3-11MB)
3. .NET DeflateStream
- .NET 内置实现,底层为 zlib(原生 C++)
- Fast 模式:高吞吐率,中等压缩比
- Optimal 模式:高压缩比,低吞吐率
核心场景:Excel Sheet XML 压缩性能
1. Fast 模式对比(吞吐优先)
Excel Sheet XML 压缩场景下的 Fast 模式对比(100K 行,21.1MB 数据):
| 引擎 |
耗时(ms) |
吞吐(MB/s) |
压缩比 |
内存分配 |
相对性能 |
| Native C libdeflate L1 |
27.58 |
765.4 |
7.22 |
45.2MB |
1.00x |
| .NET DeflateStream Fast |
31.68 |
666.4 |
5.21 |
20.0MB |
0.87x |
| C# LibDeflateCompressor (SpeedFirst) |
52.65 |
401.0 |
5.77 |
3.8MB |
0.52x |
关键发现:
✅ C# 移植版 SpeedFirst vs .NET DeflateStream Fast:
- 吞吐率:C# 移植版是 .NET Fast 的 60%(401 vs 666 MB/s)
- 压缩比:C# 移植版更高(5.77 vs 5.21)
- 内存分配:C# 移植版仅为 .NET 的 19%(3.8 vs 20.0 MB)
✅ C# 移植版 SpeedFirst vs Native C libdeflate:
- 吞吐率:C# 移植版是 C 原生的 52%(401 vs 765 MB/s)
- 压缩比:C# 移植版明显更低(5.77 vs 7.22)
2. 全规模 Excel Sheet XML 测试结果
| 数据规模 |
引擎 |
吞吐(MB/s) |
压缩比 |
相对Native C |
| 1K行 (204KB) |
Native C libdeflate L1 |
774.0 |
6.71 |
1.00x |
|
.NET DeflateStream Fast |
726.4 |
4.86 |
0.94x |
|
C# LibDeflateCompressor (SpeedFirst) |
349.0 |
5.46 |
0.45x |
|
C# LibDeflateCompressor (Optimal/L1) |
271.4 |
6.82 |
0.35x |
|
|
|
|
|
| 10K行 (2.0MB) |
Native C libdeflate L1 |
919.0 |
7.03 |
1.00x |
|
.NET DeflateStream Fast |
817.1 |
5.07 |
0.89x |
|
C# LibDeflateCompressor (SpeedFirst) |
426.5 |
5.67 |
0.46x |
|
C# LibDeflateCompressor (Optimal/L1) |
304.5 |
7.14 |
0.33x |
|
|
|
|
|
| 50K行 (10.5MB) |
Native C libdeflate L1 |
798.7 |
7.18 |
1.00x |
|
.NET DeflateStream Fast |
718.4 |
5.19 |
0.90x |
|
C# LibDeflateCompressor (SpeedFirst) |
441.6 |
5.75 |
0.55x |
|
C# LibDeflateCompressor (Optimal/L1) |
321.4 |
7.29 |
0.40x |
|
|
|
|
|
| 100K行 (21.1MB) |
Native C libdeflate L1 |
765.4 |
7.22 |
1.00x |
|
.NET DeflateStream Fast |
666.4 |
5.21 |
0.87x |
|
C# LibDeflateCompressor (SpeedFirst) |
401.0 |
5.77 |
0.52x |
|
C# LibDeflateCompressor (Optimal/L1) |
282.8 |
7.31 |
0.37x |
|
|
|
|
|
| 300K行 (64.7MB) |
Native C libdeflate L1 |
760.4 |
7.36 |
1.00x |
|
.NET DeflateStream Fast |
689.1 |
5.31 |
0.91x |
|
C# LibDeflateCompressor (SpeedFirst) |
399.6 |
5.89 |
0.53x |
|
C# LibDeflateCompressor (Optimal/L1) |
298.9 |
7.47 |
0.39x |
均衡模式(Optimal)性能分析
均衡模式(CompressionLevel.Optimal)是我们的默认策略,在吞吐率和压缩比之间取得平衡。
各 Level 性能对比(100K 行 Excel Sheet XML)
| Level |
引擎 |
吞吐(MB/s) |
压缩比 |
内存分配 |
| Level 1 |
C# LibDeflateCompressor (Optimal/L1) |
282.8 |
7.31 |
3.0MB |
| Level 2 |
C# LibDeflateCompressor (Optimal/L2) |
225.9 |
7.20 |
5.0MB |
| Level 3 |
C# LibDeflateCompressor (Optimal/L3) |
173.5 |
7.41 |
4.9MB |
|
.NET DeflateStream (Optimal) |
132.3 |
8.03 |
10.6MB |
|
Native C libdeflate L1 |
765.4 |
7.22 |
45.2MB |
分析:
✅ 压缩比优势:
- C# 移植版 Optimal/L1 压缩比 7.31,高于 Native C libdeflate L1 (7.22)
- C# 移植版 Optimal/L3 压缩比 7.41,接近 .NET DeflateStream Optimal (8.03)
- 均衡模式压缩比表现优秀
⚠️ 吞吐率差距:
- C# 移植版 Optimal/L1 吞吐率为 Native C libdeflate L1 的 37%
- C# 移植版 Optimal/L1 吞吐率为 .NET DeflateStream Optimal 的 214%(C# 更快)
压缩比模式(SmallestSize)性能分析
压缩比模式(CompressionLevel.SmallestSize)专注于最大化压缩比。
RatioFirst 性能数据
| 数据规模 |
引擎 |
吞吐(MB/s) |
压缩比 |
| 10K行 |
C# LibDeflateCompressor (RatioFirst) |
291.7 |
7.14 |
|
.NET DeflateStream (Optimal) |
170.2 |
7.75 |
|
Native C libdeflate L1 |
919.0 |
7.03 |
|
|
|
|
| 50K行 |
C# LibDeflateCompressor (RatioFirst) |
294.2 |
7.29 |
|
.NET DeflateStream (Optimal) |
128.5 |
7.98 |
|
Native C libdeflate L1 |
798.7 |
7.18 |
|
|
|
|
| 100K行 |
C# LibDeflateCompressor (RatioFirst) |
264.3 |
7.31 |
|
.NET DeflateStream (Optimal) |
132.3 |
8.03 |
|
Native C libdeflate L1 |
765.4 |
7.22 |
|
|
|
|
| 300K行 |
C# LibDeflateCompressor (RatioFirst) |
262.3 |
7.47 |
|
.NET DeflateStream (Optimal) |
121.4 |
8.21 |
|
Native C libdeflate L1 |
760.4 |
7.36 |
分析:
✅ C# 移植版 RatioFirst vs Native C libdeflate L1:
- 压缩比更高(7.31-7.47 vs 7.03-7.36)
- 吞吐率为 C 原生的 34%
- 说明 C# 的 4 候选匹配查找策略有效提升了压缩比
⚠️ C# 移植版 RatioFirst vs .NET DeflateStream Optimal:
- 压缩比低于 .NET Optimal(7.3-7.5 vs 7.8-8.2)
- 吞吐率为 .NET Optimal 的 200%+(C# 更快)
其他数据场景
Random 数据
随机数据不可压缩,所有引擎压缩比接近 1.0:
| 数据规模 |
引擎 |
吞吐(MB/s) |
压缩比 |
| 9MB |
Native C libdeflate L1 |
135.5 |
1.00 |
|
C# LibDeflateCompressor (Optimal/L1) |
147.5 |
1.00 |
|
C# LibDeflateCompressor (SpeedFirst) |
133.5 |
1.00 |
|
.NET DeflateStream Fast |
102.5 |
0.95 |
注: 小数据量下 C# 移植版甚至略快于 C 原生,因为此时不涉及复杂的匹配查找逻辑。
Repeating 数据
重复数据高压缩比场景:
| 数据规模 |
引擎 |
吞吐(MB/s) |
压缩比 |
| 9MB |
Native C libdeflate L1 |
1789.5 |
320.72 |
|
C# LibDeflateCompressor (SpeedFirst) |
1011.8 |
320.74 |
|
C# LibDeflateCompressor (Optimal/L1) |
619.8 |
320.74 |
|
.NET DeflateStream Fast |
4745.5 |
89.70 |
注: .NET DeflateStream Fast 在重复数据场景吞吐量极高,但压缩比低(RLE 优化策略不同)。
性能差距根源分析
C# 移植版 vs Native C 原生的差距(28-52%)
-
运行时开销:
- JIT 编译 vs AOT 编译
- 托管代码的数组边界检查
- 垃圾回收机制
-
SIMD 指令生成:
- C 版可直接使用
_mm_cmpeq_epi8 等 SIMD 指令
- C# Vector 类库需 JIT 生成等效指令,可能存在效率差异
-
内存访问模式:
-
算法实现细节:
- MatchFinder 实现差异
- Huffman 编码构建策略
- 块分割策略
C# 移植版 vs .NET DeflateStream 的对比
-
SpeedFirst vs .NET Fast:
- C# 移植版压缩比更高(5.77 vs 5.21)
- 输出字节更少(节省存储)
- 但吞吐率较低(401 vs 666 MB/s)
-
Optimal vs .NET Optimal:
- C# 移植版吞吐率为 .NET 的 2-2.5 倍
- 压缩比略低(7.31 vs 8.03)
- C# 移植版更适合需要均衡表现的场景
使用建议
场景推荐
| 场景 |
推荐策略 |
理由 |
| Excel Sheet XML 写入 |
CompressionLevel.Optimal |
平衡的压缩比和吞吐率,低内存占用 |
| 高速写入场景 |
CompressionLevel.Fastest |
最高吞吐率,压缩比可接受 |
| 存储空间有限 |
CompressionLevel.SmallestSize |
最高压缩比,节省存储 |
| 内存受限环境 |
CompressionLevel.Optimal |
最低内存分配 |
结论
优势
✅ 跨平台兼容性:纯 C# 实现,无需原生依赖
✅ 低内存占用:内存分配仅为 Native C 的 1/12,.NET 的 1/5
✅ 可定制策略:支持吞吐优先、压缩比优先、均衡三种模式
✅ 高压缩比:Optimal 模式压缩比接近 .NET Optimal,部分场景超越 Native C
✅ 独立可控:不依赖系统 zlib 版本,行为一致可预测
待改进
⚠️ 吞吐率差距:SpeedFirst 模式为 .NET Fast 的 60%,为 Native C 的 52%
⚠️ 算法细节:部分优化策略(如动态 Huffman)仍可改进
适用场景
C# LibDeflateCompressor 特别适合:
- 需要跨平台一致行为的场景
- 内存受限的云原生/边缘计算
- Excel 写入等需要均衡表现的业务场景
- 希望完全掌控压缩逻辑的定制化需求
附录:测试方法说明
数据生成
Excel Sheet XML 数据通过以下结构生成:
<worksheet>
<sheetData>
<row r="1">
<c r="A1" t="sharedStr"><v>0</v></c>
<c r="B1" t="sharedStr"><v>1</v></c>
...
</row>
...
</sheetData>
</worksheet>
基准测试配置
正确性验证
所有压缩结果均通过解压验证,确保与原始数据一致。
测试平台:Windows x64, .NET 10