2021年1月21日 星期四

[python] 平行(Parallel)-使用mutiprocess

平行(Parallel)-使用mutiprocess

這3篇可以一起閱讀

非同步(async)-使用asyncio

若是計算密集的任務, 在處理器中頻繁切換執行續不一定會增加效率, 若能在一個新process啟動直譯器執行任務, 在多核心的情況下, 有機會分配到各核心平行運作

  • Multi-processing (多處理程序/多進程):
    1. 資料在彼此間傳遞變得更加複雜及花時間,因為一個 process 在作業系統的管理下是無法去存取別的 process 的 memory
    2. 適合需要 CPU 密集,像是迴圈計算,即瓶頸在於計算等情況,且有多核可用時,就可以考慮用多進程提高效率
    3. 可多顆CPU運行
  • Multi-threading (多執行緒/多線程):
    1. 資料彼此傳遞簡單,因為多執行緒的 memory 之間是共用的,最直接的辦法就是設置一個全域變數,多個線程共享這個全域變數即可,但也因此要避免會有 Race Condition 問題。
    2. 適合需要 I/O 密集,像是爬蟲需要時間等待 request 回覆
    3. 如果你的程序有大量與數據交互/網絡交互,可以使用多線程,因為程序時間瓶頸不在於GIL而是在I/O,這時多線程的小開銷就比多進程更實用
    4. 如果你的程序有圖形界面GUI,使用多線程,GIL鎖會幫助你讓你的UI線程不會產生死鎖等問題
    5. 同一顆CPU運行, 但Python本身對於執行緒排程進行優化


平行

使用mutiprocess

範例1-一般狀況
import threading
import sys

def foo(filename: str) -> int:
    with open(filename) as f:
        text = f.read()
		
    ct = 0
    for ch in text:
        n = ord(ch.upper()) + 1
        if n == 67:
            ct += 1
    return ct

count = 0
for filename in sys.argv[1:]:
    count += foo(filename)

print(count)


範例2-mutiprocess

import sys
from multiprocessing import Queue, Process

def foo(filename: str, queue: Queue):
    with open(filename) as f:
        text = f.read()

    ct = 0
    for ch in text:
        n = ord(ch.upper()) + 1
        if n == 67:
            ct += 1
    queue.put(ct)

if __name__ == '__main__':
    queue: Queue = Queue()
    ps = [Process(target = foo, args = (filename, queue))
              for filename in sys.argv[1:]]
    for p in ps:
        p.start()
    for p in ps:
        p.join()

    count = 0
    while not queue.empty():
        count += queue.get()
    print(count)

範例3-使用pool

import sys, multiprocessing

def foo(filename: str) -> int:
    with open(filename) as f:
        text = f.read()

    ct = 0
    for ch in text:
        n = ord(ch.upper()) + 1
        if n == 67:
            ct += 1
    return ct

if __name__ == '__main__':
    filenames = sys.argv[1:]
    with multiprocessing.Pool(2) as pool:
        results = [pool.apply_async(foo, (filename,))
                       for filename in filenames]
        count = sum(result.get() for result in results)
        print(count)

mutiprocess最好不要共享狀態, 如果必要時須使用queue lock

範例3-lock

no lock demo

from multiprocessing import Process, Lock

def f(i: int):
    print('hello world', i)
    print('hello world', i + 1)

if __name__ == '__main__':
    for num in range(100):
        Process(target=f, args=(num, )).start()

lock demo

import multiprocessing
from multiprocessing.synchronize import Lock

def f(lock: Lock, i: int):
    with lock:
        print('hello world', i)
        print('hello world', i + 1)


if __name__ == '__main__':
    lock: Lock = multiprocessing.Lock()

    for num in range(100):
        multiprocessing.Process(target=f, args=(lock, num)).start()


Ref:

沒有留言:

張貼留言