python能夠提升程序執行速度N倍你知道嗎?

1.1。Numba的約5分鐘指南

Numba是Python的即時編譯器,它最適用於使用NumPy數組和函數以及循環的代碼。使用Numba的最經常使用方法是經過其裝飾器集合,能夠應用於您的函數來指示Numba編譯它們。當調用Numba修飾函數時,它被編譯爲機器代碼「及時」執行,而且您的所有或部分代碼隨後能夠以本機機器代碼速度運行!html

開箱即用的Numba使用如下方法:python

  • 操做系統:Windows(32位和64位),OSX和Linux(32位和64位)
  • 架構:x86,x86_64,ppc64le。在armv7l,armv8l(aarch64)上進行實驗。
  • GPU:Nvidia CUDA。AMD ROC的實驗。
  • CPython的
  • NumPy 1.10 - 最新

1.1.1。我怎麼獲得它?

Numba可做爲暢達包爲 蟒蛇Python發佈git

$ conda install numba

Numba還有pip可供選擇:github

$ pip install numba

Numba也能夠 從源代碼編譯,雖然咱們不建議首次使用Numba用戶。數組

Numba一般用做核心包,所以其依賴性保持在絕對最小值,可是,能夠按以下方式安裝額外的包以提供其餘功能:緩存

  • scipy- 支持編譯numpy.linalg功能。
  • colorama - 支持回溯/錯誤消息中的顏色突出顯示。
  • pyyaml - 經過YAML配置文件啓用Numba配置。
  • icc_rt - 容許使用Intel SVML(高性能短矢量數學庫,僅限x86_64)。安裝說明在 性能提示中

1.1.2。Numba會爲個人代碼工做嗎?

這取決於你的代碼是什麼樣的,若是你的代碼是以數字爲導向的(作了不少數學運算),常用NumPy和/或有不少循環,那麼Numba一般是一個不錯的選擇。在這些例子中,咱們將應用最基本的Numba的JIT裝飾器,@jit試圖加速一些函數來演示哪些有效,哪些無效。架構

Numba在代碼看起來像這樣:app

from numba import jit
import numpy as np

x = np.arange(100).reshape(10, 10)

@jit(nopython=True) # Set "nopython" mode for best performance
def go_fast(a): # Function is compiled to machine code when called the first time
    trace = 0
    for i in range(a.shape[0]):   # Numba likes loops
        trace += np.tanh(a[i, i]) # Numba likes NumPy functions
    return a + trace              # Numba likes NumPy broadcasting

print(go_fast(x))

對於看起來像這樣的代碼,若是有的話,它將沒法正常工做:函數

from numba import jit
import pandas as pd

x = {'a': [1, 2, 3], 'b': [20, 30, 40]}

@jit
def use_pandas(a): # Function will not benefit from Numba jit
    df = pd.DataFrame.from_dict(a) # Numba doesn't know about pd.DataFrame
    df += 1                        # Numba doesn't understand what this is
    return df.cov()                # or this!

print(use_pandas(x))

請注意,Numba不理解Pandas,所以Numba只是經過解釋器運行此代碼,但增長了Numba內部開銷的成本!oop

1.1.3。什麼是nopython模式?

Numba @jit裝飾器從根本上以兩種編譯模式運行, nopython模式和object模式。go_fast上面例子中, nopython=True@jit裝飾器中設置,這是指示Numba在nopython模式下操做nopython編譯模式的行爲本質上是編譯裝飾函數,以便它徹底運行而不須要Python解釋器的參與。這是使用Numba jit裝飾器的推薦和最佳實踐方式,由於它能夠帶來最佳性能。

若是編譯nopython模式失敗,Numba能夠編譯使用 若是沒有設置,這是裝飾器的 後退模式(如上例所示)。在這種模式下,Numba將識別它能夠編譯的循環並將它們編譯成在機器代碼中運行的函數,而且它將運行解釋器中的其他代碼。爲得到最佳性能,請避免使用此模式objectmode@jitnopython=Trueuse_pandas

1.1.4。如何衡量Numba的表現?

首先,回想一下,Numba必須爲執行函數的機器代碼版本以前給出的參數類型編譯函數,這須要時間。可是,一旦編譯完成,Numba會爲所呈現的特定類型的參數緩存函數的機器代碼版本。若是再次使用相同的類型調用它,它能夠重用緩存的版本而沒必要再次編譯。

測量性能時,一個很是常見的錯誤是不考慮上述行爲,並使用一個簡單的計時器來計算一次,該計時器包括在執行時編譯函數所花費的時間。

例如:

from numba import jit
import numpy as np
import time

x = np.arange(100).reshape(10, 10)

@jit(nopython=True)
def go_fast(a): # Function is compiled and runs in machine code
    trace = 0
    for i in range(a.shape[0]):
        trace += np.tanh(a[i, i])
    return a + trace

# DO NOT REPORT THIS... COMPILATION TIME IS INCLUDED IN THE EXECUTION TIME!
start = time.time()
go_fast(x)
end = time.time()
print("Elapsed (with compilation) = %s" % (end - start))

# NOW THE FUNCTION IS COMPILED, RE-TIME IT EXECUTING FROM CACHE
start = time.time()
go_fast(x)
end = time.time()
print("Elapsed (after compilation) = %s" % (end - start))

這,例如打印:

Elapsed (with compilation) = 0.33030009269714355
Elapsed (after compilation) = 6.67572021484375e-06

衡量Numba JIT對您的代碼的影響的一個好方法是使用timeit模塊函數來執行時間,這些函數測量屢次執行迭代,所以能夠在第一次執行時適應編譯時間。

做爲旁註,若是編譯時間成爲問題,Numba JIT支持 編譯函數的磁盤緩存,而且還具備Ahead-Of-Time編譯模式。

1.1.5。它有多快?

假設Numba能夠在nopython模式下運行,或者至少編譯一些循環,它將針對您的特定CPU進行編譯。加速因應用而異,但能夠是一到兩個數量級。Numba有一個 性能指南,涵蓋了得到額外性能的經常使用選項。

1.1.6。Numba如何運做?

Numba讀取裝飾函數的Python字節碼,並將其與有關函數輸入參數類型的信息相結合。它分析並優化您的代碼,最後使用LLVM編譯器庫生成函數的機器代碼版本,根據您的CPU功能量身定製。每次調用函數時都會使用此編譯版本。

1.1.7。其餘感興趣的東西:

Numba有至關多的裝飾,咱們看到@jit@njit,但也有:

  • @vectorize- 生成NumPy ufuncufunc支持全部方法)。文件在這裏
  • @guvectorize- 產生NumPy廣義ufuncs。 文件在這裏
  • @stencil - 將函數聲明爲相似模板操做的內核。 文件在這裏
  • @jitclass - 對於jit感知類。文件在這裏
  • @cfunc - 聲明一個函數用做本機回調(從C / C ++等調用)。文件在這裏
  • @overload- 註冊您本身的函數實現,以便在nopython模式下使用,例如@overload(scipy.special.j0)。 文件在這裏

一些裝飾者提供額外選項:

ctypes / cffi / cython互操做性:

  • cffi模式支持調用CFFI函數nopython
  • ctypes模式支持調用ctypes包裝函數nopython
  • Cython導出的函數是可調用的

1.1.7.1。GPU目標:

Numba能夠針對Nvidia CUDA和(實驗性)AMD ROC GPU。您可使用純Python編寫內核,讓Numba處理計算和數據移動(或明確地執行此操做)。單擊關於CUDAROC的 Numba文檔 

 

 

 

示例:接下來咱們寫一段簡單的代碼,來計算一下執行時間:

示例1:不使用numba的:

import time



def num():

    arr = []
    for i in range(10000000):
        arr.append(i)


stime = time.time()
num()
etime = time.time() - stime
# print(arr)
print('用時:{}秒'.format(etime))

示例輸出時間:

用時:1.4500024318695068秒

 

示例2:使用numba @jit

import time
from numba import jit

@jit
def num():

    arr = []
    for i in range(10000000):
        arr.append(i)


stime = time.time()
num()
etime = time.time() - stime
# print(arr)
print('用時:{}秒'.format(etime))

示例輸出:

用時:0.5530002117156982秒

 

結論:

上述兩個示例代碼,一個使用了numba,另外一個沒有使用numba;能夠看出使用numba @jit裝飾後,時間明顯快了不少倍。

這只是一個簡單示例;對於複雜計算提升速度更明顯。

相關文章
相關標籤/搜索