MinHash算法性能优化.docx MinHash算法性能优化时间2026年3月11日## MinHash算法介绍MinHashMinimum Hashing最小哈希是一种用于快速估算两个集合相似度的算法属于局部敏感哈希LSH技术的一种。### 核心原理MinHash算法的理论基础是Jaccard相似度。Jaccard相似度衡量两个集合A和B相似程度的指标计算方式为它们交集的大小与并集的大小之比。J(A, B) |A ∩ B| / |A ∪ B|其值在0到1之间值越接近1表示越相似。MinHash算法的巧妙之处MinHash发现了一个重要的概率关系——对于两个集合使用一个随机哈希函数对它们的所有元素进行哈希然后取各自最小的哈希值这两个最小哈希值相等的概率恰好等于它们的Jaccard相似度。P(h_min(A) h_min(B)) J(A, B)生成签名为了得到一个更稳定、更准确的估算MinHash会使用多个例如K个不同的哈希函数为每个集合生成K个最小哈希值这K个值就构成了该集合的“MinHash签名”。估算相似度最后通过比较两个集合的MinHash签名计算它们在K个位置上值相等的比例这个比例就是它们Jaccard相似度的近似值。### 算法实现1对于一段文字例如“high performance computing is interesting”通过n-grams我们将它分为[“high performance”“performance computing”“computing is”“is interesting”]在代码中对应变量为shingles2对于每个shingleMinHash算法通过Hash计算字符串对应的哈希值例如Hashshingle_1123452003计算出哈希值后算法通过长度为N的随机数组A和数组B对哈希值即12345200进行N维的随机打散生成signature指纹。这里N对应的即代码中的len_a和len_blen_a与len_b要求长度一致4之后算法会对这N维的signature指纹不断迭代取最小值min函数-- 这部分后面会通过SVE向量化指令进行加速5最终的结果及为Jaccard相似度Python简单实现直接通过datasketch中的MinHash计算from datasketch import MinHashdef compute_minhash_signature(text, num_perm128):“”计算单个文本的 MinHash 签名“”shingles text.lower().split()# 2. 初始化 MinHash 对象m MinHash(num_permnum_perm)# 3. 更新签名# datasketch 会自动处理哈希和取最小值的逻辑for s in shingles:m.update(s.encode(‘utf8’))return m# demodata1 “我爱吃苹果和香蕉”data2 “我爱吃苹果和橘子”# 计算签名sig1 compute_minhash_signature(data1)sig2 compute_minhash_signature(data2)# 估算 Jaccard 相似度similarity sig1.jaccard(sig2)print(f文档相似度估算: {similarity})## 性能优化通过NumPy实现NumPy版本仅使能NumPy SIMD能力相比于纯数组版本有一定性能提升import hashlibimport structimport timefrom typing import Listimport numpy as npdef compute_hash(s: str) - int:“”计算字符串的 64 位哈希值。“”hash_bytes hashlib.sha256(s.encode(‘utf-8’)).digest()return struct.unpack(‘Q’, hash_bytes[:8])[0]def compute_minhash_signature(shingles: List[str], a: List[int], b: List[int]) - List[int]:a_arr np.array(a, dtypenp.uint64)b_arr np.array(b, dtypenp.uint64)U64_MAX np.iinfo(np.uint64).maxsignature np.full(len(a), U64_MAX, dtypenp.uint64)for shingle in shingles:hash_value np.uint64(compute_hash(shingle))raw a_arr * hash_value b_arrcandidate np.where(raw U64_MAX, np.uint64(0), raw)signature np.minimum(signature, candidate)return signature.tolist()def main():# 性能测试配置 NUM_HASHES 1024 # 哈希函数数量NUM_SHINGLES 1000 # 文档 shingle 数量WARMUP_ITER 10 # 预热迭代次数BENCH_ITER 100 # 正式测试迭代次数# 生成测试数据a [1000 i * 137 for i in range(NUM_HASHES)]b [200 i * 251 for i in range(NUM_HASHES)]shingles [fshingle_{i:08d} for i in range(NUM_SHINGLES)]print(“” * 40)print(“Python NumPy MinHash Performance Benchmark”)print(“” * 40)print(“Configuration:”)print(f - Hash functions (len_a): {NUM_HASHES}“)print(f” - Shingles per doc: {NUM_SHINGLES}“)print(f” - Warmup iterations: {WARMUP_ITER}“)print(f” - Benchmark iterations: {BENCH_ITER}“)print()# 预热for _ in range(WARMUP_ITER):_ compute_minhash_signature(shingles, a, b)# 正式测试start time.perf_counter()for _ in range(BENCH_ITER):_ compute_minhash_signature(shingles, a, b)elapsed time.perf_counter() - starttotal_ops NUM_HASHES * NUM_SHINGLES * BENCH_ITERops_per_sec total_ops / elapsedprint(“Results:”)print(f” - Total time: {elapsed * 1000:.3f}ms)print(f - Per iteration: {elapsed * 1000 / BENCH_ITER:.3f}ms)print(f - Total operations: {total_ops} (hash * shingle * iter)“)print(f” - Throughput: {ops_per_sec:.2e} ops/sec)print()# 验证正确性小数据测试test_shingles [“the cat”, “cat sat”, “sat on”, “on the”, “the mat”]test_a [1187, 2143, 3541, 4327, 5003, 6211, 7109, 8293, 9001, 9973]test_b [211, 433, 701, 983, 1291, 1601, 1913, 2221, 2531, 2843]sig compute_minhash_signature(test_shingles, test_a, test_b)print(“Correctness check (small data):”)print(f Signature[0…5]: {sig[:5]})ifname “main”:main()## 性能优化直接通过SVE指令实现通过C的SVE实现SIMD在Python代码中通过load_library加载C优化版本的动态库Python代码实现如下import ctypesimport hashlibimport structimport osfrom pathlib import Pathfrom typing import Listimport numpy as npdef _load_library():“”“加载 C 共享库”“”lib_dir Path(file).parentlib_name “libminhash_sve.so”lib_path lib_dir / lib_nameif not lib_path.exists():raise FileNotFoundError(f共享库 {lib_path} 不存在。请先编译 C 代码:\nf g -O3 -marcharmv8-asve -shared -fPIC f-o {lib_name} minhash_sve.cpp)lib ctypes.CDLL(str(lib_path))# 定义函数签名# minhash_inner_scalarlib.minhash_inner_scalar.argtypes [ctypes.POINTER(ctypes.c_uint64), # actypes.POINTER(ctypes.c_uint64), # bctypes.POINTER(ctypes.c_uint64), # sigctypes.c_size_t, # nctypes.c_uint64, # hash_value]lib.minhash_inner_scalar.restype None# minhash_inner_svelib.minhash_inner_sve.argtypes [ctypes.POINTER(ctypes.c_uint64),ctypes.POINTER(ctypes.c_uint64),ctypes.POINTER(ctypes.c_uint64),ctypes.c_size_t,ctypes.c_uint64,]lib.minhash_inner_sve.restype Nonereturn lib# 全局加载库_lib Nonetry:_lib _load_library()except FileNotFoundError as e:print(f警告: {e})def compute_hash(s: str) - int:“”“计算字符串的 64 位哈希值”“”hash_bytes hashlib.sha256(s.encode(‘utf-8’)).digest()return struct.unpack(‘Q’, hash_bytes[:8])[0]def compute_minhash_signature_sve(shingles: List[str],a: List[int],b: List[int],use_sve: bool True) - List[int]:“”使用 C SVE/标量实现的 MinHash 签名计算“”if _lib is None:raise RuntimeError(“C 共享库未加载”)a_arr np.array(a, dtypenp.uint64)b_arr np.array(b, dtypenp.uint64)signature np.full(len(a), np.iinfo(np.uint64).max, dtypenp.uint64)# 获取 C 指针a_ptr a_arr.ctypes.data_as(ctypes.POINTER(ctypes.c_uint64))b_ptr b_arr.ctypes.data_as(ctypes.POINTER(ctypes.c_uint64))func _lib.minhash_inner_sve if use_sve else _lib.minhash_inner_scalarfor shingle in shingles:hash_value compute_hash(shingle)sig_ptr signature.ctypes.data_as(ctypes.POINTER(ctypes.c_uint64))func(a_ptr, b_ptr, sig_ptr, len(a), hash_value)return signature.tolist()def compute_minhash_signature(shingles: List[str],a: List[int],b: List[int]) - List[int]:“”默认使用 SVE 实现的 MinHash 签名计算“”return compute_minhash_signature_sve(shingles, a, b, use_sveTrue)# 测试代码ifname “main”:import time# 配置NUM_HASHES 1024NUM_SHINGLES 1000WARMUP_ITER 10BENCH_ITER 100a [1000 i * 137 for i in range(NUM_HASHES)]b [200 i * 251 for i in range(NUM_HASHES)]shingles [fshingle_{i:08d} for i in range(NUM_SHINGLES)]print(“” * 50)print(“Python C SVE MinHash Performance Benchmark”)print(“” * 50)print(fConfiguration:“)print(f” - Hash functions: {NUM_HASHES}“)print(f” - Shingles: {NUM_SHINGLES}“)print(f” - Warmup: {WARMUP_ITER}, Benchmark: {BENCH_ITER}“)print()# 预热for _ in range(WARMUP_ITER):_ compute_minhash_signature_sve(shingles, a, b, use_sveTrue)# 测试 SVE 版本start time.perf_counter()for _ in range(BENCH_ITER):sig_sve compute_minhash_signature_sve(shingles, a, b, use_sveTrue)elapsed_sve time.perf_counter() - start# 测试标量版本start time.perf_counter()for _ in range(BENCH_ITER):sig_scalar compute_minhash_signature_sve(shingles, a, b, use_sveFalse)elapsed_scalar time.perf_counter() - starttotal_ops NUM_HASHES * NUM_SHINGLES * BENCH_ITERprint(“Results:”)print(f” C SVE: {elapsed_sve * 1000:.3f}ms f({total_ops / elapsed_sve:.2e} ops/sec)“)print(f” C Scalar: {elapsed_scalar * 1000:.3f}ms f({total_ops / elapsed_scalar:.2e} ops/sec)“)print()# 验证正确性if sig_sve sig_scalar:print(“SVE 和标量版本结果一致”)else:print(“结果不一致”)diff_count sum(1 for x, y in zip(sig_sve, sig_scalar) if x ! y)print(f” 差异位置数: {diff_count}“)# 小数据验证test_shingles [“the cat”, “cat sat”, “sat on”, “on the”, “the mat”]test_a [1187, 2143, 3541, 4327, 5003, 6211, 7109, 8293, 9001, 9973]test_b [211, 433, 701, 983, 1291, 1601, 1913, 2221, 2531, 2843]sig compute_minhash_signature(test_shingles, test_a, test_b)print(f”\nCorrectness check: {sig[:5]})minhash_sve.cpp代码实现如下#include#includeextern “C” {// 标量回退实现void minhash_inner_scalar(const uint64_t* __restrict a,const uint64_t* __restrict b,uint64_t* __restrict sig,size_t n,uint64_t hash_value) {const uint64_t U64_MAX UINT64_MAX;for (size_t j 0; j n; j) {uint64_t raw a[j] * hash_value b[j];uint64_t candidate (raw U64_MAX) ? 0 : raw;if (candidate sig[j]) {sig[j] candidate;}}}// SVE 向量化实现#if defined(__ARM_FEATURE_SVE)#include arm_sve.hvoid minhash_inner_sve(const uint64_t* __restrict a,const uint64_t* __restrict b,uint64_t* __restrict sig,size_t n,uint64_t hash_value) {const svuint64_t v_hash svdup_u64(hash_value);const svuint64_t v_max svdup_u64(UINT64_MAX);const svuint64_t v_zero svdup_u64(0);size_t j 0;svbool_t pg;while (svptest_any(svptrue_b64(), (pg svwhilelt_b64(j, n)))) {// 加载 a[j…], b[j…], sig[j…]svuint64_t va svld1_u64(pg, a j);svuint64_t vb svld1_u64(pg, b j);svuint64_t vsig svld1_u64(pg, sig j);// raw a * hash bsvuint64_t vraw svmul_u64_x(pg, va, v_hash);vraw svadd_u64_x(pg, vraw, vb);// candidate (raw U64_MAX) ? 0 : rawsvbool_t eq_max svcmpeq_u64(pg, vraw, v_max);svuint64_t vcandidate svsel_u64(eq_max, v_zero, vraw);// sig min(sig, candidate)vsig svmin_u64_x(pg, vsig, vcandidate);// 写回svst1_u64(pg, sig j, vsig);j svcntd();}}#else// 无 SVE 支持时SVE 函数退化为标量实现void minhash_inner_sve(const uint64_t* __restrict a,const uint64_t* __restrict b,uint64_t* __restrict sig,size_t n,uint64_t hash_value) {minhash_inner_scalar(a, b, sig, n, hash_value);}#endif} // extern “C”

相关新闻

最新新闻

RabbitMQ集群部署与高可用架构实战指南

RabbitMQ集群部署与高可用架构实战指南

1. RabbitMQ集群部署核心价值解析RabbitMQ作为AMQP协议的标准实现,在分布式系统中承担着消息中转枢纽的关键角色。当单节点处理能力达到瓶颈时,集群部署能够实现:横向扩展吞吐量(实测可提升3-5倍)、消除单点故障&#…

2026/8/4 2:10:13
抖音批量下载终极指南:一键保存创作者全作品,高效管理内容资产

抖音批量下载终极指南:一键保存创作者全作品,高效管理内容资产

抖音批量下载终极指南:一键保存创作者全作品,高效管理内容资产 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and bro…

2026/8/4 2:10:13
Linux内核容器运行时技术深度解析与优化实践

Linux内核容器运行时技术深度解析与优化实践

1. Linux内核容器运行时技术深度解析容器技术已经成为现代云计算和分布式系统的基石,而Linux内核中的容器运行时则是支撑这一切的核心引擎。作为系列文章的第二部分,我们将深入探讨容器运行时在内核层面的实现机制,特别是系统调用拦截、命名空…

2026/8/4 2:10:13
K8s HPA自动扩缩容实战:原理与调优指南

K8s HPA自动扩缩容实战:原理与调优指南

1. K8s HPA功能实战解析:从原理到落地在容器化部署成为主流的今天,Kubernetes(K8s)作为事实上的容器编排标准,其自动扩缩容能力直接影响着线上服务的稳定性与资源利用率。Horizontal Pod Autoscaler(HPA&am…

2026/8/4 2:10:13
CN8050 5A 超大电流同步降压芯片|DFN3×3 多路功率外置软启动大功率锂电电源方案

CN8050 5A 超大电流同步降压芯片|DFN3×3 多路功率外置软启动大功率锂电电源方案

一、产品整体概述 CN8050 是简芯维尔 CHIPNEED 推出峰值电流模同步降压转换器,为同系列旗舰大功率型号,单节 2.5V~6V 锂电池供电环境下可持续输出 5A 电流,峰值限流 6A,带载能力远超 3A 级 CN8035。采用 DFN33-10 大散热 10 引脚封…

2026/8/4 2:10:13
全面战争40K兽人Waaagh机制与F2A战术深度解析

全面战争40K兽人Waaagh机制与F2A战术深度解析

这次我们来看一个名为“俺寻思F2A!全面战争40K新实机给我看Waaagh了!”的项目。从标题来看,这很可能是一个与《全面战争:战锤40K》相关的游戏模组、玩法演示或技术分析项目,核心聚焦于“Waaagh!”这一兽人阵…

2026/8/4 2:05:13