簡體   English   中英

Python 中的多處理:並行化 for 循環以填充 Numpy 數組

[英]Multiprocessing in Python: Parallelize a for loop to fill a Numpy array

我一直在閱讀這樣的主題,但其中任何一個似乎都適合我的情況。 我正在嘗試並行化以下玩具示例,以使用 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))
    
    # Parallelized version:
    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()

    # Not parallelized version:
    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()

基於 Multiprocessing 的代碼中的行似乎可以工作並為您提供正確的結果。 但是,它比非並行版本花費的時間要長得多。 這是兩個版本的output

[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

我的直覺告訴我,它應該是從 pool.apply_async() 捕獲結果的更好方法。 我究竟做錯了什么? 實現這一目標的最有效方法是什么? 謝謝。

創建流程是昂貴的 在我的機器上,創建每個進程至少需要幾百微秒。 此外,多處理模塊在進程之間復制要計算的數據,然后從進程池中收集結果。 這種進程間通信也很慢 問題是您的計算是微不足道的,並且可以非常快速地完成,可能比所有引入的開銷要快得多。 多處理模塊僅在您處理非常小的數據集並執行密集計算(與計算數據量相比)時才有用。

希望在使用Numpy進行數值計算時,有一種簡單而快速的方法可以並行化您的應用程序: Numba JIT 如果您明確使用並行結構( parallel=Trueprange ),Numba 可以並行化代碼。 它使用線程而不是在共享 memory中工作的繁重進程。 Numba can overcome the GIL if your code does not deal with native types and Numpy arrays instead of pure Python dynamic object (lists, big integers, classes, etc.). 這是一個例子:

import numpy as np
import numba as nb
import time

@nb.njit
def func1(x, y=1):
    return x**2 + y

@nb.njit('float64[:](int64)', parallel=True)
def func2(n):
    my_array = np.zeros(n)
    for i in nb.prange(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("Numba time: {}\n".format(end-start))

if __name__ == '__main__':
    main()

因為 Numba 在運行時編譯代碼,所以它能夠將循環完全優化為無操作,在這種情況下,時間接近 0 秒。

這是@thisisalsomypassword 提出的解決方案,它改進了我的初始建議。 也就是說,“在循環內的列表中收集AsyncResult對象,然后在每個結果對象上啟動所有進程后調用AsyncResult.get() ”:

import numpy as np
from multiprocessing import Pool
import time


def func1(x, y=1):
    time.sleep(0.1)
    return x**2 + y

def func2(n, parallel=False):
    my_array = np.zeros((n))
    
    # Parallelized version:
    if parallel:
        pool = Pool(processes=6)
        ####### HERE COMES THE CHANGE #######
        results = [pool.apply_async(func1, [val]) for val in range(1, n+1)]
        for idx, val in enumerate(results):
            my_array[idx] = val.get()
        #######
        pool.close()

    # Not parallelized version:
    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(600)
    end = time.time()
    
    print(my_array)
    print("Normal time: {}\n".format(end-start))

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

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


if __name__ == '__main__':
    main()

現在它起作用了。 多處理大大減少了時間:

Normal time: 60.107836008071
Time based on multiprocessing: 10.049324989318848    

func1中添加了 time.sleep time.sleep(0.1)以消除作為超級瑣碎任務的效果。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM