您現在的位置是:首頁 > 綜合
Python 速度慢,試試這個方法提高 1000 倍
- 由 CSDN 發表于 綜合
- 2021-08-11
龜兔調節旋鈕會掉怎麼辦
作者 | Andrew Zhu
譯者 | 蘇本如
出品 | CSDN(ID:CSDNnews)
龜兔比賽(我6歲兒子 Charles Zhu 的繪畫作品)
人們一直詬病 Python 程式的速度很慢,它到底有多慢呢?
在每次的程式語言速度競賽中,Python 的名次通常都比較墊底。有人解釋這是因為 Python 是一種解釋型語言(程式碼無需編譯即可執行),而所有的解釋型程式語言執行速度都很慢。然而,我們知道 Java 也是一種解釋型語言,它的位元組碼是由 JVM 解釋的。而在這個基準測試速度比較頁面上的結果卻顯示:Java 要比 Python 的速度快得多。
下面是一個可以用來演示 Python 速度慢的示例。它使用傳統的 for 迴圈來產生一個數的倒數:
import numpy as np
np。random。seed(0)
values = np。random。randint(1, 100, size=1000000)
def get_reciprocal(values):
output = np。empty(len(values))
for i in range(len(values)):
output[i] = 1。0/values[i]
%timeit get_reciprocal(values)
結果顯示:
每個迴圈平均耗時3。37秒(標準偏差±582毫秒)(共計運行了7次程式,每次一個迴圈)
計算 1,000,000 個倒數竟然需要 3。37 秒。使用 C 語言執行同樣的運算只需要不到一眨眼的工夫:9 毫秒;C# 需要 19 毫秒;Nodejs 需要 26 毫秒;Java 僅僅需要 5 毫秒!而 Python 竟然用了讓人懷疑人生的 3。37秒(它到底做了些什麼)!(注:在本文的最後,我附上了所有語言的測試程式碼)。
Python 速度緩慢的根本
原因
我們通常把 Python 稱為一種動態型別程式語言。而 Python 程式中的一切變數都是以物件的形式存在,換句話說,每次 Python 程式碼處理資料時,都需要進行物件拆箱操作,以確定物件的具體型別。在 for 迴圈內部,每次迴圈都需要拆箱物件,檢查型別並計算倒數。那3秒鐘的時間都在型別檢查中浪費了。
C 語言和其他傳統的程式語言則不同,它們對資料的訪問是直接的。但在 Python 中,大量的 CPU 時間都用在了型別檢查上。
即使是一個簡單的賦值操作也會花費很長的時間。如:
a = 1
這個簡單的賦值操作,它需要如下兩個步驟:
步驟 1:將 a->PyObject_HEAD->typecode 設定為 Integer 型別。
步驟 2。 將值 1 賦值 a (a->val =1)。
關於 Python 為什麼速度慢的更多資訊,Jake 寫的這篇精彩文章值得一讀:Why Python is Slow: Looking Under the Hood
那麼,有沒有一種方法可以繞過型別檢查,從而提高 Python 程式的效能呢?
答案是:使用 NumPy 通用函式
與 Python 列表(list)不同,NumPy 陣列是圍繞 C 陣列構建的物件。NumPy 陣列訪問項不需要任何步驟來檢查型別。這給我們找到解決方案指明瞭方向:使用 NumPy
通用函式
(亦即UFunc)。
簡而言之,UFunc 是一種可以直接對整個陣列進行算術運算的方法。下面我們將前面那個慢速的 Python 示例改寫為 UFunc 版本,它就像下面這樣:
import numpy as np
np。random。seed(0)
values = np。random。randint(1, 100, size=1000000)
%timeit result = 1。0/values
改寫後的程式碼不僅提高了速度,而且程式碼變得更短。猜猜現在這個程式執行要花多少時間?它比我上面提到的最快的語言快了
2.7毫秒
:
每個迴圈平均耗時2。71毫秒(標準偏差±50。8微秒)(共運行了7次程式,每次迴圈100個)
返回程式碼,關鍵是 1。0/values 這一行。這裡的 values 不是一個數字,而是一個 NumPy 陣列。和除法運算子一樣,Numpy 還有許多其他運算子(如下圖示)。
點選這裡可以找到所有 Ufunc 運算(操作)符。
總結
對於那些使用 Python 的人來說,使用 Python 處理資料和數字的可能性很大。這些資料可以儲存在 NumPy 或 Pandas DataFrame中,因為DataFrame 是基於 NumPy 實現的。所以 Ufunc 也可以使用。
UFunc 使我們能夠以超越幾個數量級的更快速度在 Python 中執行重複操作。最慢的 Python 甚至可以跑得 C 語言更快。這一點太讓人激動了。
附錄— C,C#,Java 和 NodeJS 的測試程式碼
C 語言:
#include
#include
#include
int main{
struct timeval stop, start;
int length = 1000000;
int rand_array[length];
float output_array[length];
for(int i = 0; i rand_array[i] = rand; } gettimeofday(&start, ); for(int i = 0; i output_array[i] = 1。0/(rand_array[i]*1。0); } gettimeofday(&stop, ); printf(“took %lu us\n”, (stop。tv_sec - start。tv_sec) * 1000000 + stop。tv_usec - start。tv_usec); printf(“done\n”); return 0; } C#(。net 5。0): using System; namespace speed_test{ class Program{ static void Main(string[] args){ int length = 1000000; double rand_array =new double[length]; double output = new double[length]; var rand = new Random; for(int i =0; i rand_array[i] = rand。Next; //Console。WriteLine(rand_array[i]); } long start = DateTimeOffset。Now。ToUnixTimeMilliseconds; for(int i =0;i output[i] = 1。0/rand_array[i]; } long end = DateTimeOffset。Now。ToUnixTimeMilliseconds; Console。WriteLine(end - start); } } } Java: import java。util。Random; public class speed_test { public static void main(String[] args){ int length = 1000000; long rand_array = new long[length]; double output = new double[length]; Random rand = new Random ; for(int i =0; i rand_array[i] = rand。nextLong; } long start = System。currentTimeMillis; for(int i = 0;i output[i] = 1。0/rand_array[i]; } long end = System。currentTimeMillis; System。out。println(end - start); } } NodeJS: let length = 1000000; let rand_array = ; let output = ; for(var i=0;i rand_array[i] = Math。floor(Math。random*10000000); } let start = (new Date)。getMilliseconds; for(var i=0;i output[i] = 1。0/rand_array[i]; } let end = (new Date)。getMilliseconds; console。log(end - start); 原文連結:https://python。plainenglish。io/a-solution-to-boost-python-speed-1000x-times-c9e7d5be2f40 宣告:本文由CSDN翻譯,轉載請註明來源。 4月20日晚八點,歡迎來到CSDN悅讀時間直播間,與四位大咖一起探索UNIX傳奇往事的啟示,圍觀《UNIX傳奇》新書釋出會!