如何加速MD5全零前缀哈希查找代码?解决速度骤降问题
MD5前缀全零哈希查找的性能优化方案
问题背景
我出于爱好写了一段查找前缀全零MD5哈希的C#代码,运行首秒能算约4亿次MD5哈希,但之后速度骤降到约1000万次/秒。已经试过批量缓冲区复用MD5对象、线程绑定核心这些优化,考虑过ILGPU的GPU加速但觉得太复杂,想找更简单有效的加速方案。
核心优化方案
- 替换随机数生成器:当前用的加密级随机数生成器是核心性能瓶颈,改用Xoshiro256++这类高性能伪随机数生成器,速度比加密级RNG快几十倍,完全满足碰撞查找的随机性需求。
- 改用硬件加速的MD5实现:替换BouncyCastle的
MD5Digest为.NET内置的MD5.Create(),它会自动利用CPU的SIMD/AES-NI硬件加速指令,性能远高于纯软件实现的MD5。 - 减少全局变量竞争:全局计数器
n的多线程修改会触发缓存一致性开销,给每个线程分配独立计数器,定期用Interlocked.Add原子更新全局统计,避免锁冲突。 - 调整批量大小:原批量大小对应的400MB缓冲区远超CPU缓存容量,缩小到100万次/批(对应16MB内存),大幅提升缓存命中率。
- 简化循环逻辑:去掉每次计算后的
md5.Reset(),直接复用内置MD5对象的ComputeHash方法,内部已经做了状态重置的优化。
优化后的代码示例
using System; using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using System.Runtime.InteropServices; public class OptimizedMD5HashFinder { private static readonly int numberOfThreads = Environment.ProcessorCount; private static readonly CancellationTokenSource cts = new CancellationTokenSource(); private const int batchSize = 1000000; // 适配CPU缓存的批量大小 private const int byteArraySize = 16; private static ulong totalHashes = 0; [DllImport("kernel32.dll")] private static extern IntPtr GetCurrentThread(); [DllImport("kernel32.dll", SetLastError = true)] private static extern IntPtr SetThreadAffinityMask(IntPtr hThread, IntPtr dwThreadAffinityMask); public static void Main(string[] args) { Console.WriteLine("启动多线程查找前缀全零的MD5哈希..."); Task[] tasks = new Task[numberOfThreads]; for (int i = 0; i < numberOfThreads; i++) { int coreId = i; tasks[i] = Task.Run(() => FindHash(coreId)); } Task.WaitAll(tasks); Console.WriteLine("所有线程已完成或找到匹配项。"); Console.ReadKey(); } private static void FindHash(int coreId) { PinThreadToCore(coreId); using var md5 = MD5.Create(); // 启用硬件加速的MD5实现 byte[] inputBuffer = new byte[byteArraySize]; // 用线程ID和时间戳初始化伪随机数生成器,避免线程间重复序列 var rng = new Xoshiro256PlusPlus((ulong)coreId, (ulong)DateTime.UtcNow.Ticks); ulong localCount = 0; while (!cts.Token.IsCancellationRequested) { for (int i = 0; i < batchSize; i++) { // 生成伪随机填充输入缓冲区 rng.NextBytes(inputBuffer); // 计算MD5哈希 byte[] hashBytes = md5.ComputeHash(inputBuffer); // 检查前4字节是否全零 if (hashBytes[0] == 0 && hashBytes[1] == 0 && hashBytes[2] == 0 && hashBytes[3] == 0) { Console.WriteLine($"\n线程 {coreId} 找到匹配项!"); Console.WriteLine($"随机字节: {BitConverter.ToString(inputBuffer).Replace("-", "").ToLower()}"); Console.WriteLine($"MD5哈希: {BitConverter.ToString(hashBytes).Replace("-", "").ToLower()}"); cts.Cancel(); return; } localCount++; } // 原子更新全局统计,避免线程竞争 Interlocked.Add(ref totalHashes, (long)batchSize); if (totalHashes % 10000000 == 0) { Console.WriteLine($"已计算 {totalHashes:N0} 次哈希"); } } } private static void PinThreadToCore(int coreId) { IntPtr mask = new IntPtr(1 << coreId); IntPtr thread = GetCurrentThread(); SetThreadAffinityMask(thread, mask); } // 高性能伪随机数生成器 Xoshiro256++ private class Xoshiro256PlusPlus { private ulong s0, s1, s2, s3; public Xoshiro256PlusPlus(ulong seed1, ulong seed2) { s0 = SplitMix64(ref seed1); s1 = SplitMix64(ref seed1); s2 = SplitMix64(ref seed1); s3 = SplitMix64(ref seed2); } private ulong SplitMix64(ref ulong x) { x += 0x9e3779b97f4a7c15; ulong z = x; z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; z = (z ^ (z >> 27)) * 0x94d049bb133111eb; return z ^ (z >> 31); } public ulong Next() { ulong result = RotateLeft(s0 + s3, 23) + s0; ulong t = s1 << 17; s2 ^= s0; s3 ^= s1; s1 ^= s2; s0 ^= s3; s2 ^= t; s3 = RotateLeft(s3, 45); return result; } public void NextBytes(byte[] buffer) { int i = 0; while (i < buffer.Length) { ulong val = Next(); for (int j = 0; j < 8 && i < buffer.Length; j++) { buffer[i++] = (byte)(val >> (j * 8)); } } } private ulong RotateLeft(ulong x, int k) { return (x << k) | (x >> (64 - k)); } } }
内容的提问来源于stack exchange,提问作者user12316363
相关产品推荐
相关产品推荐

