1. 程式人生 > >python並發性能concurrent.futures

python並發性能concurrent.futures

-1 兩個 size logs ssp int ces 底層 套接字

concurrent.futures模塊,可以利用multiprocessing實現真正的平行計算。核心原理是:concurrent.futures會以子進程的形式,平行的運行多個python解釋器,從而令python程序可以利用多核CPU來提升執行速度。由於子進程與主解釋器相分離,所以他們的全局解釋器鎖也是相互獨立的。每個子進程都能夠完整的使用一個CPU內核。

最大公約數

這個函數是一個計算密集型的函數。

# -*- coding:utf-8 -*-
# 求最大公約數
def gcd(pair):
    a, b = pair
    low = min(a, b)
    for i in
range(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