淺談python中的多線程和多進程

本文以一個簡單的例子介紹python中多線程和多進程的差別。

我們在進行生信分析時經常要處理大文件,如果用串行運算往往費時,所以需要並行運算以節省時間。目前,流行的生信工具通常都可以並行運算,比如bwa。通常來講,我們進行並行運算可以選擇多線程或者多進程。那麼二者有什麼差別呢,我們又該如何選擇呢?

不同編程語言中的多線程和多進程實現機制是不一樣的,其實我們不關心實現機制,我們關注的是實際的性能。本文以python語言爲例,用一個測試腳本來比較python中多線程和多進程的性能區別。我們主要關注運行時間和內存佔用情況。

我們知道,python中常用的多線程模塊是threading,常用的多進程模塊是multiprocessing。我們的測試腳本要解決的是一個運算量比較大的任務,根據是否(並行)運算以及使用哪種並行運算可以分爲四種情形:

  • 不進行計算
  • 串行運算
  • 多線程運算
  • 多進程運算

得到的結果如下:
在這裏插入圖片描述

從中可以看出,對這個運算任務以及測試腳本而言,與串行運算相比,多線程所用的時間多很多,所佔的內存一樣;而多進程所用的時間變少(大約是串行運算時間的一半),所佔用的內存變大(大約是串行運算的三倍)。

上述結果值得討論的有兩個:

  1. 爲什麼python中多線程運算所用的時間比串行運算還多?
    這是因爲python中GIL(Global Interpreter Lock)的存在使得對一個進程而言,不管有多少線程,任一時刻,只會有一個線程在執行。對於CPU密集型的線程,由於系統調度等其它時間花銷,其效率不僅僅不高,反而有可能比較低[1]。也就是說,python中的多線程運算不能算作真正的並行運算。上面例子中的任務正好是一個CPU密集型任務,所以用多線程運算的時間反倒比串行運算還多。
  2. 爲什麼多線程運算佔用的內存和串行運算一樣,而多進程所用內存比串行運算大很多?
    這是一個正常的結果,是由線程和進程的特點決定的。簡單來說,線程會共享所屬進程的內存資源,所以不會有額外的內存佔用;而子進程會從父進程那裏拷貝一份內存資源,所以每多一個子進程,就會多一份內存資源的拷貝,佔用的內存就多了,上面的例子中共有兩個子進程,所以就會多出來兩份內存拷貝,看起來所佔用的內存就是串行運算的三倍。(所用的術語只是爲了闡述方便而用,可能有不恰當的地方)

綜上,由於生信分析大多是CPU密集型(計算密集型)的任務,如果你用python來處理此類任務,多進程並行運算可能更適合。

參考

[1] https://www.cnblogs.com/yssjun/p/11302500.html

所用的測試腳本如下:

#!/usr/bin/python
from threading import Thread 
from multiprocessing import Process
import time
import sys

def test_func(n):
    for _ in range(n):
        for i in a:
            j = i + 1
    print j

a = [1] * 10000000  # 10M
ncore = 2
per_run = 20
total_run = ncore * per_run

if __name__ == "__main__":
    start_time = time.time()
    if len(sys.argv) < 2:
        print "Usage: python %s <no-calc | serial | multi-thread | multi-process>" % sys.argv[0]
        sys.exit(1)
    cores = None
    if sys.argv[1] == "no-calc":
        time.sleep(1)
    elif sys.argv[1] == "serial":
        test_func(total_run)
    elif sys.argv[1] == "multi-thread":
        cores = [Thread(target = test_func, args=(per_run, )) for _ in range(ncore)]
    elif sys.argv[1] == "multi-process":
        cores = [Process(target = test_func, args=(per_run, )) for _ in range(ncore)]
    else:
        print "Usage: python %s <no-calc | serial | multi-thread | multi-process>" % sys.argv[0]
        sys.exit(3)
    if cores:
        for cr in cores:
            cr.start()
        for cr in cores:
            cr.join()
    end_time = time.time()
    print end_time - start_time
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章