Python 多程序、多執行緒效率對比
阿新 • • 發佈:2020-11-20
Python 界有條不成文的準則: 計算密集型任務適合多程序,IO 密集型任務適合多執行緒。本篇來作個比較。
通常來說多執行緒相對於多程序有優勢,因為建立一個程序開銷比較大,然而因為在 python 中有 GIL 這把大鎖的存在,導致執行計算密集型任務時多執行緒實際只能是單執行緒。而且由於執行緒之間切換的開銷導致多執行緒往往比實際的單執行緒還要慢,所以在 python 中計算密集型任務通常使用多程序,因為各個程序有各自獨立的 GIL,互不干擾。
而在 IO 密集型任務中,CPU 時常處於等待狀態,作業系統需要頻繁與外界環境進行互動,如讀寫檔案,在網路間通訊等。在這期間 GIL 會被釋放,因而就可以使用真正的多執行緒。
以上是理論,下面做一個簡單的模擬測試: 大量計算用 math.sin() + math.cos()
來代替,IO 密集型用 time.sleep()
來模擬。 在 Python 中有多種方式可以實現多程序和多執行緒,這裡一併納入看看是否有效率差異:
- 多程序: joblib.multiprocessing,multiprocessing.Pool,multiprocessing.apply_async,concurrent.futures.ProcessPoolExecutor
- 多執行緒: joblib.threading,threading.Thread,concurrent.futures.ThreadPoolExecutor
from multiprocessing import Pool from threading import Thread from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor import time,os,math from joblib import Parallel,delayed,parallel_backend def f_IO(a): # IO 密集型 time.sleep(5) def f_compute(a): # 計算密集型 for _ in range(int(1e7)): math.sin(40) + math.cos(40) return def normal(sub_f): for i in range(6): sub_f(i) return def joblib_process(sub_f): with parallel_backend("multiprocessing",n_jobs=6): res = Parallel()(delayed(sub_f)(j) for j in range(6)) return def joblib_thread(sub_f): with parallel_backend('threading',n_jobs=6): res = Parallel()(delayed(sub_f)(j) for j in range(6)) return def mp(sub_f): with Pool(processes=6) as p: res = p.map(sub_f,list(range(6))) return def asy(sub_f): with Pool(processes=6) as p: result = [] for j in range(6): a = p.apply_async(sub_f,args=(j,)) result.append(a) res = [j.get() for j in result] def thread(sub_f): threads = [] for j in range(6): t = Thread(target=sub_f,)) threads.append(t) t.start() for t in threads: t.join() def thread_pool(sub_f): with ThreadPoolExecutor(max_workers=6) as executor: res = [executor.submit(sub_f,j) for j in range(6)] def process_pool(sub_f): with ProcessPoolExecutor(max_workers=6) as executor: res = executor.map(sub_f,list(range(6))) def showtime(f,sub_f,name): start_time = time.time() f(sub_f) print("{} time: {:.4f}s".format(name,time.time() - start_time)) def main(sub_f): showtime(normal,"normal") print() print("------ 多程序 ------") showtime(joblib_process,"joblib multiprocess") showtime(mp,"pool") showtime(asy,"async") showtime(process_pool,"process_pool") print() print("----- 多執行緒 -----") showtime(joblib_thread,"joblib thread") showtime(thread,"thread") showtime(thread_pool,"thread_pool") if __name__ == "__main__": print("----- 計算密集型 -----") sub_f = f_compute main(sub_f) print() print("----- IO 密集型 -----") sub_f = f_IO main(sub_f)
結果:
----- 計算密集型 ----- normal time: 15.1212s ------ 多程序 ------ joblib multiprocess time: 8.2421s pool time: 8.5439s async time: 8.3229s process_pool time: 8.1722s ----- 多執行緒 ----- joblib thread time: 21.5191s thread time: 21.3865s thread_pool time: 22.5104s ----- IO 密集型 ----- normal time: 30.0305s ------ 多程序 ------ joblib multiprocess time: 5.0345s pool time: 5.0188s async time: 5.0256s process_pool time: 5.0263s ----- 多執行緒 ----- joblib thread time: 5.0142s thread time: 5.0055s thread_pool time: 5.0064s
上面每一方法都統一建立6個程序/執行緒,結果是計算密集型任務中速度:多程序 > 單程序/執行緒 > 多執行緒, IO 密集型任務速度: 多執行緒 > 多程序 > 單程序/執行緒。
以上就是Python 多程序、多執行緒效率比較的詳細內容,更多關於Python 多程序、多執行緒的資料請關注我們其它相關文章!