尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

RetroArch 中 xxHash 快速摘要算法规范全解:XXH32 / XXH64 算法原理与仓库实践

RetroArch 中 xxHash 快速摘要算法规范全解:XXH32 / XXH64 算法原理与仓库实践 RetroArch 中 xxHash 快速摘要算法规范全解XXH32 / XXH64 算法原理与仓库实践【免费下载链接】RetroArchCross-platform, sophisticated frontend for the libretro API. Licensed GPLv3.项目地址: https://gitcode.com/GitHub_Trending/re/RetroArch导读本文以 RetroArch 仓库内置的 xxHash 依赖deps/xxHash/doc为核心完整解读 xxHash 官方算法规范文档所定义的 XXH32 与 XXH64 两种摘要算法从数据结构、素数与轮函数到初始化、条纹stripe处理、收敛、收尾与雪崩avalanche的七个计算步骤再到算法在不同位宽平台上的性能取向与参考实现。读者读完后既能逐行理解规范中的伪代码也能在仓库源码xxhash.c、xxhash.h中找到对应实现并掌握一字节单发one-shot与流式streaming两种调用方式具备将其移植或应用到自研项目中的实战能力。规范文档概览算法定义的权威来源xxHash 的算法规范集中在仓库的 deps/xxHash/doc 目录下。其中doc/README.md 是规范的入口页它明确说明本目录定义了 xxHash 算法本身具体描述见 xxhash_spec.mdxxhash_spec.md 是算法规范的正文即本文讲解的主体。与以速度优化为导向的参考库不同规范文档用伪代码与结构化步骤描述算法应该做什么可作为任何语言实现的行为基准。仓库中的 xxhash.h6075 行与 xxhash.c 则是符合该规范的 C 参考实现此外还有面向 x86/x64 的运行时指令集分发文件 xxh_x86dispatch.c 与 xxh_x86dispatch.h以及用于验证正确性与碰撞特性的 tests 目录。版本说明规范文档标注为 v0.1.12018-10-10且Version changes一节记录了 v0.7.3 的 minor fixes仓库实际携带的 xxHash 实现版本以源码与 CHANGELOG 为准。规范正文描述的是经典 XXH32/XXH64 算法而仓库实现还额外包含 XXH3/XXH128 家族详见后文。算法设计目标与基本约定规范开篇即明确了 xxHash 的定位输入任意长度的消息长度L可以为 0加上一个可选的种子值seed可为 0输出32 位XXH32或 64 位XXH64的指纹fingerprint/摘要digest性质非密码学哈希non-cryptographic。它不追求抵抗有意构造的碰撞也不承诺防止根据预定摘要反推消息这类密码学安全属性它追求的是极致的速度同时满足良好的分散性与随机性可移植性同一变体在任何 CPU / OS 上输出完全相同不受大小端endianness与机器字长影响。规范对操作记号也做了明确约定所有运算按 {32, 64} 位取模进行算术溢出是被预期并允许的。其中表示模加*表示模乘X s表示循环左移rotateX s表示逻辑右移高位补 0xor表示按位异或。这一与机器无关、纯算术定义的设计正是后续各语言移植版能够保证结果一致的原因算法只依赖小端读取约定与固定宽度的模运算见 xxhash_spec.md 的 Operation notations 一节。XXH32 算法七个步骤详解XXH32 面向 32 位机器设计全部使用 32 位算术。算法将输入按16 字节的条纹stripe收集并变换变换结果保存在4 个 32 位累加器accumulator中各累加器可独立并行处理以利用 CPU 的多执行单元。素数与轮函数算法依赖 5 个 32 位素数常量static const u32 PRIME32_1 0x9E3779B1U; // 0b10011110001101110111100110110001 static const u32 PRIME32_2 0x85EBCA77U; // 0b10000101111010111100101001110111 static const u32 PRIME32_3 0xC2B2AE3DU; // 0b11000010101100101010111000111101 static const u32 PRIME32_4 0x27D4EB2FU; // 0b00100111110101001110101100101111 static const u32 PRIME32_5 0x165667B1U; // 0b00010110010101100110011110110001规范特别指出v0.1.1 起新增的说明这些常量是素数且0与1位分布均衡既不过于规则也不过于不对称这一性质有助于提升分散dispersion能力。Step 1. 初始化内部累加器4 个累加器基于可选seed初始化u32 acc1 seed PRIME32_1 PRIME32_2; u32 acc2 seed PRIME32_2; u32 acc3 seed 0; u32 acc4 seed - PRIME32_1;特殊情况输入小于 16 字节时不处理任何条纹、不使用并行累加器改用单个累加器acc seed PRIME32_5并直接跳到 Step 4。Step 2. 处理条纹一个条纹是连续的 16 字节均分为 4 个 4 字节的车道lane第 N 个车道更新第 N 个累加器每个车道按小端读取 32 位值。每对 {车道, 累加器} 的更新称为一轮roundaccN accN (laneN * PRIME32_2); accN accN 13; accN accN * PRIME32_1;所有运算按 2^32 取模。这一轮函数将输入车道的任何一位扩散到输出累加器的多个位上。Step 2 循环直至剩余不足 16 字节为止然后进入 Step 3。Step 3. 累加器收敛4 个车道累加器合并为单个 32 位累加器acc (acc1 1) (acc2 7) (acc3 12) (acc4 18);Step 4. 加入输入长度将输入总长度仅低 32 位加入累加器使长度参与最终混合acc acc (u32)inputLength;Step 5. 消费剩余输入剩余最多 15 字节按如下伪代码消化确保所有输入字节都进入最终混合while (remainingLength 4) { lane read_32bit_little_endian(input_ptr); acc acc lane * PRIME32_3; acc (acc 17) * PRIME32_4; input_ptr 4; remainingLength - 4; } while (remainingLength 1) { lane read_byte(input_ptr); acc acc lane * PRIME32_5; acc (acc 11) * PRIME32_1; input_ptr 1; remainingLength - 1; }Step 6. 最终混合雪崩效应最后的雪崩阶段保证任何输入位都有机会影响输出摘要的任何位得到无偏分布acc acc xor (acc 15); acc acc * PRIME32_2; acc acc xor (acc 13); acc acc * PRIME32_3; acc acc xor (acc 16);Step 7. 输出XXH32()输出一个无符号 32 位值。若系统需要以二进制或十六进制存储/展示结果规范定义的规范格式canonical format与十进制数值一致即按大端最高有效字节在前排列。XXH64 算法结构相同位宽升级XXH64 与 XXH32 的结构高度相似主要差异在于使用 64 位算术对 64 位系统更友好内存搬运更快但依赖 CPU 的 64 位运算能力条纹为 32 字节均分为 4 个 8 字节车道使用 5 个 64 位素数常量static const u64 PRIME64_1 0x9E3779B185EBCA87ULL; static const u64 PRIME64_2 0xC2B2AE3D27D4EB4FULL; static const u64 PRIME64_3 0x165667B19E3779F9ULL; static const u64 PRIME64_4 0x85EBCA77C2B2AE63ULL; static const u64 PRIME64_5 0x27D4EB2F165667C5ULL;对应的步骤为Step 1初始化u64 acc1 seed PRIME64_1 PRIME64_2; u64 acc2 seed PRIME64_2; u64 acc3 seed 0; u64 acc4 seed - PRIME64_1;输入小于 32 字节时同样走简化路径acc seed PRIME64_5后直达 Step 4。Step 2处理条纹每轮round(accN, laneN): accN accN (laneN * PRIME64_2); accN accN 31; return accN * PRIME64_1;**Step 3收敛**比 32 位版本更复杂需先定义mergeAccumulator()mergeAccumulator(acc, accN): acc acc xor round(0, accN); acc acc * PRIME64_1; return acc PRIME64_4;再执行收敛公式acc (acc1 1) (acc2 7) (acc3 12) (acc4 18); acc mergeAccumulator(acc, acc1); acc mergeAccumulator(acc, acc2); acc mergeAccumulator(acc, acc3); acc mergeAccumulator(acc, acc4);Step 4加入长度acc acc inputLength;Step 5消费剩余输入最多 31 字节分三档处理while (remainingLength 8) { lane read_64bit_little_endian(input_ptr); acc acc xor round(0, lane); acc (acc 27) * PRIME64_1; acc acc PRIME64_4; input_ptr 8; remainingLength - 8; } if (remainingLength 4) { lane read_32bit_little_endian(input_ptr); acc acc xor (lane * PRIME64_1); acc (acc 23) * PRIME64_2; acc acc PRIME64_3; input_ptr 4; remainingLength - 4; } while (remainingLength 1) { lane read_byte(input_ptr); acc acc xor (lane * PRIME64_5); acc (acc 11) * PRIME64_1; input_ptr 1; remainingLength - 1; }Step 6雪崩acc acc xor (acc 33); acc acc * PRIME64_2; acc acc xor (acc 29); acc acc * PRIME64_3; acc acc xor (acc 32);Step 7输出XXH64()输出无符号 64 位值规范格式同样按大端排列。性能取向何时用 XXH32何时用 XXH64规范的 Performance considerations 一节给出明确建议这也是工程选型时的重要依据算法实现简单紧凑提供与系统无关的任意长度消息指纹算法支持流式处理分多次喂入输入此时需要一个内部缓冲区保证数据以完整条纹呈现给算法64 位系统上XXH64通常计算更快即使只需要 32 位结果也推荐使用 XXH6432 位系统上情况反转由于 64 位算术的开销XXH64性能下降XXH32反而是更快选择。换句话说选型首先看目标平台的字长其次才是输出位宽需求。从规范到实现仓库源码对照规范描述的是行为xxhash.c 与 xxhash.h 给出了可运行的 C 实现。两者的对应关系可以直接在源码中验证素数常量在源码中以PRIME32_1…PRIME32_5、PRIME64_1…PRIME64_5命名定义数值与规范一致规范中的轮函数、收敛、雪崩步骤分别对应源码中的XXH32_round()、XXH32_mergeRound()、XXH32_avalanche()等内部函数XXH64 同理小端读取约定对应XXH_readLE32()/XXH_readLE64()系列辅助函数并配合XXH_FORCE_MEMORY_ACCESS等宏在便携 memcpy、gcc packed、非对齐直读、字节移位几种读取策略间切换详见 xxhash.h 中的编译期宏文档。仓库中的测试目录也从不同侧面印证了规范的行为tests/bench/hashes.h 将XXH32/XXH64封装为标准基准接口XXH32_wrapper、XXH64_wrapper用于与其他哈希算法横向对比吞吐量tests/collisions/README.md 记录了大规模碰撞测试的结果例如len8时XXH64在 100 Gi 数据量下 0 碰撞该文档注明此长度下 XXH64 是双射tests/collisions/hashes.h 提供了对应的XXH64_wrapper接入碰撞测试框架。需要说明的是碰撞测试与基准数据依赖特定硬件与测试规模具体数值以 tests/collisions/README.md 及基准代码为准不应脱离测试条件外推。实战单发one-shot与流式streamingAPI 用法在 C/C 工程中使用 xxHash 的标准做法如下。单发模式适合一次性哈希整块内存最简单也通常最快#include xxhash.h /* ... */ XXH64_hash_t hash XXH64(buffer, size, seed);其中seed为 64 位种子值可传 0。对应头文件 xxhash.h 中的 Single Shot 小节说明XXH32()、XXH64()、XXH3_64bits()、XXH3_128bits()都是无状态函数直接对连续内存块返回结果。流式模式适用于输入分批到达例如从文件/网络流读取的场景需要显式管理状态#include stdlib.h /* abort() */ #include xxhash.h XXH64_hash_t calcul_hash_streaming(FileHandler fh) { /* 1. 创建哈希状态 */ XXH64_state_t* const state XXH64_createState(); if (stateNULL) abort(); size_t const bufferSize SOME_SIZE; void* const buffer malloc(bufferSize); if (bufferNULL) abort(); /* 2. 用选定的种子初始化状态 */ XXH64_hash_t const seed 0; /* 或任意其他值 */ if (XXH64_reset(state, seed) XXH_ERROR) abort(); /* 3. 任意次数、任意大小地喂入输入数据 */ while ( /* 还有数据 */ ) { size_t const length get_more_data(buffer, bufferSize, fh); if (XXH64_update(state, buffer, length) XXH_ERROR) abort(); /* ... */ } /* 4. 产出最终哈希值 */ XXH64_hash_t const hash XXH64_digest(state); /* 状态可复用此例中直接释放 */ free(buffer); XXH64_freeState(state); return hash; }要点XXH64_createState()/XXH64_freeState()负责动态分配状态XXH64_reset()设定种子XXH64_update()可被多次调用XXH64_digest()产出最终摘要。状态本身也可以静态分配配合XXH_STATIC_LINKING_ONLY宏适合无动态内存的嵌入式环境。常用编译期宏Build Modifiersxxhash.h 的文档部分与仓库 README.md 列举了大量编译期行为开关实际集成时最常用的有宏作用XXH_INLINE_ALL将所有函数内联进xxhash.h小键key哈希提速明显当键长为编译期常量时效果极佳文档称性能提升可达 200% 量级见 README.mdXXH_PRIVATE_API与XXH_INLINE_ALL等效保留用于兼容旧代码XXH_*符号不再导出XXH_NAMESPACE为所有符号添加前缀规避同一份源码被多次包含时的符号冲突客户端仍用原名由头文件自动翻译XXH_FORCE_MEMORY_ACCESS0便携 memcpy默认1gcc packed2非对齐直读非标准但可能更快3字节移位适合不内联 memcpy 的老编译器或无字节交换指令的大端系统XXH_FORCE_ALIGN_CHECK输入对齐时走更快的直读路径对无法高效非对齐访问的架构收益显著在 x86/x64/aarch64 上自动禁用XXH_VECTOR手动选择向量指令集XXH_SCALAR/XXH_SSE2/XXH_AVX2/XXH_AVX512/XXH_NEON/XXH_VSX默认编译期自动选择例如 gcc 下 AVX2 需-mavx2、AVX512 需-mavx512fXXH_NO_STREAM禁用流式 API仅保留单发变体XXH_SIZE_OPT0默认速度优先1配合-Os/-Oz禁用部分提速技巧2代码体积最小化性能可能明显下降XXH_NO_STDLIB禁用stdlib.h主要是malloc/freeXXH*_createState()恒返回 NULL但单发与静态状态流式仍可用适合无动态分配的嵌入式环境XXH_STATIC_LINKING_ONLY暴露内部状态声明以便静态分配因有 ABI 变化风险与动态链接不兼容XXH_NO_XXH3从产物中移除 XXH364/128 位相关符号减小体积XXH_NO_LONG_LONG移除依赖 64 位类型的算法XXH3、XXH64只编译 XXH32适用于无 64 位支持的平台XXH_CPU_LITTLE_ENDIAN显式声明小端1或大端0跳过编译期可化简的运行时字节序探测避免其带来的性能开销XXH_DEBUGLEVEL设为 ≥1 时启用assert()便于调试此外用make构建命令行工具xxhsum时可设置环境变量DISPATCH1使用 xxh_x86dispatch.c在 x86/x64 上按宿主机运行时自动选择 scalar/SSE2/AVX2/AVX512 指令集。扩展阅读仓库中的 XXH3 与 XXH128规范正文只覆盖 XXH32/XXH64但仓库实现xxhash.h还提供更新的XXH3 家族XXH3_64bits()与XXH3_128bits()。按头文件 mainpage 文档的说明XXH3 是现代化 64/128 位哈希函数家族在小数据上尤其受益于 SIMD 与 64 位运算但并不强制要求这些指令集。头文件中给出的参考基准Intel i7-9700K / Ubuntu x64 20.04 / clang v10 -O3显示AVX2 下XXH3_64bits()大块数据吞吐约 59.4 GB/s、SSE2 下约 31.5 GB/sXXH3_128bits()分别约 57.9 GB/s 与 29.6 GB/s更多对比见 README.md 的 Benchmark 表。这些数据用于说明 XXH3 相对经典变体的性能取向具体数值受硬件与编译器影响仅作参考。若项目仅需经典且极端可移植的哈希XXH32/XXH64 已足够若追求更高吞吐并接受对较新硬件的依赖可考虑 XXH3。在 RetroArch 仓库中的定位与调用方式在本仓库中xxHash 以第三方依赖形式位于 deps/xxHash遵循其上游许可库文件xxhash.c/xxhash.h为 BSD 许可命令行工具xxhsum为 GPL 许可详见 deps/xxHash/README.md 的 License 一节。仓库整体RetroArch 本体为 GPLv3使用方需按各自许可要求合规。需要澄清的是本文所讲的 xxHash 属于通用哈希工具与本仓库中其他任何功能模块例如 cheevos 成就系统、ai 等没有依赖关系——deps 目录在仓库中主要作为可复用的第三方库源码存在而非 RetroArch 主程序编译链的强制组成部分。因此在阅读规范与参考实现时可将其视为一份可独立学习、独立移植的算法资源若要在自己的 C/C 项目中复用直接按上文单发/流式示例引入 xxhash.h 并链接 xxhash.c 即可。总结xxHash 是非密码学哈希追求速度与良好分散性跨平台输出一致XXH3216 字节条纹、4×32 位累加器面向 32 位机器XXH6432 字节条纹、4×64 位累加器面向 64 位机器且收敛步骤更复杂两者都遵循初始化 → 条纹轮函数 → 收敛 → 加长度 → 收尾 → 雪崩 → 输出七步流程并以固定素数常量与小端读取保证行为确定性64 位系统优先用 XXH64即使只要 32 位结果32 位系统优先用 XXH32仓库源码 xxhash.c / xxhash.h 是规范的可运行实现配合 tests 目录可验证正确性与碰撞表现更新的 XXH3/XXH128 则代表更高吞吐的演进方向。【免费下载链接】RetroArchCross-platform, sophisticated frontend for the libretro API. Licensed GPLv3.项目地址: https://gitcode.com/GitHub_Trending/re/RetroArch创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表