Python 速度慢,試試這個方法提高 1000 倍
作者 | Andrew Zhu
譯者 | 蘇本如
出品 | CSDN(ID:CSDNnews)
1、人們一直詬病 Python 程序的速度很慢,它到底有多慢呢?
在每次的編程語言速度競賽中,Python 的名次通常都比較墊底。有人解釋這是因為 Python 是一種解釋型語言(代碼無需編譯即可執(zhí)行),而所有的解釋型編程語言執(zhí)行速度都很慢。然而,我們知道 Java 也是一種解釋型語言,它的字節(jié)碼是由 JVM 解釋的。而在這個基準測試速度比較頁面上的結果卻顯示:Java 要比 Python 的速度快得多。
下面是一個可以用來演示 Python 速度慢的示例。它使用傳統(tǒng)的 for 循環(huán)來產生一個數(shù)的倒數(shù):
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)
結果顯示:
每個循環(huán)平均耗時3.37秒(標準偏差±582毫秒)(共計運行了7次程序,每次一個循環(huán))
計算 1,000,000 個倒數(shù)竟然需要 3.37 秒。使用 C 語言執(zhí)行同樣的運算只需要不到一眨眼的工夫:9 毫秒;C# 需要 19 毫秒;Nodejs 需要 26 毫秒;Java 僅僅需要 5 毫秒!而 Python 竟然用了讓人懷疑人生的 3.37秒(它到底做了些什么)?。ㄗⅲ涸诒疚牡淖詈螅腋缴狭怂姓Z言的測試代碼)。
2、Python 速度緩慢的根本原因
我們通常把 Python 稱為一種動態(tài)類型編程語言。而 Python 程序中的一切變量都是以對象的形式存在,換句話說,每次 Python 代碼處理數(shù)據(jù)時,都需要進行對象拆箱操作,以確定對象的具體類型。在 for 循環(huán)內部,每次循環(huán)都需要拆箱對象,檢查類型并計算倒數(shù)。那3秒鐘的時間都在類型檢查中浪費了。
C 語言和其他傳統(tǒng)的編程語言則不同,它們對數(shù)據(jù)的訪問是直接的。但在 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 程序的性能呢?
3、答案是:使用 NumPy 通用函數(shù)
與 Python 列表(list)不同,NumPy 數(shù)組是圍繞 C 數(shù)組構建的對象。NumPy 數(shù)組訪問項不需要任何步驟來檢查類型。這給我們找到解決方案指明了方向:使用 NumPy 通用函數(shù)(亦即UFunc)。

簡而言之,UFunc 是一種可以直接對整個數(shù)組進行算術運算的方法。下面我們將前面那個慢速的 Python 示例改寫為 UFunc 版本,它就像下面這樣:
import numpy as np
np.random.seed(0)
values = np.random.randint(1, 100, size=1000000)
%timeit result = 1.0/values
改寫后的代碼不僅提高了速度,而且代碼變得更短。猜猜現(xiàn)在這個程序執(zhí)行要花多少時間?它比我上面提到的最快的語言快了2.7毫秒:
每個循環(huán)平均耗時2.71毫秒(標準偏差±50.8微秒)(共運行了7次程序,每次循環(huán)100個)
返回代碼,關鍵是 1.0/values 這一行。這里的 values 不是一個數(shù)字,而是一個 NumPy 數(shù)組。和除法運算符一樣,Numpy 還有許多其他運算符(如下圖示)。

點擊這里可以找到所有 Ufunc 運算(操作)符。
4、總結
對于那些使用 Python 的人來說,使用 Python 處理數(shù)據(jù)和數(shù)字的可能性很大。這些數(shù)據(jù)可以存儲在 NumPy 或 Pandas DataFrame中,因為DataFrame 是基于 NumPy 實現(xiàn)的。所以 Ufunc 也可以使用。
UFunc 使我們能夠以超越幾個數(shù)量級的更快速度在 Python 中執(zhí)行重復操作。最慢的 Python 甚至可以跑得 C 語言更快。這一點太讓人激動了。
5、附錄— C,C#,Java 和 NodeJS 的測試代碼
C 語言:
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
int main(){
struct timeval stop, start;
int length = 1000000;
int rand_array[length];
float output_array[length];
for(int i = 0; i<length; i++){
rand_array[i] = rand();
}
gettimeofday(&start, NULL);
for(int i = 0; i<length; i++){
output_array[i] = 1.0/(rand_array[i]*1.0);
}
gettimeofday(&stop, NULL);
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<length;i++){
rand_array[i] = rand.Next();
//Console.WriteLine(rand_array[i]);
}
long start = DateTimeOffset.Now.ToUnixTimeMilliseconds();
for(int i =0;i<length;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<length; i++){
rand_array[i] = rand.nextLong();
}
long start = System.currentTimeMillis();
for(int i = 0;i<length; 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<length;i++){
rand_array[i] = Math.floor(Math.random()*10000000);
}
let start = (new Date()).getMilliseconds();
for(var i=0;i<length;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
-End-
最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網(wǎng)盤了,歡迎下載!

面試題】即可獲取
