牌组洗牌与分析的并行化优化方案咨询
提升牌组洗牌与分析流程的运行效率
首先,你的代码已经尝试了ProcessPoolExecutor并行化,但未达到预期效果。下面从瓶颈定位、并行化修复、效率优化三个维度给出具体解决方案:
1. 先定位核心性能瓶颈
并行化仅对CPU/计算密集型任务有效,先明确哪一步最耗时:
在shuffle_and_solve中加入计时代码,拆分各环节耗时:
import time import os def shuffle_and_solve(_): print(f"当前进程ID: {os.getpid()}") # 验证是否多进程运行 start = time.time() cards = [(i, j) for i in '23456789TJQKA' for j in 'CDHS'] shuffle(cards) print(f"洗牌耗时: {time.time() - start:.4f}s") start = time.time() result = prettysolve(cards) print(f"求解+格式化耗时: {time.time() - start:.4f}s") return cards, result
如果solve(即ddstable.get_ddstable)占总耗时90%以上,说明并行化方向正确;若其他步骤占比高,优先优化这些环节。
2. 修复并行化不生效问题
2.1 确认多进程真的在运行
运行上述代码,若输出的进程ID全部相同,说明并行未启动:
- 检查
num_decks是否大于1; - 确保所有进程相关代码被
if __name__ == '__main__':包裹(你的代码已满足)。
2.2 避免子进程重复加载库
每个子进程会重新初始化环境,若ddstable加载成本高,会浪费大量时间。用initializer提前在每个子进程加载一次:
def init_worker(): global solve from ddstable import ddstable solve = ddstable.get_ddstable if __name__ == '__main__': num_decks = 16 # 指定进程数为CPU核心数(默认也是这个值,可手动调整) with concurrent.futures.ProcessPoolExecutor(initializer=init_worker) as executor: futures = [executor.submit(shuffle_and_solve, _) for _ in range(num_decks)] outputs = [future.result() for future in concurrent.futures.as_completed(futures)]
2.3 减少进程间数据传输开销
shuffle_and_solve返回的cards和result需要序列化后传给主进程,若不需要cards可直接省略;若必须保存,尽量精简返回数据的体积。
3. 进一步提升运行效率
3.1 批量处理任务
单任务处理一个牌组的调度、序列化开销相对较高,改为一次处理多个牌组:
def shuffle_and_solve_batch(batch_size): results = [] # 预生成基础牌组,避免重复创建 base_cards = [(i, j) for i in '23456789TJQKA' for j in 'CDHS'] for _ in range(batch_size): cards = base_cards.copy() shuffle(cards) result = prettysolve(cards) results.append((cards, result)) return results if __name__ == '__main__': total_decks = 16 num_workers = os.cpu_count() decks_per_worker = total_decks // num_workers with concurrent.futures.ProcessPoolExecutor(max_workers=num_workers, initializer=init_worker) as executor: futures = [executor.submit(shuffle_and_solve_batch, decks_per_worker) for _ in range(num_workers)] # 处理剩余的牌组 if total_decks % num_workers != 0: futures.append(executor.submit(shuffle_and_solve_batch, total_decks % num_workers)) outputs = [] for future in concurrent.futures.as_completed(futures): outputs.extend(future.result()) sets, results = zip(*outputs)
3.2 优化prettysolve的字符串处理
字符串拼接用列表收集后join比逐个+=更高效,可进一步优化格式化逻辑:
def prettysolve(cards): lines = [" {:>2} {:>2} {:>2} {:>2} {:>2}".format("S", "H", "D", "C", "NT")] all_solves = solve(bytes(PBN(cards), 'utf-8')) for each in all_solves: line_parts = [f"{each:>1}"] for suit in ddstable.dcardSuit: line_parts.append(f"{all_solves[each][suit]-6:2}") lines.append(" ".join(line_parts)) return '\n'.join(lines)
3.3 调整进程数
- 纯CPU密集型任务:设为CPU核心数即可;
- 若
solve涉及IO操作:可设为核心数的2倍,充分利用等待时间。
4. 其他潜在优化方向
- 检查
ddstable是否支持批量求解:若get_ddstable可一次性处理多个牌组的PBN字符串,直接批量调用会比单张处理效率高很多; - 预生成所有基础牌组:将
base_cards移到全局,避免每个任务重复创建; - 替换并行库:若
concurrent.futures仍有问题,可直接用multiprocessing模块,底层控制更精细。
内容的提问来源于stack exchange,提问作者NumberBasher
相关产品推荐
相关产品推荐

