欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python?提速器numba

 更新時(shí)間:2022年01月12日 08:28:13   作者:愛摸魚的菜鳥碼農(nóng)  
這篇文章主要介紹了Python?提速器numba,相信大部分人都感嘆過(guò)python 真的太好用了,但是它真的好慢啊,然而今天我們就來(lái)用numba解決Python?慢的這個(gè)問(wèn)題,需要的朋友可以參考一下

Python

python 真的太好用了,但是它真的好慢?。匏? ; C++ 很快,但是真的好難寫啊,此生能不碰它就不碰它。老天啊,有沒有什么兩全其美的辦法呢?俗話說(shuō)的好:辦法總是比困難多,大家都有這個(gè)問(wèn)題,自然也就有大佬來(lái)試著解決這個(gè)問(wèn)題,這就請(qǐng)出我們今天的主角: numba

不過(guò)在介紹 numba 之前,我們還是得來(lái)看看 python 為什么這么慢:

1.為什么 python 這么慢

用過(guò) python 的人都知道, 尤其是在有循環(huán)的情況下,python 會(huì)比 C++ 慢很多,所以很多人都避免在 python 代碼里引入復(fù)雜的 for 循環(huán)。我們可以想想 python 和 C++ 寫起來(lái)有哪些區(qū)別呢:

動(dòng)態(tài)變量

如果你寫過(guò) C/C++ 就會(huì)發(fā)現(xiàn),我們需要對(duì)變量類型有嚴(yán)格的定義,我們需要定義變量的類型是 int 或者 float 之類的。但是 python 就不一樣了,寫過(guò)的 python 的人都知道,它去掉了變量申明和數(shù)據(jù)類型。也就是說(shuō),無(wú)論啥數(shù)據(jù),咱啥都不用管,想存就存!那么 python 是如何做到這樣灑脫自由的呢?這就不得不提 python 中萬(wàn)物皆是對(duì)象了,真正的數(shù)據(jù)是存在對(duì)象里面的。對(duì)于一個(gè)簡(jiǎn)單的兩個(gè)變量的加法,python 每次在做運(yùn)算的時(shí)候都得先判斷變量的類型,再取出來(lái)進(jìn)行運(yùn)算,而對(duì)于 C 來(lái)說(shuō),簡(jiǎn)單的內(nèi)存讀寫和機(jī)器指令 ADD 即可。其實(shí)在 C/C++ 中也有可變數(shù)據(jù)類型,但是其聲明是非常復(fù)雜的,是一種非常令人頭疼的結(jié)構(gòu)。

解釋性語(yǔ)言

C/C++ 這類編譯性語(yǔ)言最大的好處就是其編譯過(guò)程是發(fā)生在運(yùn)行之前的,源代碼在調(diào)用前被編譯器轉(zhuǎn)換為可執(zhí)行機(jī)器碼,這樣就節(jié)約了大量的時(shí)間。而 python 作為一種解釋性語(yǔ)言,沒法做到一次編譯,后續(xù)可以直接運(yùn)行,每次運(yùn)行的時(shí)候都要重新將源代碼通過(guò)解釋器轉(zhuǎn)化為機(jī)器碼。這樣一個(gè)好處就是非常容易 debug( 這里要再次感嘆一下 python 真不愧是新手友好型語(yǔ)言~), 當(dāng)然,這個(gè)問(wèn)題自然也是有嘗試解決的辦法,一個(gè)很重要的技術(shù)就是 JIT (Just-in-time compilation):JIT 即時(shí)編譯技術(shù)是在運(yùn)行時(shí)(runtime)將調(diào)用的函數(shù)或程序段編譯成機(jī)器碼載入內(nèi)存,以加快程序的執(zhí)行。說(shuō)白了,就是在第一遍執(zhí)行一段代碼前,先執(zhí)行編譯動(dòng)作,然后執(zhí)行編譯后的代碼。

上面只是簡(jiǎn)單列出了兩點(diǎn),當(dāng)然還有更多的原因,限于篇幅就不再具體介紹,而我們開篇提到的 numba 就是通過(guò) JIT 加速了 python 代碼。那么怎么使用 numba 加速我們的代碼呢?我們可以看一些簡(jiǎn)單的例子:

2.numba 加速 python 的小例子

numba 加速 python 代碼多簡(jiǎn)單方便呢,我們先來(lái)看看如何使用 numba 加速 python 代碼:

如果讓你用單純的 python 計(jì)算一個(gè)矩陣所有元素的和,很容易可以寫出下面的代碼:

def cal_sum(a):?
? ? result = 0?
? ? for i in range(a.shape[0]):?
? ? ? ? for j in range(a.shape[1]):?
? ? ? ? ? ? result += a[i, j]?
? ? return result?

當(dāng)需要計(jì)算的矩陣很小的時(shí)候,貌似速度也不慢,可以接受,但是如果輸入的矩陣大小為 (500, 500),

a = np.random.random((500, 500))?
%timeit cal_sum(a)?

輸出結(jié)果為:

47.8 ms ± 499 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) 

我們嘗試加上 numba:

import numba ?
?
@numba.jit(nopython=True)?
def cal_sum(a):?
? ? result = 0?
? ? for i in range(a.shape[0]):?
? ? ? ? for j in range(a.shape[1]):?
? ? ? ? ? ? result += a[i, j]?
? ? return result?

輸入同樣大小的矩陣

a = np.random.random((500, 500))?
%timeit cal_sum(a)?

輸出結(jié)果為:

236 µs ± 545 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each) 

注意在這里我們使用了%itemit 測(cè)試運(yùn)行時(shí)間(原因我們留到后面說(shuō)),通過(guò)對(duì)比兩個(gè)時(shí)間,我們可以發(fā)現(xiàn)通過(guò) numba 獲得了非常明顯的加速效果!

我們來(lái)具體看一下如何用 numba 加速 python 代碼:在實(shí)際使用過(guò)程中,numba 其實(shí)是以裝飾器的形式加在 python 函數(shù)上的,用戶可以不用關(guān)心到底 numba 是通過(guò)什么方法來(lái)優(yōu)化代碼,只需要調(diào)用就行。同時(shí)需要注意到 @jit 裝飾器同時(shí)也有一個(gè)參數(shù) nopython, 這個(gè)參數(shù)主要是來(lái)區(qū)分 numba 的運(yùn)行模式,numba 其實(shí)有兩種運(yùn)行模式:一個(gè)是 nopython 模式,另一個(gè)就是 object模式。只有在nopython 模式下,才會(huì)獲得最好的加速效果,如果 numba 發(fā)現(xiàn)你的代碼里有它不能理解的東西,就會(huì)自動(dòng)進(jìn)入 object 模式,保證程序至少是能夠運(yùn)行的(當(dāng)然這其實(shí)就失去了添加 numba 的意義)。如果我們將裝飾器改為 @jit(nopython=True) 或者 @njit,numba 會(huì)假設(shè)你已經(jīng)對(duì)所加速的函數(shù)非常了解,強(qiáng)制使用加速的方式,不會(huì)進(jìn)入 object 模式,如編譯不成功,則直接拋出異常。

當(dāng)然說(shuō)到這里,可能大家還是很困惑,numba 到底是怎么加速 python 代碼的?

python 代碼的編譯過(guò)程包括四個(gè)階段:詞法分析 -> 語(yǔ)法分析 -> 生成字節(jié)碼 -> 將字節(jié)碼解釋為機(jī)器碼執(zhí)行, 常見的 python 解釋器的類型有 cpython、IPython、PyPy、Jython、IronPython,與其他解釋器不同,numba 是使用 LLVM 編譯技術(shù)來(lái)解釋字節(jié)碼的。

LLVM 是一個(gè)編譯器,它采用字節(jié)碼,并將其編譯為機(jī)器碼,編譯過(guò)程涉及許多額外的傳遞,而 LLVM編譯器可以優(yōu)化字節(jié)碼,例如某些頻繁執(zhí)行的模塊,LLVM 可以將其作為 “hot code” 從而進(jìn)行相應(yīng)的優(yōu)化,LLVM 工具鏈非常擅長(zhǎng)優(yōu)化字節(jié)碼,它不僅可以編譯 numba 的代碼,還可以優(yōu)化它。

在第一次調(diào)用 numba 裝飾的函數(shù)時(shí),numba 將在調(diào)用期間推斷參數(shù)類型,numba 會(huì)結(jié)合給定的參數(shù)類型將其編譯為機(jī)器代碼。這個(gè)過(guò)程是有一定的時(shí)間消耗的,但是一旦編譯完成,numba 會(huì)為所呈現(xiàn)的特定類型的參數(shù)緩存函數(shù)的機(jī)器代碼版本,如果再次使用相同的類型調(diào)用它,它可以重用緩存的機(jī)器代碼而不必再次編譯。

  1. 在測(cè)量性能時(shí),如果只使用一個(gè)簡(jiǎn)單的計(jì)時(shí)器來(lái)計(jì)算一次,該計(jì)時(shí)器包括在執(zhí)行時(shí)編譯函數(shù)所花費(fèi)的時(shí)間,最準(zhǔn)確的運(yùn)行時(shí)間應(yīng)該是第二次及以后調(diào)用函數(shù)的運(yùn)行時(shí)間。
  2. 對(duì)于指定輸入類型這個(gè)問(wèn)題,我們可以嘗試做一個(gè)簡(jiǎn)單的實(shí)驗(yàn)看看到底有怎樣的影響:
a = np.random.random((5000, 5000))?
?
# 第一次調(diào)用時(shí)間包括編譯時(shí)間?
start = time.time()?
cal_sum(a)?
end = time.time()?
print("Elapsed (with compilation) = %s" % (end - start))?
?
# 函數(shù)被編譯,機(jī)器代碼被緩存?
start = time.time()?
cal_sum(a)?
end = time.time()?
print("Elapsed (after compilation) = %s" % (end - start))?
?
# 這里 a 本身的類型為 np.float64?
b = a.astype(np.float32)?
?
# 調(diào)用相同的函數(shù),但是輸入數(shù)據(jù)的類型變?yōu)?np.float32?
start = time.time()?
cal_sum(b)?
end = time.time()?
print("Elapsed (after compilation) = %s" % (end - start))?

輸出結(jié)果:

Elapsed (with compilation) = 0.20406198501586914 
Elapsed (after compilation) = 0.025263309478759766 
Elapsed (after compilation) = 0.07892274856567383 

可以看到如果我們輸入了和第一次調(diào)用編譯時(shí)不同的數(shù)據(jù)類型,函數(shù)的運(yùn)行時(shí)間也會(huì)有一個(gè)很明顯的增加,但仍然是遠(yuǎn)低于第一次運(yùn)行時(shí)的編譯的時(shí)間。

3. 如果調(diào)用 numba 的時(shí)候顯式地指定輸入、輸出數(shù)據(jù)的類型,可以加快初次調(diào)用的函數(shù)時(shí)的編譯速度,同時(shí)壞處就是如果顯式指定后,那么之后調(diào)用該函數(shù)都必須滿足規(guī)定的數(shù)據(jù)類型。

a = np.random.random((500, 500))?
?
@numba.njit()?
def cal_sum1(a):?
? ? result = 0?
? ? for i in range(a.shape[0]):?
? ? ? ? for j in range(a.shape[1]):?
? ? ? ? ? ? result += a[i, j]?
? ? return result?
?
@numba.njit('float64(float64[:, :])')?
def cal_sum2(a):?
? ? result = 0?
? ? for i in range(a.shape[0]):?
? ? ? ? for j in range(a.shape[1]):?
? ? ? ? ? ? result += a[i, j]?
? ? return result?
?
# 不指定輸入輸出數(shù)據(jù)類型,讓 numba 自己判斷?
start = time.time()?
cal_sum1(a)?
end = time.time()?
print("Elapsed (with compilation) = %s" % (end - start))?
?
# 指定輸入輸出數(shù)據(jù)類型?
start = time.time()?
cal_sum2(a)?
end = time.time()?
print("Elapsed (with compilation) = %s" % (end - start))?

分別耗時(shí):

Elapsed (after compilation) = 0.054465532302856445?
Elapsed (after compilation) = 0.0004112720489501953?

 

可以看到編譯的時(shí)間被大大減少了,其實(shí)這個(gè)時(shí)間非常接近直接運(yùn)行該函數(shù)生成的機(jī)器代碼的時(shí)間。

上面說(shuō)了這么多,但是轉(zhuǎn)念一想,矩陣相加這個(gè)函數(shù) numpy 里好像早就有了,np.sum 它不好用,它不香嘛??干嘛搞得這么復(fù)雜?

好吧,就上面舉的簡(jiǎn)單的例子來(lái)說(shuō),使用 numpy numba 加速基本效果差不多,但是在實(shí)際情況里面,不是所有的 for 循環(huán)代碼都可以直接用 numpy 自帶的函數(shù)實(shí)現(xiàn)。但是 numba 基本對(duì)所有的 for 循環(huán)代碼都有非常好的加速效果,當(dāng)然前提是 for 循環(huán)里面的代碼必須是 numba 能夠理解的。

而在從實(shí)際使用中,一般推薦將代碼中密集的計(jì)算部分提取出來(lái)作為單獨(dú)的函數(shù)實(shí)現(xiàn),并使用 nopython 方式優(yōu)化,這樣可以保證我們能使用到 numba 的加速功能。其余部分還是使用 python 原生代碼,這樣一方面就可以做到在 numba 加速不明顯或者無(wú)法加速的代碼中調(diào)用各種函數(shù)實(shí)現(xiàn)自己的代碼邏輯, 另一方面也能享受到 numba 的加速效果。

3.numba 加速 numpy 運(yùn)算

上面說(shuō)了 numba 一大亮點(diǎn)就是加速 for 循環(huán),除此以外,numba 對(duì) numpy 的運(yùn)算也同樣的有加速的效果。因?yàn)榧词故?numpy 也沒有 numba 轉(zhuǎn)換為機(jī)器碼快,numba 尤其擅長(zhǎng)加速 numpy 的基本運(yùn)算 (如加法、相乘和平方等等) ,其實(shí)準(zhǔn)確來(lái)說(shuō)如果 numpy 函數(shù)是對(duì)各個(gè)元素采用相同的操作的情況下,都會(huì)有比較好的效果。

我們簡(jiǎn)單舉一個(gè) numba 加速 numpy 運(yùn)算的例子:

a = np.ones((1000, 1000), np.int64) * 5?
b = np.ones((1000, 1000), np.int64) * 10?
c = np.ones((1000, 1000), np.int64) * 15?
?
def add_arrays(a, b, c):?
? ? return np.square(a, b, c)?
?
@numba.njit?
def add_arrays_numba(a, b, c):?
? ? return np.square(a, b, c)?
?
# 第一次調(diào)用完成編譯?
add_arrays_numba(a)?
?
# 函數(shù)被編譯,機(jī)器代碼被緩存?
start = time.time()?
add_arrays_numba(a)?
end = time.time()?
print("Elapsed (after compilation) = %s" % (end - start))?
?
# 不使用 numba 加速?
start = time.time()?
add_arrays(a)?
end = time.time()?
print("Elapsed = %s" % (end - start))?

Elapsed (after compilation) = 0.002088785171508789
Elapsed = 0.0031290054321289062

當(dāng)我們對(duì) numpy 數(shù)組進(jìn)行基本的數(shù)組計(jì)算,比如加法、乘法和平方,numpy 都會(huì)自動(dòng)在內(nèi)部向量化,這也是它可以比原生 python 代碼有更好性能的原因。但是在特定情況下,numpy 的代碼也不會(huì)和優(yōu)化過(guò)的機(jī)器代碼速度一樣快,此時(shí) numba 直接作用于 numpy 運(yùn)算也能起到一定的加速效果。

另一個(gè)例子主要來(lái)自于MMDetection3D,經(jīng)過(guò)一定的簡(jiǎn)化,主要是用來(lái)計(jì)算將點(diǎn)的坐標(biāo) (x, y) 壓縮到給定的[x_min, y_min, x_max, y_max] 范圍內(nèi):

x = np.random.random((5000))*5000?
y = np.random.random((5000))*5000?
x_min = 0?
x_max = 1000?
y_min=0?
y_max=2000?
?
@numba.njit?
def get_clip_numba(x, y, x_min, y_min, x_max, y_max):?
? ? z = np.stack((x, y), axis=1)?
? ? z[:, 0] = np.clip(z[:, 0], x_min, x_max)?
? ? z[:, 1] = np.clip(z[:, 1], y_min, y_max)?
? ? return z?
?
def get_clip(x, y, x_min, y_min, x_max, y_max):?
? ? z = np.stack((x, y), axis=1)?
? ? z[:, 0] = np.clip(z[:, 0], x_min, x_max)?
? ? z[:, 1] = np.clip(z[:, 1], y_min, y_max)?
? ? return z?
?
%timeit get_clip_numba(x, y, x_min, y_min, x_max, y_max)?
%timeit get_clip(x, y, x_min, y_min, x_max, y_max)?

分別用時(shí):

33.8 μs ± 12.2 μs per loop (mean ± std. dev. of 7 runs, 10000 loops each)?
57.2 μs ± 258 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)?

從實(shí)際情況來(lái)看, 并不是所有的 numpy 函數(shù)在使用 numba 后都能獲得比較好的加速效果,在某些情況下甚至?xí)档?numpy 的運(yùn)行速度。因此,在實(shí)際使用過(guò)程中建議提前測(cè)試一下確認(rèn)加速效果。通常將 numba 用于加速 numpy 的時(shí)候都是 for 循環(huán)和 numpy 一起使用的情況。 numba 對(duì) numpy 的大部分常用的函數(shù)都做了支持。

4.numba 使用 CUDA 加速

numba 更厲害的地方就在于,我們可以直接用 python 寫 CUDA Kernel, 直接在 GPU 上編譯和運(yùn)行我們的 Python 程序,numba 通過(guò)將 python 代碼直接編譯為遵循 CUDA 執(zhí)行模型的 CUDA 內(nèi)核和設(shè)備函數(shù)來(lái)支持 CUDA GPU 編程( 但是實(shí)際上 numba 目前支持的 CUDA API 很少,希望開發(fā)團(tuán)隊(duì)能更肝一點(diǎn)~~~) ,為了節(jié)省將 numpy 數(shù)組復(fù)制到指定設(shè)備,然后又將結(jié)果存儲(chǔ)到 numpy 數(shù)組中所浪費(fèi)的時(shí)間,numba 提供了一些函數(shù)來(lái)聲明并將數(shù)組送到指定設(shè)備來(lái)節(jié)省不必要的復(fù)制到 cpu 的時(shí)間。

常用內(nèi)存分配函數(shù):

  • cuda.device_array():在設(shè)備上分配一個(gè)空向量,類似于numpy.empty();
  • cuda.to_device():將主機(jī)的數(shù)據(jù)拷貝到設(shè)備;
  • cuda.copy_to_host():將設(shè)備的數(shù)據(jù)拷貝回主機(jī);

我們可以通過(guò)一個(gè)簡(jiǎn)單的矩陣相加的例子來(lái)看看通過(guò) numba 使用 CUDA 加速的效果:

from numba import cuda # 從numba調(diào)用cuda
import numpy as np
import math
from time import time
?
@cuda.jit
def matrix_add(a, b, result, m, n):
? ? idx = cuda.threadIdx.x + cuda.blockDim.x * cuda.blockIdx.x
? ? idy = cuda.threadIdx.y+ cuda.blockDim.y * cuda.blockIdx.y
? ? if idx < m and idy < n:
? ? ? ? result[idx, idy] = a[idx, idy] + b[idx, idy]
?
?
m = 5000
n = 4000
?
x = np.arange(m*n).reshape((m,n)).astype(np.int32)
y = np.arange(m*n).reshape((m,n)).astype(np.int32)
?
# 拷貝數(shù)據(jù)到設(shè)備端
x_device = cuda.to_device(x)
y_device = cuda.to_device(y)
?
# 在顯卡設(shè)備上初始化一塊用于存放GPU計(jì)算結(jié)果的空間
gpu_result1 = cuda.device_array((m,n))
gpu_result2 = cuda.device_array((m,n))
cpu_result = np.empty((m,n))
?
threads_per_block = 1024
blocks_per_grid = math.ceil(m*n / threads_per_block)
# 第一次調(diào)用包含編譯時(shí)間
start = time()
matrix_add[blocks_per_grid, threads_per_block](x_device, y_device, gpu_result1, m, n)
cuda.synchronize()
print("gpu matrix add time (with compilation) " + str(time() - start))
start = time()
matrix_add[blocks_per_grid, threads_per_block](x_device, y_device, gpu_result2, m, n)
cuda.synchronize()
print("gpu matrix add time (after compilation)" + str(time() - start))
start = time()
cpu_result = np.add(x, y)
print("cpu matrix add time " + str(time() - start))

運(yùn)行時(shí)間分別為:

gpu matrix add time (with compilation) 0.15977692604064941
gpu matrix add time (after compilation) 0.0005376338958740234
cpu matrix add time 0.023023128509521484

在通過(guò) numba 進(jìn)行 CUDA 加速的時(shí)候,主要是通過(guò)調(diào)用@cuda.jit 裝飾器實(shí)現(xiàn),從結(jié)果可以看到 numba 通過(guò)調(diào)用 CUDA 明顯加速了 python 程序。

5.For 循環(huán)寫法的影響

下面的一段代碼截取自MMDetection3D, 主要是用來(lái)判斷一系列點(diǎn)是否在一系列多邊形的內(nèi)部,

我們可以有如下的兩種寫法:

在 For 循環(huán)里面計(jì)算 vec1, 每次循環(huán)都需要訪問(wèn)多邊形 polygon 變量

@numba.jit(nopython=True)?
def points_in_convex_polygon1(points, polygon, clockwise=True):?
? ? # first convert polygon to directed lines?
? ? num_points_of_polygon = polygon.shape[1]?
? ? num_points = points.shape[0]?
? ? num_polygons = polygon.shape[0]?
? ? vec1 = np.zeros((2), dtype=polygon.dtype)?
? ? ret = np.zeros((num_points, num_polygons), dtype=np.bool_)?
? ? success = True?
? ? cross = 0.0?
? ? for i in range(num_points):?
? ? ? ? for j in range(num_polygons):?
? ? ? ? ? ? success = True?
? ? ? ? ? ? for k in range(num_points_of_polygon):?
? ? ? ? ? ? ? ? if clockwise:?
? ? ? ? ? ? ? ? ? ? vec1 = polygon[j, k] - polygon[j, k - 1]?
? ? ? ? ? ? ? ? else:?
? ? ? ? ? ? ? ? ? ? vec1 = polygon[j, k - 1] - polygon[j, k]?
? ? ? ? ? ? ? ? cross = vec1[1] * (polygon[j, k, 0] - points[i, 0])?
? ? ? ? ? ? ? ? cross -= vec1[0] * (polygon[j, k, 1] - points[i, 1])?
? ? ? ? ? ? ? ? if cross >= 0:?
? ? ? ? ? ? ? ? ? ? success = False?
? ? ? ? ? ? ? ? ? ? break?
? ? ? ? ? ? ret[i, j] = success?
? ? return ret?

在循環(huán)前預(yù)先計(jì)算好所有的 vec

@numba.jit(nopython=True)?
def points_in_convex_polygon2(points, polygon, clockwise=True):?
? ? # first convert polygon to directed lines?
? ? num_points_of_polygon = polygon.shape[1]?
? ? num_points = points.shape[0]?
? ? num_polygons = polygon.shape[0]?
? ? # vec for all the polygons?
? ? if clockwise:?
? ? ? ? vec1 = polygon - polygon[:, np.array([num_points_of_polygon - 1] +?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?list(range(num_points_of_polygon - 1))), :]?
? ? else:?
? ? ? ? vec1 = polygon[:, np.array([num_points_of_polygon - 1] +?
? ? ? ? ? ? ? ? ? ? ? ?list(range(num_points_of_polygon - 1))), :] - polygon?
? ? ret = np.zeros((num_points, num_polygons), dtype=np.bool_)?
? ? success = True?
? ? cross = 0.0?
? ? for i in range(num_points):?
? ? ? ? for j in range(num_polygons):?
? ? ? ? ? ? success = True?
? ? ? ? ? ? for k in range(num_points_of_polygon):?
? ? ? ? ? ? ? ? vec = vec1[j,k]?
? ? ? ? ? ? ? ? cross = vec[1] * (polygon[j, k, 0] - points[i, 0])?
? ? ? ? ? ? ? ? cross -= vec[0] * (polygon[j, k, 1] - points[i, 1])?
? ? ? ? ? ? ? ? if cross >= 0:?
? ? ? ? ? ? ? ? ? ? success = False?
? ? ? ? ? ? ? ? ? ? break?
? ? ? ? ? ? ret[i, j] = success?
? ? return ret?

簡(jiǎn)單測(cè)試一下兩種寫法的速度:

points = np.random.random((20000, 2)) * 100?
polygon = np.random.random((1000, 100, 2)) * 200 ?
?
start = time.time()?
points_in_convex_polygon1(points, polygon)?
end = time.time()?
print("Elapsed (with compilation) = %s" % (end - start))?
?
start = time.time()?
points_in_convex_polygon1(points, polygon)?
end = time.time()?
print("Elapsed (after compilation) = %s" % (end - start))?
?
start = time.time()?
points_in_convex_polygon2(points, polygon)?
end = time.time()?
print("Elapsed (with compilation) = %s" % (end - start))?
?
start = time.time()?
points_in_convex_polygon2(points, polygon)?
end = time.time()?
print("Elapsed (after compilation) = %s" % (end - start))?

輸出時(shí)間:

Elapsed (with compilation) = 3.9232356548309326 
Elapsed (after compilation) = 3.6778993606567383 
Elapsed (with compilation) = 0.6269152164459229 
Elapsed (after compilation) = 0.22288227081298828 

通過(guò)測(cè)試我們可以發(fā)現(xiàn)第二種方案會(huì)更快,在實(shí)際使用的時(shí)候,我們可以盡量減少在 for 循環(huán)內(nèi)部?jī)?nèi)存的訪問(wèn)次數(shù),從而降低函數(shù)的運(yùn)行時(shí)間。

總結(jié) :

我們介紹了一些用 numba 加速的常見場(chǎng)景,能夠有效地提高我們代碼的速度。不過(guò)大家在使用的時(shí)候,建議多多嘗試,比較一下使用與不使用的速度區(qū)別(有時(shí)候用了 numba 還可能變得更慢......),此外 MMDetection3D 很早就使用了 numba 加速代碼,而且我們最近在 MMDetection3D 中升級(jí)了 numba 的版本,從而獲得更好的 numpy 兼容性和代碼加速效果,

到此這篇關(guān)于Python 提速器numba的文章就介紹到這了,更多相關(guān)Python numba內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論