You need to enable JavaScript to run this app. Python使用Multiprocessing并行for循环填充Numpy数组效率低于串行的问题
优惠活动
大模型
产品
解决方案
定价
更多

Python使用Multiprocessing并行for循环填充Numpy数组效率低于串行的问题

场景描述

此前查阅过不少相关技术帖子,但现有方案均不适配当前场景:尝试基于Python多进程实现for循环填充Numpy数组的并行化,测试代码如下:

import numpy as np
from multiprocessing import Pool
import time


def func1(x, y=1):
    return x**2 + y

def func2(n, parallel=False):
    my_array = np.zeros((n))
    
    # 并行版本逻辑
    if parallel:
        pool = Pool(processes=6)
        for idx, val in enumerate(range(1, n+1)):
            result = pool.apply_async(func1, [val])
            my_array[idx] = result.get()
        pool.close()

    # 串行版本逻辑
    else:
        for i in range(1, n+1):
            my_array[i-1] = func1(i)

    return my_array

def main():
    start = time.time()
    my_array = func2(60000)
    end = time.time()
    
    print(my_array)
    print("Normal time: {}\n".format(end-start))

    start_parallel = time.time()
    my_array_parallelized = func2(60000, parallel=True)
    end_parallel = time.time()

    print(my_array_parallelized)
    print("Time based on multiprocessing: {}".format(end_parallel-start_parallel))


if __name__ == '__main__':
    main()

代码逻辑可正常运行、输出结果正确,但多进程版本耗时远高于原生串行版本,实际运行输出如下:

[2.00000e+00 5.00000e+00 1.00000e+01 ... 3.59976e+09 3.59988e+09
 3.60000e+09]
Normal time: 0.01605963706970215

[2.00000e+00 5.00000e+00 1.00000e+01 ... 3.59976e+09 3.59988e+09
 3.60000e+09]
Time based on multiprocessing: 2.8775112628936768

核心疑问:是否存在更优的异步结果获取方式?当前实现存在什么问题?该需求最高效的实现方式是什么?

现有实现的核心问题
  • 任务粒度过细,多进程开销远高于计算收益:func1单轮计算仅为一次平方加1的算术操作,6万次串行计算总耗时仅16ms,单轮计算耗时不足1微秒;而多进程场景下,单次任务分发、进程间通信、结果回传的开销就达到毫秒级,细粒度任务使用多进程天然会比单进程慢几个数量级。
  • 阻塞式拉取结果完全消解了并行优势:当前代码每提交一个apply_async任务就立刻调用result.get()阻塞等待结果返回,相当于把并行任务强行改成「提交一个、等待返回一个」的串行模式,进程池的6个工作进程根本没有被充分调度,资源完全闲置。
  • 高频小数据序列化放大了额外开销:多进程跨进程传递参数和结果需要经过pickle序列化,每次仅传递单个数值、回传单个数值的操作,累积下来的序列化耗时已经远超过计算本身的耗时。
高效实现方案
  • 优先匹配计算场景选择实现方式:如果是无依赖的数组逐元素运算,优先使用Numpy内置向量化运算,不需要引入多进程,性能会比纯Python for循环高几个数量级。
  • 如果确实需要使用多进程(比如单轮计算逻辑复杂、无法向量化),需要按照规则优化调用方式:
    • 把细碎的单元素计算打包成块批量提交,大幅减少任务分发、序列化的次数
    • 使用进程池自带的批量映射接口替代逐任务提交、逐次等待的写法,让进程池可以自动调度任务并行执行
    • 配合合理的chunksize参数调整分块大小,平衡调度开销和并行度

优化后的并行实现参考:

import numpy as np
from multiprocessing import Pool
import time

def func1(x, y=1):
    return x**2 + y

def func2(n, parallel=False):
    if parallel:
        x_seq = np.arange(1, n+1)
        with Pool() as pool:
            # 批量提交任务,设置分块大小为1000,减少调度次数
            my_array = pool.map(func1, x_seq, chunksize=1000)
        my_array = np.array(my_array)
    else:
        # 串行版本直接用向量化运算,性能远高于Python for循环
        x_seq = np.arange(1, n+1)
        my_array = func1(x_seq)
    return my_array

注意:多进程并行的收益只有在单任务计算耗时足够长(通常单轮计算超过10微秒)的场景下才会体现,对于本次测试的极轻量算术运算,哪怕优化了多进程写法,耗时也不会低于原生Numpy向量化的单进程实现。


内容的提问来源于stack exchange,提问作者Antonio Serrano

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.24 18:57:05