嫌Python太慢?並行運算Process Pools三行代碼給你4倍提速!

2021-01-07 36kr

大數據文摘作品,作者 | Adam Geitgey,編譯 | 元元、Lisa、Saint、Aileen。

Python絕對是處理數據或者把重複任務自動化的絕佳程式語言。要抓取網頁日誌?或者要調整一百萬張圖片?總有對應的Python庫讓你輕鬆完成任務。

然而,Python的運營速度一直飽受詬病。默認狀態下,Python程序使用單個CPU的單個進程。如果你的電腦是最近十年生產的,多數情況下會有4個及以上CPU核。也就是說,當你在等程序運行結束的時候,你的計算機有75%或者更多的計算資源都是空置的!

讓我們來看看如何通過並行運算充分利用計算資源。多虧有Python的concurrent.futures模塊,僅需3行代碼就可以讓一個普通程序並行運行。

一般情況下的Python運行

比如說我們有一個文件夾,裡面全是圖片文件,我們想給每一張圖片創建縮略圖。

下面的短程序中我們使用Python自帶的glob 函數獲取一個包含文件夾中所有圖片文件的列表,並用Pillow圖片處理庫獲取每張圖片的128像素縮略圖。

這個程序遵循很常見的數據處理模式:

1.   從您想處理的一系列文件(或其他數據)開始

2.   編寫一個處理一個數據的輔助函數

3.   用for循環調動輔助函數,一個一個的去處理數據

讓我們用1000張圖片來測試這個程序,看看運行時間是多少。

程序運行時間8.9秒,但是計算機的運算資源佔用了多少呢?

讓我們再跑一次程序,同時查看活動監視器:

計算機有75%空置,這是為什麼呢?

問題在於我的計算機有4個CPU核,但是Python只用了其中一個核。即便我的程序把那個CPU核完全佔滿,但是其他3個CPU核什麼也沒幹。我們需要想辦法把整個程序的工作量分成4份然後平行運行。所幸Python可以做到這一點!

讓我們來試試並行運算。

下面是實現並行運算的一個方法:

1.把Jpeg圖片文件列表分成4個部分。

2. 同時跑四個Python解釋器。

3. 讓四個解釋器分別處理一部分圖片文件。

4. 匯總四個解釋器的結果得到最終結果。

四個Python程序分別在4個CPU上運行,跟之前在1個CPU運行相比大概可以達到4倍的速度,對不對?

好消息是Python可以幫我們解決並行運算麻煩的部分。我們僅需要告訴 Python我們想要運行什麼函數以及我們希望工作分成多少份,其他部分留給Python。我們只需要修改三行代碼。

首先,我們需要導入concurrent.futures庫。這個庫是Python自帶的:

然後,我們需要告訴 Python另外啟動4個Python實例。我們通過創建Process Pool來傳達指令:

默認設置下,上面的代碼會給計算機的每一個CPU創建一個Python進程,所以如果您的計算機有4個CPU,就會開啟4個Python進程。

最後一步是讓Process Pool 用這4個進程在數據列表中執行我們的輔助函數。我們可以把我們之前的for循環替代為:

新代碼是調用executor.map()函數 

executor.map() 函數調用時需要輸入輔助函數和待處理的數據列表。這個函數幫我們完成所有麻煩的工作,把列表分成幾個小列表,把小列表分配給每個子進程,運行子進程,以及匯總結果。幹得漂亮!

我們也可以得到每次調用輔助函數的結果。executor.map()函數以輸入數據順序返回結果。 Python的zip()函數可以一步獲取原始文件名以及相應結果。

下面是經過三步改動之後的程序:

讓我們試著運行一下,看看有沒有縮短運行時間:

2.274秒程序就運行完了!這便是原來版本的4倍加速。運行時間縮短的原因正是我們這次用4個CPU代替了1個CPU。

但是如果您仔細看看,您會看到「用戶(User)」時間大概是接近9秒,如果程序2秒就運行結束了,為什麼客戶時間會是9秒?這似乎…有哪裡不對?

其實這是因為」用戶」時間是所有CPU時間的總和。我們和上次一樣,用9秒的總CPU

注意:啟用Python進程以及給子進程分配數據都會佔用時間,因此您不一定能靠這個方法大幅提高速度。如果您處理的數據量很大,這裡有一篇「設置chunksize參數的技巧」文章可能可以幫助您:

這種方法總能幫我的程序提速嗎?

當你有一列數據,並且每個數據都可以獨立處理的時候,使用Process Pools是一個好方法。這有一些適合使用並行處理的例子:

從一系列單獨的網頁伺服器日誌裡抓取數據。

從一堆XML,CSV和JSON文件中解析數據。

對大量圖片數據做預處理,建立機器學習數據集。

但Process Pools不是萬能的。使用Process Pool需要在獨立的Python處理過程中將數據來回傳遞。如果你正在使用的數據不能在處理過程中有效的被傳遞,這種方法就行不通。你處理的數據必須是Python知道怎麼搞定的類型。

同時,數據不會按照一個預想的順序被處理。如果你需要前一步的處理結果來進行下一步驟,這種方法也行不通。

那GIL怎麼辦?

你可能聽說過Python有一個全局解釋器鎖(Global Interpreter Lock,),縮寫為GIL。這意味著即使你的程序是多層的,每一層也只有一個Python命令能被執行。GIL確保任何時候都只有一個Python線程執行。 GIL最大的問題就是Python的多線程程序並不能利用多核CPU的優勢。

但Process Pools能解決這個問題!因為我們在運行單獨的Python實例,每個實例都有自己的GIL。這樣你就有了真正的並行處理的Python代碼!

不要害怕並行處理!

有了concurrent.futures庫,Python可以讓你簡簡單單地修改腳本,卻能立刻調用你電腦上所有CPU內核開足馬力地運行。不要害怕嘗試。一旦你會用了,它就像寫一個for循環那樣簡單,但會讓整個程序快很多。

原文連結

相關焦點

  • 手把手 | 嫌Python太慢?並行運算Process Pools三行代碼給你4倍...
    大數據文摘作品,轉載要求見文末作者 | Adam Geitgey編譯 | 元元、Lisa、Saint、Aileen原文連結 | https://medium.com/@ageitgey/quick-tip-speed-up-your-python-data-processing-scripts-with-process-pools-cf275350163a
  • 三行Python代碼,讓數據處理速度提高2到6倍
    3 行代碼,大大加快數據預處理的速度。通過使用 Python 的 concurrent.futures 模塊,我們只需要 3 行代碼就可以讓一個普通的程序轉換成適用於多核處理器並行處理的程序。標準方法讓我們舉一個簡單的例子,在單個文件夾中有一個圖片數據集,其中有數萬張圖片。在這裡,我們決定使用 1000 張。
  • 代碼跑得慢甩鍋Python?手把手教你如何給代碼提速30%
    其實某個特定程序(無論使用何種程式語言)的運行速度是快還是慢,在很大程度上取決於編寫該程序的開發人員自身素質,以及他們編寫優化而高效代碼的能力。Medium上一位小哥就詳細講了講如何讓python提速30%,以此證明代碼跑得慢不是python的問題,而是代碼本身的問題。
  • 入門 | 三行Python代碼,讓數據預處理速度提高2到6倍
    本文可以教你僅使用 3 行代碼,大大加快數據預處理的速度。Python 是機器學習領域內的首選程式語言,它易於使用,也有很多出色的庫來幫助你更快處理數據。但當我們面臨大量數據時,一些問題就會顯現……目前,大數據(Big Data)這個術語通常用於表示包含數十萬數據點的數據集。在這樣的尺度上,工作進程中加入任何額外的計算都需要時刻注意保持效率。
  • 每周一個 Python 模塊|subprocess
    subprocess 模塊主要用於創建子進程,並連接它們的輸入、輸出和錯誤管道,獲取它們的返回狀態。通俗地說就是通過這個模塊,你可以在 Python 的代碼裡執行作業系統級別的命令,比如ipconfig、du -sh等。subprocess 模塊替代了一些老的模塊和函數,比如:os.system、os.spawn*等。
  • Python 中的 Subprocess 模塊
    Input and Outputsubprocess 模塊能阻止輸出,當你不關心標準輸出的時候是非常方便的。它也使你通過一種正確的方式管理輸入/輸出,有條理地整合python腳本中的的shell命令.Return Codes通過subprocess.call的返回值你能夠判定命令是否執行成功。每一個進程退出時都會返回一個狀態碼,你可以根據這個狀態碼寫一些代碼。stdin, stdout and stderr我在使用subprocess 時,有一個微妙的部分是怎麼使用管道把命令連接起來。
  • 提速30倍!這個加速包讓Python代碼飛起來
    接下來,創建一個setup.py文件,把Cython代碼翻譯成C代碼: 然後執行彙編過程,在命令行輸入如下命令: 我們的C代碼已經被編譯了,可以使用了!
  • Python打包成exe時,再犯這幾個錯誤就說不過去了
    其次,打包成可執行文件之後,別人就看不到你的Python代碼啦,這就可以避免你的代碼被人抄襲或者惡意修改,在一定程度上保護了你的代碼。最後,從用戶的角度來看,用戶只要下載了exe文件,雙擊就可以運行了,也不需要安裝任何東西。您的代碼變成了真正的「綠色軟體」,給用戶帶來了極大的方便。
  • 一行代碼讓你的Python運行速度提高100倍!Python真強!
    python一直被病垢運行速度太慢,但是實際上python的執行效率並不慢,慢的是python用的解釋器Cpython運行效率太差。「一行代碼讓python的運行速度提高100倍」這絕不是譁眾取寵的論調。我們來看一下這個最簡單的例子,從1一直累加到1億。
  • 程序媛筆記分享——python模塊之subprocess模塊
    usr/bin/env python# -*- coding:utf-8 -*-import subprocess# subprocess模塊中,多個指令的輸入,由參數shell決定輸入形式是字符串還是序列,如下:# ret = subprocess.call(["ls", "-l"], shell=False)# ret = subprocess.call
  • python性能提高10倍的通用方法
    在使用python進行模型點的計算過程中,經常需要遍歷所有點,如果點的數量很多,則運行是非常慢的,那麼,如何避開使用循環語句,而又能對每個點分別進行操作呢,答案就是:numpy!當然,除了numpy之外,python還有很多其它優秀的庫,但鑑於Houdini只內置了numpy庫,故本篇文章只討論numpy的提速方法。
  • 50行python代碼寫個計算器教程
    案例展示計算器.gif你能學到input 用戶輸入print輸出tkinter圖形界面python運算符號基礎知識準備運算符號數字運算,求和我們使用了加號 (+)運算符,除此外,還有 減號 (-), 乘號 (*), 除號 (/), 地板除 (//) 或 取餘 (%)。
  • Python代碼如何升級為Pythonic 代碼
    乍一看並不容易理解這段代碼,尤其是對於新開發人員來說,因為 lambdas 的語法很古怪,所以不容易理解。雖然這裡使用 lambda 可以節省行,然而,這並不能保證代碼的正確性和可讀性。同時這段代碼無法解決字典缺少鍵出現異常的問題。讓我們使用函數重寫此代碼,使代碼更具可讀性和正確性; 該函數將判斷異常情況,編寫起來要簡單得多。
  • Python不超過10行代碼就可實現人臉識別,教你辨別真假
    安裝 git 、cmake 、 python-pip# 安裝 git$ sudo apt-get install -y git# 安裝 cmake$ sudo apt-get install -y cmake# 安裝 python-pip$ sudo apt-get install -y python-pip4.
  • 這些Python代碼技巧,你肯定還不知道
    那麼你需要這個有用的命令行工具:https://github.com/gleitz/howdoi。inspect 模塊:https://docs.python.org/3/library/inspect.html下面的代碼示例使用 inspect.getsource() 列印自己的原始碼
  • 一行代碼實現Python並行處理
    ]    queue = Queue.Queue()    worker_threads = build_worker_pool(queue, 4)    start_time = time.time()    # Add the urls to process    for url in urls:        queue.put
  • Python代碼技巧,你值得擁有!
    __dict__# {'y': 4, 'x': 3}使用dict方法,將類以字典形式列印出來,也比較易讀。如何將類列印成json字符串如何在命令行查看python文檔如何將python代碼打包成獨立的二進位文件需要編譯的python代碼如下:#!/usr/bin/env python# -*- coding: utf-8 -*-print 'hello, world!'將python代碼打包成獨立的二進位文件步驟:
  • python小課堂17 - 30行代碼破解加密ZIP文件
    打開命令行,執行py腳本。我這裡命令行用的是git bash shell,這是一種windows下的類linux命令行,非常好用,兼容所有linux命令,裝了git的同學用了都說好!!~命令行函數代碼parser = optparse.OptionParser('\n  %prog -z <zipfile> -d <dictionary>')parser.add_option('-z', dest='zname', type='string', help='specify zip file')
  • 在Rust 代碼中編寫 Python 是種怎樣的體驗?
    預覽如果不熟悉inline-python類庫,你可以執行以下操作:fn main() {let who = "world";let n = 5; python!{for i in range('n): print(i, "Hello", 'who)print("Goodbye") }}它允許你將Python代碼直接嵌入Rust代碼行之間,甚至直接在Python代碼中使用Rust
  • 六行代碼實現你的第一個機器學習程序!
    下面用第2、3行代碼表示訓練數據,定義兩個變量:features特徵和labels標籤並處理一下特性數據用整數而不用字符串,0代表Bumpy;1代表Smooth;0代表Apple;1代表橘子。在第4行代碼中創建一個分類器:clf = tree.DecisionTreeClassifier()