You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多进程计算结果不一致:串行与并行平方和结果差异排查

问题描述

我分别以串行和并行方式运行下述Python脚本,测试目标是通过并行计算不同数组的平方和,得到与串行计算一致的结果。在并行版本中,数组A、B、C被拆分后分配给不同工作进程,D、E为常量不进行拆分,通过语句p = Process(target=square_sum, args=(q,) + minibatch + (D, E))指定各进程的任务。我使用2个处理器进行测试:当A的规模为1e5时,串行与并行结果一致(差值为0.0);但将A的规模扩大至1e6时,两者差值变为-479104.0。请问导致该结果差异的原因是什么?本测试脚本的任务拆分是否合理?

import time
import numpy as np
from multiprocessing import Process, Manager, Lock
A= np.arange(1e6)
B = np.arange(1e5)
C = np.arange(1e5)
D= np.arange(0)
E = np.arange(0)
#############################################################################################
def square_sum_ser(A, B, C, D, E):
    """
    Calculates the sum of squares of a list of numbers.
    """
    result = np.sum([num**2 for num in A], dtype=np.int64) + \
            np.sum([num**2 for num in B], dtype=np.int64) + \
            np.sum([num**2 for num in C], dtype=np.int64) + \
            np.sum([num**2 for num in D], dtype=np.int64) + \
            np.sum([num**2 for num in E], dtype=np.int64)
    return result


t_start = time.time()

final_result_s = square_sum_ser(A, B, C, D, E)
print(final_result_s)

t_end = time.time()
print("serial: ",t_end - t_start)
#############################################################################################
t_start = time.time()

manager = Manager()
lock = Lock()
results = manager.list()

def square_sum(q,mini_A, mini_B, mini_C,mini_D, mini_E):
    """
    Calculates the sum of squares of a list of numbers.
    """
    result=sum([num**2 for num in mini_A])+\
        sum([num**2 for num in mini_B])+\
        sum([num**2 for num in mini_C],)+\
        sum([num**2 for num in mini_D])+ \
            sum([num**2 for num in mini_E])
    with lock:
        q.put(result)



num_processes=2
# Split the arrays into minibatches 
mini_A = np.array_split(A, num_processes)
mini_B =np.array_split(B, num_processes)
mini_C= np.array_split(C, num_processes)


# ...
from multiprocessing import Process, Queue
q = Queue()
processes = []
for i, minibatch in enumerate(zip(mini_A, mini_B, mini_C)):
    p = Process(target=square_sum, args=(q,) + minibatch + (D, E))
    processes.append(p)
    p.start()

for p in processes:
    p.join()

while not q.empty():
    results.append(q.get())


final_result_p = sum(results)
print(final_result_p)
t_end = time.time()
print("parallel: ",t_end - t_start)
#############################################################################################
print("difference: ",final_result_p-final_result_s)
问题分析与解答

一、结果差异的核心原因

  1. 浮点数精度损失与类型不匹配
    串行代码通过np.sum(..., dtype=np.int64)强制使用64位整数求和,避免了精度问题;但并行代码存在两个关键问题:

    • 创建数组时,np.arange(1e6)因参数是浮点数,默认生成float64类型数组,大整数的平方运算会出现浮点数精度丢失(比如999999的平方用float64存储会有微小误差),累加后误差随规模扩大被放大,最终导致结果偏差。
    • 并行代码用Python内置sum()累加浮点数平方,没有像串行代码那样强制转换为64位整数,进一步加剧了精度误差。
  2. 潜在的重复计算风险
    并行代码中每个进程都传入完整的D、E数组并计算其平方和,最终求和时相当于D、E的平方和被重复计算了num_processes次(本次测试中D、E为空数组,暂未影响结果,但后续如果有数据会直接导致结果错误)。

二、任务拆分的合理性判断

不合理之处

  • 冗余拆分低计算量数组:B、C规模仅为1e5,远小于A的1e6,拆分这两个数组并分配给多个进程,只会增加进程间数据传递的开销,没有性能收益。
  • 常量数组重复计算:D、E作为常量数组,无需让每个进程都计算其平方和,重复计算既浪费资源又可能导致结果错误。
  • 类型逻辑不一致:并行代码未对齐串行代码的整数求和逻辑,导致精度差异。

优化方向

  • 统一数据类型:创建数组时指定整数类型,如A = np.arange(10**6, dtype=np.int64),并行计算时改用np.sum(..., dtype=np.int64),与串行逻辑保持一致。
  • 避免重复计算:提前在主进程计算D、E的平方和,最后加到并行结果中,无需分配给子进程。
  • 聚焦核心任务拆分:只拆分计算量最大的数组A,B、C直接在主进程计算,减少不必要的进程通信开销。

内容的提问来源于stack exchange,提问作者seif elfetni

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 23:47:20