Python 是一個(gè)用途非常廣泛的編程語(yǔ)言,擁有成千上萬(wàn)的第三方庫(kù),在人工智能、機(jī)器學(xué)習(xí)、自動(dòng)化等方面有著廣泛的應(yīng)用。
眾所周知,Python 是動(dòng)態(tài)語(yǔ)言,有全局解釋器鎖,比其他靜態(tài)語(yǔ)言要慢,也正是這個(gè)原因,你也許會(huì)轉(zhuǎn)向其他語(yǔ)言如 Java、C++,不過(guò)先等等,今天分享一個(gè)可以讓 Python 比 C++ 還要快的技術(shù),看完再?zèng)Q定要不要轉(zhuǎn)吧。
今天的主角就是 Numba,Numba 是一個(gè)開(kāi)源的即時(shí)編譯器(JIT compiler),可將 Python 和 NumPy 的代碼的轉(zhuǎn)換為快速的機(jī)器碼,從而提升運(yùn)行速度??梢赃_(dá)到 C 或 FORTRAN 的速度。 這么牛逼是不是很難用呢?No,No,No,So easy,你不需要替換 Python 解釋器,不需要單獨(dú)編譯,甚至不需要安裝 C / C ++ 編譯器。只需將 Numba 提供的裝飾器放在 Python 函數(shù)上面就行,剩下的就交給 Numba 完成。舉個(gè)簡(jiǎn)單的例子:
c++ C++ 確實(shí)牛逼,才 2.3 秒,不過(guò)好戲還在后頭,現(xiàn)在我們使用 Numba 來(lái)加速一下,操作很簡(jiǎn)單,不需要改動(dòng)原有的代碼,先導(dǎo)入 Numba 的 njit,再在函數(shù)上方放個(gè)裝飾器 @njit 即可,其他保持不變,代碼如下:
Python 看到這里,Numba 又讓我燃起了對(duì) Python 的激情,我不轉(zhuǎn) C++ 了,Python 夠用了。 Numba 如何做到的呢?官方文檔這樣介紹:它讀取裝飾函數(shù)的 Python 字節(jié)碼,并將其與有關(guān)函數(shù)輸入?yún)?shù)類型的信息結(jié)合起來(lái),分析和優(yōu)化代碼,最后使用編譯器庫(kù)(LLVM)針對(duì)你的 CPU 生成量身定制的機(jī)器代碼。每次調(diào)用函數(shù)時(shí),都會(huì)使用此編譯版本,你說(shuō)牛逼不? Numba 還有更多詳細(xì)的用法,這里不多說(shuō),想了解的請(qǐng)移步官方文檔[1]。
眾所周知,Python 是動(dòng)態(tài)語(yǔ)言,有全局解釋器鎖,比其他靜態(tài)語(yǔ)言要慢,也正是這個(gè)原因,你也許會(huì)轉(zhuǎn)向其他語(yǔ)言如 Java、C++,不過(guò)先等等,今天分享一個(gè)可以讓 Python 比 C++ 還要快的技術(shù),看完再?zèng)Q定要不要轉(zhuǎn)吧。
今天的主角就是 Numba,Numba 是一個(gè)開(kāi)源的即時(shí)編譯器(JIT compiler),可將 Python 和 NumPy 的代碼的轉(zhuǎn)換為快速的機(jī)器碼,從而提升運(yùn)行速度??梢赃_(dá)到 C 或 FORTRAN 的速度。 這么牛逼是不是很難用呢?No,No,No,So easy,你不需要替換 Python 解釋器,不需要單獨(dú)編譯,甚至不需要安裝 C / C ++ 編譯器。只需將 Numba 提供的裝飾器放在 Python 函數(shù)上面就行,剩下的就交給 Numba 完成。舉個(gè)簡(jiǎn)單的例子:
from numba import jitNumba 是專為科學(xué)計(jì)算而設(shè)計(jì)的,在與 NumPy 一起使用時(shí),Numba 會(huì)為不同的數(shù)組數(shù)據(jù)類型生成專門的代碼,以優(yōu)化性能:
import random
@jit(nopython=True)
def monte_carlo_pi(nsamples):
acc = 0
for i in range(nsamples):
x = random.random()
y = random.random()
if (x ** 2 + y ** 2) < 1.0:
acc += 1
return 4.0 * acc / nsamples
@numba.jit(nopython=True, parallel=True)現(xiàn)在我們來(lái)看看,同樣的代碼,使用 Numba 前后與 C++ 的性能對(duì)比。比如說(shuō)我們要找出 1000 萬(wàn)以內(nèi)所有的素?cái)?shù),代碼的算法邏輯是相同的: Python 代碼:
def logistic_regression(Y, X, w, iterations):
for i in range(iterations):
w -= np.dot(((1.0 /
(1.0 + np.exp(-Y * np.dot(X, w)))
- 1.0) * Y), X)
return w
import math執(zhí)行耗時(shí):
import time
def is_prime(num):
if num == 2:
return True
if num <= 1 or not num % 2:
return False
for div in range(3, int(math.sqrt(num) + 1), 2):
if not num % div:
return False
return True
def run_program(N):
total = 0
for i in range(N):
if is_prime(i):
total += 1
return total
if __name__ == "__main__":
N = 10000000
start = time.time()
total = run_program(N)
end = time.time()
print(f"total prime num is {total}")
print(f"cost {end - start}s")
total prime num is 664579C++ 代碼如下:
cost 47.386465072631836s
#include <iostream>
#include <cmath>
#include <time.h>
using namespace std;
bool isPrime(int num) {
if (num == 2) return true;
if (num <= 1 || num % 2 == 0) return false;
double sqrt_num = sqrt(double(num));
for (int div = 3; div <= sqrt_num; div +=2){
if (num % div == 0) return false;
}
return true;
}
int run_program(int N){
int total = 0;
for (int i; i < N; i++) {
if(isPrime(i)) total ++;
}
return total;
}
int main()
{
int N = 10000000;
clock_t start,end;
start = clock();
int total = run_program(N);
end = clock();
cout << "total prime num is " << total;
cout << "\ncost " << (end - start) / ((double) CLOCKS_PER_SEC) << "s\n";
return 0;
}
$ g++ isPrime.cpp -o isPrime
$ ./isPrime
total prime num is 664579
cost 2.36221s
c++ C++ 確實(shí)牛逼,才 2.3 秒,不過(guò)好戲還在后頭,現(xiàn)在我們使用 Numba 來(lái)加速一下,操作很簡(jiǎn)單,不需要改動(dòng)原有的代碼,先導(dǎo)入 Numba 的 njit,再在函數(shù)上方放個(gè)裝飾器 @njit 即可,其他保持不變,代碼如下:
import math運(yùn)行一下,可以看出時(shí)間已經(jīng)從 47.39 秒降低到 3 秒。
import time
from numba import njit
# @njit 相當(dāng)于 @jit(nopython=True)
@njit
def is_prime(num):
if num == 2:
return True
if num <= 1 or not num % 2:
return False
for div in range(3, int(math.sqrt(num) + 1), 2):
if not num % div:
return False
return True
@njit
def run_program(N):
total = 0
for i in range(N):
if is_prime(i):
total += 1
return total
if __name__ == "__main__":
N = 10000000
start = time.time()
total = run_program(N)
end = time.time()
print(f"total prime num is {total}")
print(f"cost {end - start}s")
total prime num is 664579相比 C++ 的 2.3 秒還是有一點(diǎn)慢,你可能會(huì)說(shuō) Python 還是不行啊。等一等,我們還有優(yōu)化的空間,就是 Python 的 for 循環(huán),那可是 1000 萬(wàn)的循環(huán),對(duì)此,Numba 提供了 prange 參數(shù)來(lái)并行計(jì)算,從而并發(fā)處理循環(huán)語(yǔ)句,只需要將 range 修改為 prange,裝飾器傳個(gè)參數(shù):parallel = True,其他不變,代碼改動(dòng)如下:
cost 3.0948808193206787s
import math現(xiàn)在運(yùn)行一下:
import time
from numba import njit, prange
@njit
def is_prime(num):
if num == 2:
return True
if num <= 1 or not num % 2:
return False
for div in range(3, int(math.sqrt(num) + 1), 2):
if not num % div:
return False
return True
@njit(parallel = True)
def run_program(N):
total = 0
for i in prange(N):
if is_prime(i):
total += 1
return total
if __name__ == "__main__":
N = 10000000
start = time.time()
total = run_program(N)
end = time.time()
print(f"total prime num is {total}")
print(f"cost {end - start}s")
python isPrime.py才 1.43 秒,比 C++ 還快,Numba 真的牛逼!我又運(yùn)行了兩次,確認(rèn)自己沒(méi)看錯(cuò),平均就是 1.4 秒:
total prime num is 664579
cost 1.4398791790008545s
Python 看到這里,Numba 又讓我燃起了對(duì) Python 的激情,我不轉(zhuǎn) C++ 了,Python 夠用了。 Numba 如何做到的呢?官方文檔這樣介紹:它讀取裝飾函數(shù)的 Python 字節(jié)碼,并將其與有關(guān)函數(shù)輸入?yún)?shù)類型的信息結(jié)合起來(lái),分析和優(yōu)化代碼,最后使用編譯器庫(kù)(LLVM)針對(duì)你的 CPU 生成量身定制的機(jī)器代碼。每次調(diào)用函數(shù)時(shí),都會(huì)使用此編譯版本,你說(shuō)牛逼不? Numba 還有更多詳細(xì)的用法,這里不多說(shuō),想了解的請(qǐng)移步官方文檔[1]。
最后的話
Python 幾乎在每一個(gè)領(lǐng)域都有對(duì)應(yīng)的解決方案,本文提到的 Numba 庫(kù)就是專門解決 Python 在計(jì)算密集型任務(wù)方面性能不足的問(wèn)題,如果你從事機(jī)器學(xué)習(xí)、數(shù)據(jù)挖掘等領(lǐng)域,這個(gè)會(huì)非常有幫助,如果本文對(duì)你有用,請(qǐng)點(diǎn)贊、在看、關(guān)注支持。轉(zhuǎn)載自:菜鳥學(xué)python
以上就是小編為您整理的關(guān)于 Python可以比C++更快 的全部?jī)?nèi)容。