'使用Cython讓python代碼的速度提高30倍以上'

Python 編程語言 C語言 GPU 編譯器 人工智能遇見磐創 2019-07-31
"
"
使用Cython讓python代碼的速度提高30倍以上

毫無疑問,Python是社區最喜愛的編程語言!到目前為止,它是最容易使用的語言之一,因為python代碼是用一種直觀的、人類可讀的方式編寫的。

然而,你經常會反覆聽到一些對Python的抱怨,尤其是來自C語言愛好者的抱怨,這些抱怨無非就是Python很慢。

是的,他們並沒有說錯。

與許多其他編程語言相比,Python確實很慢。Benchmark game有一些比較不同編程語言在不同任務上的速度的可靠基準。

https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/gpp-python3.html

對於Python,我們有幾種不同的方法可以加快速度:

  • 使用多進程庫來使用所有的CPU核心

https://towardsdatascience.com/heres-how-you-can-get-a-2-6x-speed-up-on-your-data-pre-processing-with-python-847887e63be5

  • 如果你使用Numpy、panda或Scikit-Learn,使用Rapids來加速GPU上的處理。

https://towardsdatascience.com/heres-how-you-can-accelerate-your-data-science-on-gpu-4ecf99db3430

如果你所做的實際上可以並行化,比如數據預處理或矩陣運算,這些都是很好的方法。

但是如果你的代碼是純Python的呢?如果你不得不使用一個很大的for循環,且不能將數據放入矩陣中,因為數據必須按順序處理,那會怎樣?有沒有辦法加快Python本身的速度呢?

答案是肯定的,這就是Cython來加速原生Python代碼的地方。

什麼是Cython?

Cython是Python和C/C++之間的一箇中間步驟。它允許你編寫純Python代碼,並且只需要做一些小修改,然後將其直接翻譯成C代碼。

你對Python代碼所做的惟一調整就是向每個變量添加類型信息。通常,我們可以像這樣在Python中聲明一個變量:

x = 0.5

使用Cython,我們將向該變量添加一個類型:

cdef float x = 0.5

這告訴Cython,我們的變量是浮點類型,就像我們在C中所做的一樣。對於純Python,變量的類型是動態確定的。Cython中類型的顯式聲明使轉換為C成為可能,因為顯式類型聲明是必須的。

安裝Cython只需要一行簡單的pip命令:

pip install cython

Cython中的類型

使用Cython時,變量和函數分別有不同的類型。

對於變量我們有以下類型:

  • cdef int a, b, c
  • cdef char *s
  • cdef float x = 0.5 (單精度)
  • cdef double x = 63.4 (雙精度)
  • cdef list names
  • cdef dict goals_for_each_play
  • cdef object card_deck

注意所有這些類型都來自C/C++ ! 而對於方法我們有以下類型:

  • def — 常規python函數,僅從python調用。
  • cdef — 不能從python的代碼中訪問Cython的函數。即必須在Cython內調用
  • cpdef — C 和 Python. 可以從C和Python中訪問

瞭解了Cython類型之後,我們就可以直接實現加速了!

如何使用Cython加速你的代碼

我們要做的第一件事是設置Python代碼基準:用於計算數值階乘的for循環。原生Python代碼如下:

def test(x):
y = 1
for i in range(x+1):
y *= i
return y

相同功能的Cython方法看起來非常相似。首先,我們將確保Cython代碼文件具有.pyx擴展名。對代碼本身的惟一更改是,我們已經聲明瞭每個變量和函數的類型。

cpdef int test(int x):
cdef int y = 1
cdef int i
for i in range(x+1):
y *= i
return y

注意到該函數具有cpdef,以確保我們可以從Python中調用它。並且注意到到循環變量i是具有類型的。你需要為函數中的所有變量設置類型,以便C編譯器知道使用哪種類型!

接下來,創建setup.py文件,該文件將Cython代碼編譯為C代碼:

from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules = cythonize('run_cython.pyx'))

並執行編譯:

python setup.py build_ext --inplace

Boom ! 可以看到我們的C代碼已經編譯好了,可以使用了!

你將看到,在Cython代碼所在的文件夾中,你擁有運行C代碼所需的所有文件,包括run_cython.c文件。如果你感興趣,可以查看一下Cython生成的C代碼!

現在我們準備測試我們新的並且超級快的C代碼!查看下面的代碼,它實現了一個速度測試,將原生Python代碼與Cython代碼進行比較。

import run_python
import run_cython
import time
number = 10
start = time.time()
run_python.test(number)
end = time.time()
py_time = end - start
print("Python time = {}".format(py_time))
start = time.time()
run_cython.test(number)
end = time.time()
cy_time = end - start
print("Cython time = {}".format(cy_time))
print("Speedup = {}".format(py_time / cy_time))

代碼非常直觀,我們以與普通Python相同的方式導入文件,並以與普通Python相同的方式運行函數!

Cython幾乎可以讓你在所有原生Python代碼上獲得良好的加速,而不需要太多額外的工作。需要注意的關鍵是,循環次數越多,處理的數據越多,Cython可以提供的幫助就越多。

下表顯示了Cython為不同的數值階乘帶來的加速性能。當數值為10000000的時候,可以看到,我們的Cython加速超過了36倍。

"
使用Cython讓python代碼的速度提高30倍以上

毫無疑問,Python是社區最喜愛的編程語言!到目前為止,它是最容易使用的語言之一,因為python代碼是用一種直觀的、人類可讀的方式編寫的。

然而,你經常會反覆聽到一些對Python的抱怨,尤其是來自C語言愛好者的抱怨,這些抱怨無非就是Python很慢。

是的,他們並沒有說錯。

與許多其他編程語言相比,Python確實很慢。Benchmark game有一些比較不同編程語言在不同任務上的速度的可靠基準。

https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/gpp-python3.html

對於Python,我們有幾種不同的方法可以加快速度:

  • 使用多進程庫來使用所有的CPU核心

https://towardsdatascience.com/heres-how-you-can-get-a-2-6x-speed-up-on-your-data-pre-processing-with-python-847887e63be5

  • 如果你使用Numpy、panda或Scikit-Learn,使用Rapids來加速GPU上的處理。

https://towardsdatascience.com/heres-how-you-can-accelerate-your-data-science-on-gpu-4ecf99db3430

如果你所做的實際上可以並行化,比如數據預處理或矩陣運算,這些都是很好的方法。

但是如果你的代碼是純Python的呢?如果你不得不使用一個很大的for循環,且不能將數據放入矩陣中,因為數據必須按順序處理,那會怎樣?有沒有辦法加快Python本身的速度呢?

答案是肯定的,這就是Cython來加速原生Python代碼的地方。

什麼是Cython?

Cython是Python和C/C++之間的一箇中間步驟。它允許你編寫純Python代碼,並且只需要做一些小修改,然後將其直接翻譯成C代碼。

你對Python代碼所做的惟一調整就是向每個變量添加類型信息。通常,我們可以像這樣在Python中聲明一個變量:

x = 0.5

使用Cython,我們將向該變量添加一個類型:

cdef float x = 0.5

這告訴Cython,我們的變量是浮點類型,就像我們在C中所做的一樣。對於純Python,變量的類型是動態確定的。Cython中類型的顯式聲明使轉換為C成為可能,因為顯式類型聲明是必須的。

安裝Cython只需要一行簡單的pip命令:

pip install cython

Cython中的類型

使用Cython時,變量和函數分別有不同的類型。

對於變量我們有以下類型:

  • cdef int a, b, c
  • cdef char *s
  • cdef float x = 0.5 (單精度)
  • cdef double x = 63.4 (雙精度)
  • cdef list names
  • cdef dict goals_for_each_play
  • cdef object card_deck

注意所有這些類型都來自C/C++ ! 而對於方法我們有以下類型:

  • def — 常規python函數,僅從python調用。
  • cdef — 不能從python的代碼中訪問Cython的函數。即必須在Cython內調用
  • cpdef — C 和 Python. 可以從C和Python中訪問

瞭解了Cython類型之後,我們就可以直接實現加速了!

如何使用Cython加速你的代碼

我們要做的第一件事是設置Python代碼基準:用於計算數值階乘的for循環。原生Python代碼如下:

def test(x):
y = 1
for i in range(x+1):
y *= i
return y

相同功能的Cython方法看起來非常相似。首先,我們將確保Cython代碼文件具有.pyx擴展名。對代碼本身的惟一更改是,我們已經聲明瞭每個變量和函數的類型。

cpdef int test(int x):
cdef int y = 1
cdef int i
for i in range(x+1):
y *= i
return y

注意到該函數具有cpdef,以確保我們可以從Python中調用它。並且注意到到循環變量i是具有類型的。你需要為函數中的所有變量設置類型,以便C編譯器知道使用哪種類型!

接下來,創建setup.py文件,該文件將Cython代碼編譯為C代碼:

from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules = cythonize('run_cython.pyx'))

並執行編譯:

python setup.py build_ext --inplace

Boom ! 可以看到我們的C代碼已經編譯好了,可以使用了!

你將看到,在Cython代碼所在的文件夾中,你擁有運行C代碼所需的所有文件,包括run_cython.c文件。如果你感興趣,可以查看一下Cython生成的C代碼!

現在我們準備測試我們新的並且超級快的C代碼!查看下面的代碼,它實現了一個速度測試,將原生Python代碼與Cython代碼進行比較。

import run_python
import run_cython
import time
number = 10
start = time.time()
run_python.test(number)
end = time.time()
py_time = end - start
print("Python time = {}".format(py_time))
start = time.time()
run_cython.test(number)
end = time.time()
cy_time = end - start
print("Cython time = {}".format(cy_time))
print("Speedup = {}".format(py_time / cy_time))

代碼非常直觀,我們以與普通Python相同的方式導入文件,並以與普通Python相同的方式運行函數!

Cython幾乎可以讓你在所有原生Python代碼上獲得良好的加速,而不需要太多額外的工作。需要注意的關鍵是,循環次數越多,處理的數據越多,Cython可以提供的幫助就越多。

下表顯示了Cython為不同的數值階乘帶來的加速性能。當數值為10000000的時候,可以看到,我們的Cython加速超過了36倍。

使用Cython讓python代碼的速度提高30倍以上

"

相關推薦

推薦中...