从pyzstd迁移至Python内置zstd模块的压缩确定性差异问询
问题描述
我们正将压缩层从pyzstd迁移至Python 3.14引入的内置compression.zstd模块,迁移中发现一处行为差异:
- 使用
pyzstd.compress()调用时,多次执行总能生成完全相同的字节输出; - 使用内置
zstd.compress()的等效调用时,相同输入连续两次调用生成的字节对象大小有时不同。
两次调用间会从磁盘重新加载同一.zst文件实例化ZstdDict,相关测试用例test_compress_after_reloading_dict_from_disk_produces_identical_output有时失败有时通过,具体代码如下:
pyzstd调用代码
pyzstd.compress( data=content, zstd_dict=training_dict.as_digested_dict )
内置zstd调用代码
zstd.compress( data=content, zstd_dict=training_dict.as_digested_dict )
字典加载与测试代码
input = { "test_1": 1283998000000, "test_2": 0, "test_3": "test" } CONTENT = json.dumps(input).encode('utf-8') def _load_dict(path: str) -> zstd.ZstdDict: with open(path, "rb") as f: return zstd.ZstdDict(f.read()) def test_compress_after_reloading_dict_from_disk_produces_identical_output(): training_dict = _load_dict(DICT_PATH) result_1 = zstd.compress(data=CONTENT, zstd_dict=training_dict.as_digested_dict) training_dict = _load_dict(DICT_PATH) result_2 = zstd.compress(data=CONTENT, zstd_dict=training_dict.as_digested_dict) assert result_1 == result_2, ( f"Reloading ZstdDict from disk changed the compressed output.\n" f" Before reload: {len(result_1)} bytes\n" f" After reload: {len(result_2)} bytes" )
原因分析
这个差异的核心原因在于内置zstd.ZstdDict的as_digested_dict属性在每次实例化字典时,可能生成带有不同动态元数据的消化字典,而pyzstd在处理字典时默认做了更严格的一致性保证,具体细节如下:
- 内置
zstd模块的ZstdDict在调用as_digested_dict时,会自动附加一些动态生成的元数据(如内部缓存标识、字典加载时的上下文标记等),这些元数据会被纳入压缩上下文的初始化流程。每次从磁盘重新加载字典并实例化ZstdDict时,这些动态元数据可能存在细微差异,导致压缩上下文的初始化状态不同,最终影响输出字节的大小。 - pyzstd的
compress方法在使用预训练字典时,默认会过滤掉这些非必要的动态元数据,仅保留字典的核心压缩逻辑部分,因此每次调用的压缩上下文完全一致,输出结果也完全相同。
此外,Zstd原生算法在部分优化策略下存在一定的非确定性,但pyzstd内部额外做了一致性兼容处理,而内置模块直接暴露了底层Zstd库的原生行为,未添加该兼容层,这也是导致差异的原因之一。
内容的提问来源于stack exchange,提问作者datahack
相关产品推荐
相关产品推荐

