如何优化SHA256哈希前d个零查找的循环以提升运行速度?
我现在需要通过统计SHA256哈希字符串的前d个零来确定安全等级d,已经用c_hash(mystring,sha256)实现了哈希计算。另外我用generate_name()生成包含3-6个字母的随机名字和4-8个字母的随机姓氏。当前的实现代码如下:
def d_security(d): t0 = time.clock() cnt = 0 while cnt != d: obj = generate_name() hash_obj = c_hash(obj,sha256) if hash_obj[:d] == d*"0": cnt+=1 t1 = time.clock() print(t1-t0) return None
当d=5时,找到符合要求的字符串耗时超过2分钟,请问怎么通过调整循环方式或者其他手段优化来提升运行速度?
几个实用的优化方向,亲测有效
1. 批量生成+批量校验,减少循环冗余开销
咱们现在的代码是生成一个就校验一个,循环次数多了之后,光循环本身的开销都不小。不如一次生成一批随机名字,然后批量计算哈希筛选符合条件的,这样能大幅减少循环的次数,提升整体效率。比如可以这么改:
def d_security(d): import time t0 = time.perf_counter() # 提一嘴:time.clock()在Python3.3+已经被弃用了,用perf_counter计时更准确 target = "0" * d # 提前算好目标字符串,别每次循环都生成 cnt = 0 batch_size = 1000 # 这个值可以根据你机器性能调,比如2000、5000都试试 while cnt < d: # 一次生成一批名字,比单个生成快很多 names = [generate_name() for _ in range(batch_size)] # 逐个校验,找到够数就立刻停 for name in names: hash_obj = c_hash(name, sha256) if hash_obj[:d] == target: cnt += 1 if cnt >= d: break t1 = time.perf_counter() print(t1 - t0) return None
2. 上并行计算,榨干多核CPU的性能
哈希计算是典型的CPU密集型任务,单线程跑完全浪费了多核CPU的资源。咱们可以用Python的concurrent.futures模块开多进程来并行处理生成和校验,速度能翻好几倍:
from concurrent.futures import ProcessPoolExecutor import time # 把校验逻辑抽成单独的函数,方便并行调用 def check_name(name, d, sha256): target = "0" * d hash_obj = c_hash(name, sha256) return hash_obj[:d] == target def d_security(d): t0 = time.perf_counter() cnt = 0 batch_size = 2000 max_workers = 4 # 改成你CPU的核心数,比如8核就填8 with ProcessPoolExecutor(max_workers=max_workers) as executor: while cnt < d: names = [generate_name() for _ in range(batch_size)] # 并行提交所有名字的校验任务 results = executor.map(check_name, names, [d]*batch_size, [sha256]*batch_size) # 统计符合条件的数量 matches = sum(1 for res in results if res) cnt += matches t1 = time.perf_counter() print(t1 - t0) return None
小提醒:如果你的
c_hash是用C扩展实现的(比如调用OpenSSL),多进程比多线程更有效;如果是纯Python写的哈希函数,多线程提升有限,优先选多进程。
3. 优化循环细节,避免无用功
原代码里while cnt != d其实可以改成while cnt < d,逻辑更清晰,也避免一些极端情况(比如cnt不小心超过d的话,循环会卡死)。另外,找到足够数量的匹配项后,一定要立刻停止当前的批量处理,别再继续算剩下的名字,像第一个示例里那样的提前break,能省不少时间。
4. 先优化generate_name的效率
如果你的generate_name本身写得比较慢,那整个流程的瓶颈可能在这里。比如可以检查下:
- 是不是用了低效的随机字符串生成方式?试试用
random.choices来快速生成:import random import string def generate_name(): first_len = random.randint(3,6) last_len = random.randint(4,8) first = ''.join(random.choices(string.ascii_lowercase, k=first_len)) last = ''.join(random.choices(string.ascii_lowercase, k=last_len)) return f"{first} {last}" - 如果不需要大小写混合,固定用小写或者大写,减少字符集的选择范围,也能加快生成速度。
最后一个小细节
别再用time.clock()了,Python3.3之后这个函数就被废弃了,改用time.perf_counter(),它能提供更精确的计时结果,适合这种性能测试场景。
内容的提问来源于stack exchange,提问作者roman asa

