如何通过多线程等方式加速Python中排列列表的迭代计算?
优化方案:多核并行 + 数学公式简化 + 低效操作移除
核心问题分析
原代码的性能瓶颈集中在三点:
- 单线程遍历大规模排列列表,无法利用多核CPU算力
- 每次循环调用
aa.index(i)做O(n)查找,严重拖慢进度跟踪效率 - 使用
LinearRegression模型拟合一元线性回归,存在不必要的框架开销
优化步骤
1. 用数学公式替代sklearn模型
对于x为0,1,2,...,n-1的一元线性回归,斜率可通过公式直接计算,无需调用sklearn框架,速度提升数倍:
# x的均值和方差为固定值,可预计算 x_mean = (n-1)/2 x_var = (n**2 - 1)/12 # 斜率 = 协方差(x,y) / 方差(x) = sum( (x-x_mean)*(y-y_mean) ) / (n*x_var)
2. 抽离单排列处理函数
将单个排列的计算逻辑封装为独立函数,方便多进程调用:
import numpy as np import pandas as pd from itertools import permutations from multiprocessing import Pool, cpu_count # 预计算x的固定统计量 def precompute_x_stats(n): x_mean = (n - 1) / 2 x_var = (n**2 - 1) / 12 return x_mean, x_var def process_permutation(perm, avg_np, x_mean, x_var): # 按排列索引取数据(用numpy数组提升速度) y = avg_np[perm] # 计算每个变量的斜率 y_mean = y.mean(axis=0) cov = np.sum( (np.arange(len(perm)) - x_mean).reshape(-1,1) * (y - y_mean), axis=0 ) slopes = cov / (len(perm) * x_var) # 生成block_order_id block_order_id = tuple(p + 1 for p in perm) return (block_order_id, slopes[0], slopes[1], slopes[2])
3. 多进程并行处理
利用multiprocessing.Pool调用全部CPU核心,同时用enumerate替代低效的aa.index(i)跟踪进度:
def permuta(avg, seed, thr): kpi = [] n = len(avg) # 预计算x的统计量 x_mean, x_var = precompute_x_stats(n) # 将DataFrame转为numpy数组,减少索引开销 avg_np = avg[['A', 'B', 'C']].values # 生成排列列表 if len(pd.unique(avg.Block)) > 9: rng = np.random.default_rng(seed) perm_base = pd.unique(avg.Block - 1).astype(int) perm = rng.permuted(np.tile(perm_base, thr).reshape(thr, len(perm_base)), axis=1) aa = [tuple(p) for p in perm] # 检查原始序列是否在结果中,不在则补充 original_perm = tuple(np.arange(n)) if original_perm not in aa: aa.append(original_perm) else: aa = list(permutations(avg.index)) n0 = len(aa) print(f"Total permutations to process: {n0}") # 启动多进程池,进程数设为CPU核心数 with Pool(cpu_count()) as pool: # 用imap_unordered异步获取结果,无需等待全部任务完成 results = pool.imap_unordered( lambda p: process_permutation(p, avg_np, x_mean, x_var), aa ) # 跟踪处理进度 for idx, res in enumerate(results, 1): kpi.append(res) if idx % 1000 == 0: print(f" progress: {idx/n0*100:.2f}%") # 转换为最终DataFrame kpi_df = pd.DataFrame(kpi, columns=['Block_ord', 'm_A', 'm_B', 'm_C']) return kpi_df
4. 细节优化补充
- 用numpy数组替代DataFrame索引,避免
df.loc[list(i)]和reset_index的内存拷贝开销 - 生成排列时直接转为
tuple,减少后续类型转换操作 - 使用
imap_unordered替代map,可提前获取已完成的任务结果,提升感知速度
测试代码
# 生成测试数据 avg = {'Block': np.arange(1, 17, 1), 'A': np.random.uniform(0,1,16), 'B': np.random.uniform(0,1,16), 'C': np.random.uniform(0,1,16)} avg = pd.DataFrame(avg) seed = 1234 thr = 100000 # 调用优化后的函数 result = permuta(avg, seed, thr)
性能提升说明
- 多进程利用全部CPU核心,处理速度随核心数线性提升(CPU密集型任务)
- 数学公式替代sklearn模型,单任务计算速度提升5-10倍
- 移除
aa.index(i)的O(n)查找,进度跟踪开销可忽略 - numpy数组替代DataFrame操作,减少框架层的内存和时间损耗
内容的提问来源于stack exchange,提问作者WillyW0nka
相关产品推荐
相关产品推荐

