OpenCV 第三方依赖 zlib-ng:IBM Z 上 DFLTCC 硬件压缩加速的完整实践
本文基于 OpenCV 仓库中内置的 zlib-ng 子项目 3rdparty/zlib-ng/arch/s390/README.md 编写,系统讲解 s390x(IBM Z)平台上 DFLTCC(DEFLATE CONVERSION CALL)硬件压缩/解压缩加速的启用方式、运行时行为、限制条件与 hook 宏集成机制,并结合 arch/s390 目录下的真实源码,剖析参数块结构、调优默认值、dfltcc_deflate()/dfltcc_inflate() 两大核心函数的职责划分,以及 DFLTCC 测试用自托管 CI Builder 的搭建流程。读完后你将能够在 IBM z15 及以上机型上正确开启硬件压缩,理解软硬件状态机之间的衔接原理,并掌握相关限制场景下的规避策略。
一、DFLTCC 是什么:硬件加速背景与启用方式
SystemZ(IBM Z)平台从 z15 开始提供名为 "Integrated Accelerator for zEnterprise Data Compression" 的硬件数据压缩能力,其编程接口是一条机器指令——DEFLATE CONVERSION CALL(DFLTCC),定义于 IBM《Principles of Operation》手册第 26 章。zlib-ng 在 arch/s390 目录中提供了对该指令的完整封装,使 deflate 压缩与 inflate 解压都可以交给硬件执行。
根据 README 给出的公开性能数据,启用 DFLTCC 后,压缩速度最高可获得约 110 倍的提升,解压缩速度最高可获得约 15 倍的提升(详见上游 zlib-ng 项目公布的性能基准)。
1.1 构建启用
有两种等价方式启用 DFLTCC 支持:
configure 方式:
./configure --with-dfltcc-deflate --with-dfltcc-inflate
make
CMake 方式:
cmake -DWITH_DFLTCC_DEFLATE=1 -DWITH_DFLTCC_INFLATE=1 .
make
这两组开关对应 OpenCV 仓库中 CMakeLists.txt 中的两个 option,且默认均为 OFF:
option(WITH_DFLTCC_DEFLATE "Build with DFLTCC intrinsics for compression on IBM Z" OFF)
option(WITH_DFLTCC_INFLATE "Build with DFLTCC intrinsics for decompression on IBM Z" OFF)
当开关打开且目标为 s390x 时,构建系统会分别注入 S390_DFLTCC_DEFLATE 与 S390_DFLTCC_INFLATE 编译宏(见 CMakeLists.txt),由这些宏决定 hook 宏是否被替换为真实的 DFLTCC 调用。
1.2 压缩级别策略:默认只加速 level 1
按上述方式构建后,zlib-ng 的运行时策略是:
- 压缩:仅在 level 1 使用硬件,其余所有级别回退软件实现;
- 解压:始终使用硬件。
若希望让 level 1–6 默认都走硬件压缩,可以在构建时向 CFLAGS 追加:
-DDFLTCC_LEVEL_MASK=0x7e
从源码看,DFLTCC_LEVEL_MASK 是一个位掩码,每一位对应一个压缩级别。dfltcc_detail.h 给出了全部构建期可调参数的默认值:
| 宏 | 默认值 | 含义 |
|---|---|---|
DFLTCC_LEVEL_MASK |
0x2 |
默认仅 level 1 使用硬件(0x7e 对应 level 1–6) |
DFLTCC_BLOCK_SIZE |
1048576(1 MiB) |
每压缩约 1 MiB 数据开启一个新块 |
DFLTCC_FIRST_FHT_BLOCK_SIZE |
4096 |
首个"固定 Huffman 表"块的长度 |
DFLTCC_DHT_MIN_SAMPLE_SIZE |
4096 |
动态 Huffman 表生成的最小采样量 |
DFLTCC_RIBM |
0 |
参数块中 Reserved for IBM use 字段的初始值 |
这些值均可在构建前通过 #define 覆盖,因此在不改动代码的情况下即可调整硬件压缩的分块与触发策略。
二、限制条件与不可复现性:必须知道的边界
2.1 压缩结果不可复现
两次输入完全相同的 DFLTCC 压缩调用,不保证产生完全相同的输出。当业务要求可复现的压缩结果(例如归档去重、内容寻址、审计校验)时需注意这一点。zlib-ng 为此提供了专有扩展接口:zng_deflateSetParams 调用中设置 Z_DEFLATE_REPRODUCIBLE 参数,即可针对特定压缩流禁用 DFLTCC,强制该流走软件压缩。
2.2 不支持的 inflate 特性
DFLTCC 并非覆盖 zlib-ng 的全部功能,以下调用不受硬件支持:
inflate(Z_BLOCK)与inflate(Z_TREES)inflateMark()inflatePrime()inflateSyncPoint()
遇到这些调用时,zlib-ng 会切换到软件路径;如果因流状态不允许而无法切换,则会优雅地失败(gracefully fail)而不是产生损坏数据。
三、代码结构:hook 宏如何把 s390 代码嫁接进通用 zlib-ng
所有 SystemZ 专用代码都位于 arch/s390 目录,与 zlib-ng 主代码通过一组 hook 宏 集成:主代码中到处是"若某平台支持则调用平台函数、否则走软件逻辑"的条件宏,启用 DFLTCC 编译后,这些宏被展开为对 dfltcc_* 系列函数的真实调用。
3.1 hook 宏全景
按 README 的梳理,hook 宏可分为以下几组:
窗口(window)管理。DFLTCC 要求窗口 4k 对齐且固定为 64k 大小,这与通用软件实现的窗口管理不同,由 PAD_WINDOW()、WINDOW_PAD_SIZE、HINT_ALIGNED_WINDOW、DEFLATE_ADJUST_WINDOW_SIZE()、INFLATE_ADJUST_WINDOW_SIZE() 一组宏完成对齐与尺寸调整。DEFLATE_ADJUST_WINDOW_SIZE 的具体定义见 dfltcc_deflate.h:
#define DEFLATE_ADJUST_WINDOW_SIZE(n) MAX(n, HB_SIZE)
其中 HB_SIZE 在 dfltcc_common.h 中定义为 1 << 15(32 KiB),即硬件历史缓冲区的最小尺寸。
字典翻译。软硬件窗口格式不一致,deflateSetDictionary()、deflateGetDictionary()、inflateSetDictionary()、inflateGetDictionary() 需要特殊处理,分别由 DEFLATE_SET_DICTIONARY_HOOK()、DEFLATE_GET_DICTIONARY_HOOK()、INFLATE_SET_DICTIONARY_HOOK()、INFLATE_GET_DICTIONARY_HOOK() 触发。前两个宏的展开逻辑可以在 dfltcc_deflate.h 中直接看到:先询问 dfltcc_can_deflate() 该流是否可走硬件,是则调用 dfltcc_deflate_set_dictionary() / dfltcc_deflate_get_dictionary() 做软硬件字典格式互转,否则落回软件实现。
状态重置。deflateResetKeep() 与 inflateResetKeep() 通过 DEFLATE_RESET_KEEP_HOOK() / INFLATE_RESET_KEEP_HOOK() 同步更新 DFLTCC 参数块(对应 dfltcc_deflate.h 中的 dfltcc_reset_deflate_state)。
优雅失败。INFLATE_PRIME_HOOK()、INFLATE_MARK_HOOK()、INFLATE_SYNC_POINT_HOOK() 让上述三个不受支持的调用在 DFLTCC 流上以错误码形式干净地失败。
压缩参数切换。DEFLATE_PARAMS_HOOK() 实现流中途(mid-stream)在硬件/软件压缩之间切换:切换通常需要冲刷当前块,在低内存情况下可能无法完成,因此 deflateParams() 通过 DEFLATE_DONE() hook 检测并妥善处理这类场景(见 dfltcc_deflate.h)。
输出边界计算。硬件与软件的压缩比不同,DEFLATE_BOUND_ADJUST_COMPLEN() 与 DEFLATE_NEED_CONSERVATIVE_BOUND() 使 deflateBound() 对硬件路径返回正确上界。硬件路径的压缩输出上界由 dfltcc_common.h 的 DEFLATE_BOUND_COMPLEN() 按位精确计算:块头 3 位 + HLIT/HDIST/HCODE 计数位 + 码长编码(最多 19 个码长 × 3 位)+ (286+30) 个符号 × 最多 16 位 + EOBS(最多 15 位)+ 填充(最多 7 位)。
压缩/解压主入口与校验和抑制。实际的压缩与解压缩分别由 DEFLATE_HOOK() 与 INFLATE_TYPEDO_HOOK() 完成。由于 DFLTCC 解压时自行管理滑动窗口,updatewindow() 调用被 INFLATE_NEED_UPDATEWINDOW() 宏抑制。又因为 DFLTCC 在压缩的同时顺带计算 CRC-32 与 Adler-32,只要硬件路径生效,软件侧校验和计算就用 DEFLATE_NEED_CHECKSUM() / INFLATE_NEED_CHECKSUM() 宏关闭,避免重复计算(对应 dfltcc_deflate.h 中的 !dfltcc_can_deflate(strm))。
可复现性开关。DEFLATE_CAN_SET_REPRODUCIBLE() 宏判定在流的某个时刻能否切换 Z_DEFLATE_REPRODUCIBLE 设置——压缩开始前总能设置,但 deflate 流中途并不总是允许。
3.2 参数块与状态扩展
DFLTCC 指令接收一个参数块(parameter block)、输入缓冲区、输出缓冲区和窗口。参数块与 zlib 流状态体(stream state)并排存放。struct dfltcc_param_v0 的完整定义见 dfltcc_common.h,关键字段包括:
cf(Continuation Flag):跨调用的续压标志;cvt(Check Value Type):CVT_CRC32/CVT_ADLER32,选择校验和算法(见 dfltcc_detail.h);htt(Huffman-Table Type):固定表/动态表;bcc/bhf/bcf:块收尾控制、块头最终位、块续压标志;sbb(Sub-Byte Boundary):对应软件侧的bi_valid;hl/ho:历史长度/历史偏移,对应软件侧的whave/wnext;cv:校验值(CRC-32 或 Adler-32 累计结果);cdht[288]:压缩的动态 Huffman 表;csb[1152]:Continuation-State Buffer,续压状态缓冲。
此外,硬件流状态在通用 deflate_state/inflate_state 基础上扩展为 arch_deflate_state / arch_inflate_state(见 dfltcc_common.h),其中 arch_deflate_state 携带 level_mask、block_size、block_threshold、dht_threshold 四个字段,恰好对应第一节表格中的调优参数。
功能启用探测方面,dfltcc_detail.h 通过 STFLE 指令读取 CPU 设施位图,检查 facility 151 是否置位,以此判断硬件是否真正支持 DFLTCC——这意味着即使库是按 DFLTCC 构建的,在不支持该指令的 z/Architecture 机器上运行时也会自动退回软件路径。
四、核心状态机:dfltcc_deflate() 与 dfltcc_inflate()
当以 DFLTCC 方式构建时,上述 hook 宏被转换为对 arch/s390/dfltcc_* 文件中函数的调用。这些函数分三类:
- DFLTCC 基础封装——如
dfltcc(),直接包装机器指令; - 软硬件数据格式翻译——如
dfltcc_deflate_set_dictionary(); - 软硬件状态机翻译——如
dfltcc_deflate()与dfltcc_inflate()。
前两类相对简单;由于软硬件两套状态机都存在各种"怪癖"(quirk),第三类相当复杂。
4.1 dfltcc_deflate() 的九项职责
dfltcc_deflate() 由 deflate() 调用,负责:
- 可用性判定:判断当前流能否使用 DFLTCC,不能则返回 0,
deflate()转而去软件压缩函数;否则返回 1; - 块管理与 Huffman 表决策:DFLTCC 只有被软件显式指示才会结束块,且必须决定用固定表还是动态表。由于"查看数据以收集统计"会抵消硬件加速收益,采取的策略是:前
DFLTCC_FIRST_FHT_BLOCK_SIZE(默认 4096)字节放入固定表块,其后每DFLTCC_BLOCK_SIZE(默认 1 MiB)字节放入动态表块; - EOBS(块结束符)写入:参数块的 Block Closing Control 位可指示 DFLTCC 写 EOBS,但要求输入长度非零或 Continuation Flag 已置位——换言之,如果 EOBS 是 DFLTCC 唯一要做的事,它会静默拒绝。因此代码干脆从不使用该位,而是用
soft_bcc变量自行控制何时写 EOBS; - 块后处理触发:根据 flush 模式,块或流结束时
deflate()要做各种附加动作,dfltcc_deflate()通过block_state *result参数把这些"待办"告知deflate(); - 状态字段互转:软件状态字段 ↔ 硬件参数块字段,例如
wrap↔ Check Value Type、bi_valid↔ Sub-Byte Boundary;某些字段(如 Continuation Flag、Continuation State Buffer)不可翻译,必须在多次调用间原样保留在参数块中; - flush 模式与低内存处理:两者深度交织。总原则是:当 Continuation Flag 置位时,软件侧不能做任何事——无论显式调用
send_eobs(),还是隐式地带着某些返回码和*result值回到deflate(); - 流结束处理:新块开始且 flush 模式为
Z_FINISH时,用 Block Header Final 位把该块标记为最终块;但有时需要一个空最终块,而 DFLTCC 和 EOBS 一样会静默拒绝只写空块。为此代码"假装自己不支持 DFLTCC",诱使deflate()调用软件压缩函数去写空最终块,该路径由need_empty_block变量控制; - 错误处理:把 Operation-Ending-Supplemental Code(OESC)转成字符串。此类错误只会由内存损坏之类的异常引起,因此不影响
deflate()的返回码。
在 dfltcc_deflate.c 中可以看到这些调优参数在流初始化时被装入状态:level_mask = DFLTCC_LEVEL_MASK、block_size = DFLTCC_BLOCK_SIZE、block_threshold = DFLTCC_FIRST_FHT_BLOCK_SIZE;need_empty_block、soft_bcc 等局部变量则出现在压缩主循环的块收尾分支中(dfltcc_deflate.c),与上文描述一一对应。
4.2 dfltcc_inflate() 的五项职责
dfltcc_inflate() 由 inflate() 在 TYPEDO 状态(元数据已全部解析、流定位在 deflate 块头的类型位处)调用,负责:
- 降级到软件:当 flush 模式为
Z_BLOCK或Z_TREES时必须回退——DFLTCC 没有"在块边界或树边界停止解压"的能力; - 解压循环管理:通过返回值
DFLTCC_INFLATE_BREAK或DFLTCC_INFLATE_CONTINUE控制inflate()的解压循环是否继续; - 状态字段互转:如
whave↔ History Length、wnext↔ History Offset; - 流结束:通过
last状态字段指示inflate()返回Z_STREAM_END; - 错误处理:与压缩侧一样做 OESC 到字符串的转换,但区别在于解压错误可能由坏输入引起,因此会把
mode字段置为MEM或BAD传播回inflate(),最终体现为标准的错误返回码。
五、测试 DFLTCC:需要真机 z15 与自托管 CI Builder
DFLTCC 指令的复杂性决定了 QEMU TCG 短期内无法模拟它(截至文档撰写时)。要测试 DFLTCC 支持,必须拥有 IBM z15 或更新机型的 VM 或 LPAR。由于 DFLTCC 是非特权指令,无需特殊 VM/LPAR 配置,也无需 root 权限。
zlib-ng 的 CI 使用一台 IBM 提供的 z15 自托管构建机(self-hosted builder)做 DFLTCC 测试。由于 IBM Z 没有官方 GitHub Actions runner,该构建机参考 anup-kodlekere/gaplib 项目搭建,actions-runner 后续升级可能需要同步更新补丁;其中 .NET 版本号补丁被单独拆出,避免补丁频繁变动。
5.1 配置构建机
安装前置依赖:
sudo dnf install podman
添加 actions-runner 服务:
sudo cp self-hosted-builder/actions-runner.service /etc/systemd/system/
sudo systemctl daemon-reload
创建配置文件(需要一个具有 repo 权限范围的 GitHub personal access token):
# 创建 /etc/actions-runner
repo=<owner>/<name>
access_token=<ghp_***>
自动启动:
sudo systemctl enable --now actions-runner
5.2 重建容器
为获取最新 OS 安全更新等,可按下述步骤重建 gaplib-actions-runner podman 容器:
# 停止 actions-runner 服务
sudo systemctl stop actions-runner
# 删除旧容器
sudo podman container rm gaplib-actions-runner
# 删除旧镜像
sudo podman image rm localhost/zlib-ng/actions-runner
# 构建镜像
sudo podman build --squash -f Dockerfile.zlib-ng --tag zlib-ng/actions-runner --build-arg .
# 创建容器
sudo podman create --name=gaplib-actions-runner --env-file=/etc/actions-runner \
--init --interactive --volume=actions-runner-temp:/home/actions-runner zlib-ng/actions-runner
# 启动 actions-runner 服务
sudo systemctl start actions-runner
在 OpenCV 仓库中,构建机的全部资产实际位于 self-hosted-builder 目录,包含 actions-runner.Dockerfile(runner 镜像定义)、actions-runner.service(systemd 服务单元)、runner-global.json(runner 全局配置模板)与 runner-s390x.patch(s390x 平台适配补丁),与 README 中上述步骤一一对应。
六、目录速查:arch/s390 各文件的职责
最后给出一张文件级速查表,方便在 OpenCV 仓库中定位与深入:
| 文件 | 职责 |
|---|---|
| dfltcc_common.h | QAF/参数块结构定义、dfltcc_state 与 arch_*_state 扩展、历史缓冲区与块大小常量、DEFLATE_BOUND_COMPLEN |
| dfltcc_detail.h | 构建期调优默认值、STFLE 设施探测、OESC 错误码转字符串 |
| dfltcc_deflate.c / dfltcc_deflate.h | 硬件压缩主状态机及全部 deflate 侧 hook 宏展开 |
| dfltcc_inflate.c / dfltcc_inflate.h | 硬件解压主状态机及 inflate 侧 hook |
| s390_features.c / s390_features.h | s390 特性探测(当前仅探测向量扩展 has_vx) |
| crc32-vx.c | 基于 s390 向量扩展(Vector Facility)的 CRC-32 实现,独立于 DFLTCC 的另一条 s390 加速路径 |
| Makefile.in | configure 构建入口,编译 s390_features、dfltcc_deflate、dfltcc_inflate、crc32-vx 四个目标,其中 crc32-vx 额外启用 VGFMAFLAG(向量 GFMA)编译标志 |
需要强调:DFLTCC 加速是 zlib-ng 面向 IBM Z 的平台特定优化,OpenCV 在 s390x 平台链接该第三方 zlib 时即可获得;在 x86_64、ARM 等不具备 DFLTCC 指令的平台上,相关 hook 宏不会被展开为硬件调用,zlib-ng 自动运行在纯软件路径上,功能与接口完全不受影响。若你的业务运行在 IBM Z 上且对压缩吞吐敏感,按第一节的开关开启 DFLTCC,并注意第二节所述的不可复现性与 API 限制,即可安全地获得数量级的压缩/解压加速。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00