簡體   English   中英

加速 Python

[英]Speeding Up Python

這確實是兩個問題,但它們非常相似,為了簡單起見,我想我會把它們放在一起:

  • 首先:給定一個已建立的 python 項目,除了簡單的代碼優化之外,還有哪些不錯的方法可以加快它的速度?

  • 其次:在python中從頭開始編寫程序時,有哪些可以大大提高性能的好方法?

對於第一個問題,假設您收到了一個編寫得體的項目,您需要提高性能,但您似乎無法通過重構/優化獲得太多收益。 在這種情況下,除了用 C 之類的東西重寫它之外,你會怎么做來加速它?

關於“第二:用python從頭開始寫程序時,有什么好的方法可以大大提高性能?”

記住傑克遜的優化規則:

  • 規則1:不要這樣做。
  • 規則 2(僅適用於專家):先不要這樣做。

和克努斯規則:

  • “過早優化是萬惡之源。”

更有用的規則在優化通用規則中

  1. 不要邊走邊優化。 先弄對。 那就快點拿吧。 優化錯誤的程序仍然是錯誤的。

  2. 記住 80/20 規則。

  3. 始終運行“之前”和“之后”基准測試。 否則,您將不知道是否找到了 80%。

  4. 使用正確的算法和數據結構。 這個規則應該是第一位的。 沒有什么比算法和數據結構更重要。

底線

您無法阻止或避免“優化此程序”的努力。 這是工作的一部分。 你必須為它計划並仔細地做,就像設計、編碼和測試活動一樣。

我建議,而不是僅僅使用 C,而是:

讓你的代碼有價值。 以更少的執行次數做更多的事情:

  • 將算法更改為更快的算法。 在許多情況下,速度更快並不需要花哨。
  • 使用碰巧用 C 編寫的 python 原語。有些事情會強制解釋器調度,而有些則不會。 后者更可取
  • 謹防首先構建大數據結構然后使用它的代碼。 想想 range 和 xrange 之間的區別。 一般來說,通常值得考慮程序的內存使用情況。 使用生成器有時可以將 O(n) 內存使用降低到 O(1)。
  • Python 通常是非優化的。 從循環中提升不變代碼,在緊密循環中盡可能消除常見的子表達式。
  • 如果某樣東西很貴,那么預先計算或記住它。 例如,可以編譯正則表達式。
  • 需要處理數字嗎? 您可能想查看numpy
  • 許多 python 程序運行緩慢是因為它們受磁盤 I/O 或數據庫訪問的限制。 確保在等待數據到達時您有一些值得做的事情,而不僅僅是阻塞。 武器可能類似於Twisted框架。
  • 請注意,許多關鍵的數據處理庫都有 C 版本,無論是 XML、JSON 還是諸如此類。 它們通常比 Python 解釋器快得多。

如果上述所有分析和測量代碼都失敗了,那么開始考慮 C 重寫路徑。

通常的嫌疑人——分析它,找到最昂貴的線路,弄清楚它在做什么,修復它。 如果您以前沒有做過很多分析,那么可能會有一些大的二次循環或字符串重復隱藏在其他看起來無害的表達式后面。

在 Python 中,我發現導致非明顯減速的兩個最常見原因是字符串連接和生成器。 由於 Python 的字符串是不可變的,因此可以執行以下操作:

result = u""
for item in my_list:
    result += unicode (item)

每次迭代將復制整個字符串兩次。 這已經被很好地覆蓋了,解決方案是使用"".join

result = "".join (unicode (item) for item in my_list)

發電機是另一個罪魁禍首。 它們非常易於使用並且可以極大地簡化某些任務,但是應用不佳的生成器將比簡單地將項目附加到列表並返回列表要慢得多。

最后,不要害怕用 C 重寫位! Python 作為一種動態的高級語言,根本無法與 C 的速度相提並論。 如果您無法在 Python 中優化某個函數,請考慮將其提取到擴展模塊中。

我最喜歡的技術是同時維護模塊的 Python 和 C 版本。 Python 版本的編寫盡可能清晰明了——任何錯誤都應該易於診斷和修復。 針對此模塊編寫測試。 然后寫C版本,測試一下。 它的行為在所有情況下都應該與 Python 實現的行為相同——如果它們不同,應該很容易找出哪個是錯誤的並糾正問題。

首先想到的是: psyco 它暫時只在 x86 上運行。

然后,常量綁定 也就是說,使所有全局引用(以及global.attrglobal.attr.attr ...)成為函數和方法內部的局部名稱。 這並不總是成功的,但總的來說它是有效的。 它可以手工完成,但顯然很乏味。

你說除了代碼內優化之外,所以我不會深入研究這一點,但請對人們所做的典型錯誤( for i in range(10000000)想到)保持開放的心態。

Cython 和 pyrex 可用於使用類似 python 的語法生成 c 代碼。 Psyco 對於適當的項目也非常棒(有時你不會注意到速度有多大提升,有時它會快 50 倍)。 我仍然認為最好的方法是分析您的代碼(cProfile 等),然后將瓶頸編碼為 python 的 c 函數。

我很驚訝沒有人提到 ShedSkin: http : //code.google.com/p/shedskin/ ,它自動將您的 Python 程序轉換為 C++,並且在某些基准測試中比 psyco 在速度方面產生了更好的改進。

加上關於簡單性的軼事: http : //pyinsci.blogspot.com/2006/12/trying-out-latest-release-of-shedskin.html

但是有限制,請參閱: http : //tinyurl.com/shedskin-limitations

我希望你已經閱讀: http : //wiki.python.org/moin/PythonSpeed/PerformanceTips

恢復已經存在的東西通常有 3 個原則:

  • 編寫在更好的字節碼中轉換的代碼,例如,使用本地變量,避免不必要的查找/調用,使用慣用結構(如果有你想要的自然語法,使用它 - 通常更快。例如:不要這樣做:“輸入some_dict.keys()", 做 "for key in some_dict")
  • 用 C 編寫的任何東西都快得多,濫用您可用的任何 C 函數/模塊
  • 如有疑問,請導入 timeit、profile

這不一定會加速您的任何代碼,但如果您想避免減慢代碼速度,那么在使用 Python 編程時是關鍵知識。 如果不理解多線程程序的行為,“全局解釋器鎖”(GIL)有可能大幅降低多線程程序的速度(是的,這有點像我......我有一台不錯的 4 處理器機器,它不會一次使用超過 1.2 個處理器)。 有一篇介紹性文章,其中包含一些鏈接,可幫助您開始使用SmoothSpan

通過 Python 分析器運行您的應用程序。 找到一個嚴重的瓶頸。 用 C 重寫那個瓶頸。重復。

人們已經給出了一些很好的建議,但你必須意識到,當需要高性能時,python 模型是:punt to c。 像 psyco 這樣的努力在未來可能會有所幫助,但 python 並不是一種快速的語言,它的設計也不是這樣。 很少有語言能夠真正出色地處理動態內容並且仍然生成非常快的代碼; 至少在可預見的未來(以及一些不利於快速編譯的設計)情況會如此。

所以,如果你真的發現自己陷入這種困境,你最好的辦法是隔離系統中那些在(好的)python 中慢得不能接受的部分,並圍繞你將用 C 重寫這些位的想法進行設計。抱歉。 好的設計可以幫助減少這種痛苦。 不過,首先在 python 中對其進行原型設計,然后您也可以輕松地對您的 c 進行完整性檢查。

畢竟,這對於像 numpy 這樣的東西來說已經足夠了。 不過,好的設計對你有多大幫助,我再怎么強調也不為過。 如果你只是反復地檢查你的 python 位並用 C 替換最慢的,你最終可能會弄得一團糟。 仔細考慮需要 C 位的確切位置,以及如何合理地最小化和封裝它們。

通常可以通過使用內置 Python 調用將 Python 手寫的顯式算法替換為隱式算法來實現接近 C 的速度(對於任何使用 Python 的項目來說都足夠接近!)。 這是有效的,因為大多數 Python 內置函數無論如何都是用 C 編寫的。 好吧,當然是在 CPython 中;-) https://www.python.org/doc/essays/list2str/

關於使用 psyco 的注意事項:在某些情況下,它實際上會產生更慢的運行時間。 特別是在嘗試將 psyco 與用 C 編寫的代碼一起使用時。我不記得我讀過的文章,但特別提到了map()reduce()函數。 幸運的是,您可以告訴 psyco 不要處理指定的函數和/或模塊。

這是我嘗試遵循的程序:

  • 進口精神科; psyco.full()
  • 如果速度不夠快,請通過分析器運行代碼,看看瓶頸在哪里。 (在這一步禁用 psyco!)
  • 嘗試做其他人提到的事情,以盡快獲得那些瓶頸處的代碼。
    • 像 [str(x) for x in l] 或 [x.strip() for x in l] 之類的東西比 map(str, x) 或 map(str.strip, x) 慢得多。
  • 在此之后,如果我仍然需要更高的速度,實際上很容易啟動和運行 PyRex。 我先復制一段python代碼,直接放到pyrex代碼中,看看會發生什么。 然后我玩弄它,直到它變得越來越快。

關於如何改進 Python 代碼的規范參考在這里: PerformanceTips 除非您確實需要,否則我建議不要在 C 中進行優化。 對於大多數應用程序,您可以通過遵循該鏈接中發布的規則來獲得所需的性能。

如果使用 psyco,我會推薦psyco.profile()而不是psyco.full() 對於更大的項目,優化的功能和使用更少的內存會更聰明。

我還建議查看迭代器和生成器。 如果您的應用程序使用大型數據集,這將為您節省許多容器副本。

除了(很棒的) psyco和(漂亮的) shedskin 之外,我建議您嘗試使用cython一個很棒的pyrex叉子。

或者,如果您不着急,我建議您等待。 更新的 python 虛擬機即將到來, unladen-swallow將進入主流。

提出這個問題后,介紹了幾種加速 Python 代碼的方法:

  • Pypy有一個 JIT 編譯器,這使得它對於受 CPU 限制的代碼要快得多。
  • Pypy 是用 Rpython 編寫的, Rpython是 Python 的一個子集,它利用 LLVM 工具鏈編譯為本機代碼。

對於已建立的項目,我認為主要的性能提升將來自盡可能多地使用 python 內部庫。

一些提示在這里: http : //blog.hackerearth.com/faster-python-code

還有Python→11l→C++ transpiler,可以從這里下載。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM