簡體   English   中英

在 function 中執行循環多處理的最快方法?

[英]Fastest way to perform Multiprocessing of a loop in a function?

1.我有一個 function var 我想知道通過利用系統擁有的所有處理器、內核、線程和 RAM memory 的多處理/並行處理,在 function 中快速運行循環的最佳方法。

import numpy
from pysheds.grid import Grid

xs = 82.1206, 72.4542, 65.0431, 83.8056, 35.6744
ys = 25.2111, 17.9458, 13.8844, 10.0833, 24.8306

a = r'/home/test/image1.tif'
b = r'/home/test/image2.tif'

def var(interest):
    
    variable_avg = []
    for (x,y) in zip(xs,ys):
        grid = Grid.from_raster(interest, data_name='map')

        grid.catchment(data='map', x=x, y=y, out_name='catch')

        variable = grid.view('catch', nodata=np.nan)
        variable = numpy.array(variable)
        variablemean = (variable).mean()
        variable_avg.append(variablemean)
    return(variable_avg)

2.如果我可以同時運行 function var並針對 function 的給定多個參數並行循環,那就太好了。 例如:同時var(a)var(b) 因為它消耗的時間比單獨並行循環要少得多。

如果沒有意義,請忽略 2。

TLDR:您可以使用多處理庫並行運行您的var function。 但是,正如所寫的那樣,您可能沒有對var進行足夠的調用以進行多處理,因為它的開銷會帶來性能優勢。 如果您需要做的只是運行這兩個調用,那么串行運行可能是您將獲得的最快速度。 但是,如果您需要撥打很多電話,多處理可以幫助您。

我們需要使用進程池來並行運行它,線程不會在這里工作,因為 Python 的全局解釋器鎖會阻止我們實現真正的並行性。 進程池的缺點是進程是重量級的。 在僅運行兩次調用var的示例中,創建池的時間超過了運行var本身所花費的時間。

為了說明這一點,讓我們使用進程池並使用 asyncio 並行運行對var的調用,並將其與僅按順序運行事物進行比較。 請注意,運行此示例時,我使用了來自 Pysheds 庫https://github.com/mdbartos/pysheds/tree/master/data的圖像 - 如果您的圖像更大,則以下可能不成立。

import functools
import time
from concurrent.futures.process import ProcessPoolExecutor
import asyncio

a = 'diem.tif'
xs = 10, 20, 30, 40, 50
ys = 10, 20, 30, 40, 50

async def main():
    loop = asyncio.get_event_loop()
    pool_start = time.time()
    with ProcessPoolExecutor() as pool:
        task_one = loop.run_in_executor(pool, functools.partial(var, a))
        task_two = loop.run_in_executor(pool, functools.partial(var, a))
        results = await asyncio.gather(task_one, task_two)
        pool_end = time.time()
        print(f'Process pool took {pool_end-pool_start}')

    serial_start = time.time()

    result_one = var(a)
    result_two = var(a)

    serial_end = time.time()
    print(f'Running in serial took {serial_end - serial_start}')


if __name__ == "__main__":
    asyncio.run(main())

在我的機器(2.4 GHz 8 核 Intel Core i9)上運行上述程序,我得到以下 output:

Process pool took 1.7581260204315186
Running in serial took 0.32335805892944336

在此示例中,進程池的速度要慢五倍以上。 這是由於創建和管理多個進程的開銷,也就是說,如果您需要多次調用var ,那么進程池可能更有意義。 讓我們調整它以運行var 100 次並比較結果:

async def main():
    loop = asyncio.get_event_loop()
    pool_start = time.time()
    tasks = []
    with ProcessPoolExecutor() as pool:
        for _ in range(100):
            tasks.append(loop.run_in_executor(pool, functools.partial(var, a)))
        results = await asyncio.gather(*tasks)
        pool_end = time.time()
        print(f'Process pool took {pool_end-pool_start}')

    serial_start = time.time()

    for _ in range(100):
        result = var(a)

    serial_end = time.time()
    print(f'Running in serial took {serial_end - serial_start}')

運行100次,得到如下output:

Process pool took 3.442288875579834
Running in serial took 13.769982099533081

在這種情況下,在進程池中運行大約快 4 倍。 您可能還希望嘗試同時運行循環的每個迭代。 您可以通過創建一次處理一個 x,y 坐標的 function 來執行此操作,然后在進程池中運行您要檢查的每個點:

def process_poi(interest, x, y):
    grid = Grid.from_raster(interest, data_name='map')

    grid.catchment(data='map', x=x, y=y, out_name='catch')

    variable = grid.view('catch', nodata=np.nan)
    variable = np.array(variable)
    return variable.mean()

async def var_loop_async(interest, pool, loop):
    tasks = []
    for (x,y) in zip(xs,ys):
        function_call = functools.partial(process_poi, interest, x, y)
        tasks.append(loop.run_in_executor(pool, function_call))

    return await asyncio.gather(*tasks)

async def main():
    loop = asyncio.get_event_loop()
    pool_start = time.time()
    tasks = []
    with ProcessPoolExecutor() as pool:
        for _ in range(100):
            tasks.append(var_loop_async(a, pool, loop))
        results = await asyncio.gather(*tasks)
        pool_end = time.time()
        print(f'Process pool took {pool_end-pool_start}')

    serial_start = time.time() 

在這種情況下,我得到Process pool took 3.2950568199157715 - 所以並不比我們的第一個版本快,每次調用var一個進程。 這可能是因為此時的限制因素是我們的 CPU 上有多少可用內核,將我們的工作分成更小的增量不會增加太多價值。

也就是說,如果您希望跨兩個圖像檢查 1000 個 x 和 y 坐標,那么最后一種方法可能會產生性能提升。

我認為這是一種通過僅並行化主循環來加速代碼的合理且直接的方法。 你可以用這個來飽和你的核心,所以不需要對interest變量進行並行化。 我無法測試代碼,所以我假設您的 function 是正確的,我剛剛在新的 function 中對loop進行了編碼,並在var()中對其進行了並行化。

from multiprocessing import Pool


def var(interest,xs,ys):
    grid = Grid.from_raster(interest, data_name='map')
    with Pool(4) as p: #uses 4 cores, adjust this as you need
        variable_avg = p.starmap(loop, [(x,y,grid) for x,y in zip(xs,ys)])
    return variable_avg
    

def loop(x, y, grid):
    grid.catchment(data='map', x=x, y=y, out_name='catch')
    variable = grid.view('catch', nodata=np.nan)
    variable = numpy.array(variable)
    return variable.mean()

暫無
暫無

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

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