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

Python?提速器numba

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

Python

python 真的太好用了,但是它真的好慢啊(哭死) ; C++ 很快,但是真的好難寫(xiě)啊,此生能不碰它就不碰它。老天啊,有沒(méi)有什么兩全其美的辦法呢?俗話(huà)說(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++ 寫(xiě)起來(lái)有哪些區(qū)別呢:

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

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

解釋性語(yǔ)言

C/C++ 這類(lèi)編譯性語(yǔ)言最大的好處就是其編譯過(guò)程是發(fā)生在運(yùn)行之前的,源代碼在調(diào)用前被編譯器轉(zhuǎn)換為可執(zhí)行機(jī)器碼,這樣就節(jié)約了大量的時(shí)間。而 python 作為一種解釋性語(yǔ)言,沒(méi)法做到一次編譯,后續(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)然還有更多的原因,限于篇幅就不再具體介紹,而我們開(kāi)篇提到的 numba 就是通過(guò) JIT 加速了 python 代碼。那么怎么使用 numba 加速我們的代碼呢?我們可以看一些簡(jiǎn)單的例子:

2.numba 加速 python 的小例子

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

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

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ù)上的,用戶(hù)可以不用關(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í)行, 常見(jiàn)的 python 解釋器的類(lèi)型有 cpython、IPython、PyPy、Jython、IronPython,與其他解釋器不同,numba 是使用 LLVM 編譯技術(shù)來(lái)解釋字節(jié)碼的。

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

在第一次調(diào)用 numba 裝飾的函數(shù)時(shí),numba 將在調(diào)用期間推斷參數(shù)類(lèi)型,numba 會(huì)結(jié)合給定的參數(shù)類(lèi)型將其編譯為機(jī)器代碼。這個(gè)過(guò)程是有一定的時(shí)間消耗的,但是一旦編譯完成,numba 會(huì)為所呈現(xiàn)的特定類(lèi)型的參數(shù)緩存函數(shù)的機(jī)器代碼版本,如果再次使用相同的類(lèi)型調(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ì)于指定輸入類(lèi)型這個(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 本身的類(lèi)型為 np.float64?
b = a.astype(np.float32)?
?
# 調(diào)用相同的函數(shù),但是輸入數(shù)據(jù)的類(lèi)型變?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ù)類(lèi)型,函數(shù)的運(yùn)行時(shí)間也會(huì)有一個(gè)很明顯的增加,但仍然是遠(yuǎn)低于第一次運(yùn)行時(shí)的編譯的時(shí)間。

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

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ù)類(lèi)型,讓 numba 自己判斷?
start = time.time()?
cal_sum1(a)?
end = time.time()?
print("Elapsed (with compilation) = %s" % (end - start))?
?
# 指定輸入輸出數(shù)據(jù)類(lèi)型?
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 也沒(méi)有 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 寫(xiě) CUDA Kernel, 直接在 GPU 上編譯和運(yùn)行我們的 Python 程序,numba 通過(guò)將 python 代碼直接編譯為遵循 CUDA 執(zhí)行模型的 CUDA 內(nèi)核和設(shè)備函數(shù)來(lái)支持 CUDA GPU 編程( 但是實(shí)際上 numba 目前支持的 CUDA API 很少,希望開(kāi)發(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è)空向量,類(lèi)似于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)寫(xiě)法的影響

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

我們可以有如下的兩種寫(xiě)法:

在 For 循環(huán)里面計(jì)算 vec1, 每次循環(huán)都需要訪(fǎng)問(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è)試一下兩種寫(xiě)法的速度:

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)存的訪(fǎng)問(wèn)次數(shù),從而降低函數(shù)的運(yùn)行時(shí)間。

總結(jié) :

我們介紹了一些用 numba 加速的常見(jiàn)場(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)論