python並發性能concurrent.futures
concurrent.futures模塊,可以利用multiprocessing實現真正的平行計算。核心原理是:concurrent.futures會以子進程的形式,平行的運行多個python解釋器,從而令python程序可以利用多核CPU來提升執行速度。由於子進程與主解釋器相分離,所以他們的全局解釋器鎖也是相互獨立的。每個子進程都能夠完整的使用一個CPU內核。
最大公約數
這個函數是一個計算密集型的函數。
# -*- coding:utf-8 -*- # 求最大公約數 def gcd(pair): a, b = pair low = min(a, b) for i inrange(low, 0, -1): if a % i == 0 and b % i == 0: return i numbers = [ (1963309, 2265973), (1879675, 2493670), (2030677, 3814172), (1551645, 2229620), (1988912, 4736670), (2198964, 7876293) ]
不使用多線程/多進程
import time start = time.time() results = list(map(gcd, numbers)) end = time.time()print ‘Took %.3f seconds.‘ % (end - start) Took 2.507 seconds.
消耗時間是:2.507。
多線程ThreadPoolExecutor
import time from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, Executor start = time.time() pool = ThreadPoolExecutor(max_workers=2) results = list(pool.map(gcd, numbers)) end = time.time()print ‘Took %.3f seconds.‘ % (end - start) Took 2.840 seconds.
消耗時間是:2.840。
上面說過gcd是一個計算密集型函數,因為GIL的原因,多線程是無法提升效率的。同時,線程啟動的時候,有一定的開銷,與線程池進行通信,也會有開銷,所以這個程序使用了多線程反而更慢了。
多進程ProcessPoolExecutor
import time from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, Executor start = time.time() pool = ProcessPoolExecutor(max_workers=2) results = list(pool.map(gcd, numbers)) end = time.time() print ‘Took %.3f seconds.‘ % (end - start) Took 1.861 seconds.
消耗時間:1.861。
在兩個CPU核心的機器上運行多進程程序,比其他兩個版本都快。這是因為,ProcessPoolExecutor類會利用multiprocessing模塊所提供的底層機制,完成下列操作:
1)把numbers列表中的每一項輸入數據都傳給map。
2)用pickle模塊對數據進行序列化,將其變成二進制形式。
3)通過本地套接字,將序列化之後的數據從煮解釋器所在的進程,發送到子解釋器所在的進程。
4)在子進程中,用pickle對二進制數據進行反序列化,將其還原成python對象。
5)引入包含gcd函數的python模塊。
6)各個子進程並行的對各自的輸入數據進行計算。
7)對運行的結果進行序列化操作,將其轉變成字節。
8)將這些字節通過socket復制到主進程之中。
9)主進程對這些字節執行反序列化操作,將其還原成python對象。
10)最後,把每個子進程所求出的計算結果合並到一份列表之中,並返回給調用者。
multiprocessing開銷比較大,原因就在於:主進程和子進程之間通信,必須進行序列化和反序列化的操作。
python並發性能concurrent.futures