日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

cython python3_30倍!使用Cython加速Python代码

發布時間:2023/12/20 python 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 cython python3_30倍!使用Cython加速Python代码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原標題:30倍!使用Cython加速Python代碼

作者:George Seif、Thomas Wolf、Lukas Frei

編譯:1+1=6 | 公眾號海外部

前言

你可能經常會一次又一次地聽到關于Python的抱怨,Python跑起來太慢了!

與許多其他編程語言相比,Python的確很慢。

有幾種不同的方法可以使代碼提速:

如果你的代碼是純Python。如果你有一個很大的for循環,你只能使用它,而不能放入矩陣中,因為數據必須按順序處理,那該怎么辦?有沒有辦法加快Python本身的速度?

來吧,看看Cython!

文末下載Cython相關書籍

什么是Cython?

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

Cython 語言是 Python 的一個超集,它包含有兩種類型的對象:

Python 對象就是我們在常規 Python 中使用到的那些對象,諸如數值、字符串、列表和類實例等等。

Cython C 對象就是那些 C 和 C++ 對象,諸如雙精度、整型、浮點數、結構和向量,它們能夠由 Cython 在超級高效的低級語言代碼中進行編譯。

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

x = 0.5

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

cdef float x = 0.5

這告訴Cython,變量是浮點數,就像我們在C中所做的一樣。對于純Python,變量的類型是動態確定的。Cython中類型的顯式聲明使其轉為C代碼成為可能,因為顯式類型聲明需要+。

有很多辦法來測試、編譯和發布 Cython 代碼。Cython 甚至可以像 Python 一樣直接用于 Jupyter Notebook 中。有很多辦法來測試、編譯和發布 Cython 代碼。Cython 甚至可以像 Python 一樣直接用于 Jupyter Notebook 中。

安裝Cython只需要一行pip:

pip install cython

使用Cython需要安裝C語言編譯器,因此,安裝過程會根據你當前的操作系統而有所不同。對于Linux,通常使用GNU C編譯器(gncc)。對于Mac OS,你可以下載Xcode以獲取gncc。而Windows 桌面系統下安裝C編譯器會更復雜。

使用 %load_ext Cython 指令在 Jupyter notebook 中加載 Cython 擴展。

然后通過指令 %%cython,我們就可以像 Python 一樣在 Jupyter notebook 中使用 Cython。

如果在執行 Cython 代碼的時候遇到了編譯錯誤,請檢查 Jupyter 終端的完整輸出信息。

大多數情況下可能都是因為在 %%cython 之后遺漏了 -+ 標簽(比如當你使用 spaCy Cython 接口時)。如果編譯器報出了關于 Numpy 的錯誤,那就是遺漏了 import numpy。

如果你要在在IPython中使用Cython:

首先介紹一下IPython Magic命令。Magic命令以百分號開頭,通常有2種類型:

單行Magic由單個'%'表示,并且僅在一行輸入上操作。

單元格Magic用兩個'%'表示,并在多行輸入上操作。

首先運行下列語句引入Cython:

%load_ext Cython

然后,當運行Cython代碼時,我們需要加入以下Cython 代碼:

%%cython

然后就可以愉快地使用Cython了。

Cython中的類型

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

對于變量,我們有:

cdef int a, b, c

cdef char *s

cdef float x = 0.5 (single precision)

cdef double x = 63.4 (double precision)

cdef list names

cdef dict goals_for_each_play

cdef object card_deck

注意所有這些類型都來自C / C++ !

def - 常規Python函數,僅從Python調用。

cdef - 僅限Cython函數,接受Python對象或C值作為參數,并且可以返回Python對象或C值,cdef函數不能直接在Python中調用。

cpdef - 接受Python對象或C值作為參數,并且可以返回Python對象或C值。

我們可以方便的向C代碼傳遞和返回結果,Cython會自動為我們做相應的類型轉化。

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

如何使用Cython加速代碼

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

deftest(x):

y = 1

fori inrange(x+1):

y *= i

returny

Cython的實現過程看起來非常相似。首先,確保Cython代碼文件具有 .pyx 擴展名。這些文件將被 Cython 編譯器編譯成 C 或 C++ 文件,再進一步地被 C 編譯器編譯成字節碼文件。

你也可以使用 pyximport 將一個 .pyx 文件直接加載到 Python 程序中:

importpyximport; pyximport.install

importmy_cython_module

你也可以將自己的 Cython 代碼作為 Python 包構建,然后像正常的 Python 包一樣將其導入或者發布。不過這種做法需要花費更多的時間,特別是你需要讓 Cython 包能夠在所有的平臺上運行。如果你需要一個參考樣例,不妨看看 spaCy 的安裝腳本:

https://github.com/explosion/spaCy/blob/master/setup.py?source=post_page---------------------------

最終 Python 解釋器將能夠調用這些字節碼文件。對代碼本身的惟一更改是,我們已經聲明了每個變量和函數的類型。

cpdef int test(int x):

cdef int y = 1

cdef int i

fori inrange(x+1):

y *= i

returny

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

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

fromdistutils.core importsetup

fromCython.Build importcythonize

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代碼進行比較。

現在我們準備測試我們新的超快速C代碼了!查看下面的代碼,它執行速度測試以將原始Python代碼與Cython代碼進行比較。

importrun_python

importrun_cython

importtime

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))

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

查看下表,該表顯示了Cython為不同的階乘值提供的速度我們使用Cython獲得了超過36倍的加速!

Cython在NLP中的加速應用

當我們在操作字符串時,要如何在 Cython 中設計一個更加高效的循環呢?spaCy是個不錯的選擇!

spaCy 中所有的unicode字符串(the text of a token, its lower case text, its lemma form, POS tag label, parse tree dependency label, Named-Entity tags…)都被存儲在一個稱為StringStore的數據結構中,它通過一個64位哈希碼進行索引,例如C類型的 uint64_t。

StringStore對象實現了Python unicode字符串與 64 位哈希碼之前的查找映射。

它可以spaCy的任何地方和任意對象進行訪問,例如npl.vocab.strings、doc.vocab.strings或者span.doc.vocab.string。

當某模塊需要在某些標記上獲得更快的處理速度時,可以使用C語言類型的64位哈希碼代替字符串來實現。調用StringStore查找表將返回與該哈希碼相關聯的Python unicode字符串。

但是spaCy能做的可不僅僅只有這些,它還允許我們訪問文檔和詞匯表完全填充的C語言類型結構,我們可以在Cython循環中使用這些結構,而不必去構建自己的結構。

spaCy拓展:

https://spacy.io/api/cython?source=post_page---------------------------

建立一個腳本用于創建一個包含有 10 份文檔的列表,每份文檔都大概含有 17 萬個單詞,采用 spaCy 進行分析。當然我們也可以對 17 萬份文檔(每份文檔包含 10 個單詞)進行分析,但是這樣做會導致創建的過程非常慢,所以我們還是選擇了 10 份文檔。

我們想要在這個數據集上展開某些自然語言處理任務。例如,我們可以統計數據集中單詞「run」作為名詞出現的次數(例如,被 spaCy 標記為「NN」詞性標簽)。

采用Python循環來實現上述分析過程非常簡單和直觀:

importurllib.request

importspacy

withurllib.request.urlopen('https://raw.githubusercontent.com/pytorch/examples/master/word_language_model/data/wikitext-2/valid.txt') asresponse:

text = response.read

nlp = spacy.load('en')

doc_list = list(nlp(text[:800000].decode('utf8')) fori inrange(10))

這段代碼至少需要運行 1.4 秒才能獲得答案。如果我們的數據集中包含有數以百萬計的文檔,為了獲得答案,我們也許需要花費超過一天的時間。

我們也許能夠采用多線程來實現加速,但是在Python中這種做法并不是那么明智,因為你還需要處理全局解釋器鎖(GIL)。在Cython中可以無視GIL的存在而盡情使用線程加速。但不能再使用Python中的字典和列表,因為Python中的變量都自動帶了鎖(GIL)。還好Cython已經封裝了C++標準庫中的容器:deque,list,map,pair,queue,set,stack,vector。完全可以替代Python的dict, list, set等。

我們使用Cython就可以解決這個,但不能再使用Python中的字典和列表,因為Python中的變量都自動帶了鎖(GIL)。還好Cython已經封裝了C++標準庫中的容器:deque,list,map,pair,queue,set,stack,vector。完全可以替代Python的dict, list, set等。

另外請注意,Cython也可以使用多線程!Cython在后臺可以直接調用OpenMP。

https://cython.readthedocs.io/en/latest/src/userguide/parallelism.html?source=post_page---------------------------

現在讓我們嘗試使用spaCy和Cython來加速 Python 代碼。

首先需要考慮好數據結構,我們需要一個C類型的數組來存儲數據,需要指針來指向每個文檔的 TokenC 數組。我們還需要將測試字符(「run」和「NN」)轉成 64 位哈希碼。

當所有需要處理的數據都變成了C類型對象,我們就可以以純C語言的速度對數據集進行迭代。

以下是被轉換成Cython和spaCy的實現:

%%cython -+

importnumpy

fromcymem.cymem cimport Pool

fromspacy.tokens.doc cimport Doc

fromspacy.typedefs cimport hash_t

fromspacy.structs cimport TokenC

cdef struct DocElement:

TokenC* c

int length

cdef int fast_loop(DocElement* docs, int n_docs, hash_t word, hash_t tag):

cdef int n_out = 0

fordoc indocs[:n_docs]:

forc indoc.c[:doc.length]:

ifc.lex.lower == word andc.tag == tag:

n_out += 1

returnn_out

defmain_nlp_fast(doc_list):

cdef int i, n_out, n_docs = len(doc_list)

cdef Pool mem = Pool

cdef DocElement* docs = mem.alloc(n_docs, sizeof(DocElement))

cdef Doc doc

fori, doc inenumerate(doc_list):

docs[i].c = doc.c

docs[i].length = (doc).length

word_hash = doc.vocab.strings.add('run')

tag_hash = doc.vocab.strings.add('NN')

n_out = fast_loop(docs, n_docs, word_hash, tag_hash)

在Jupyter notebook上,這段Cython代碼運行了大概20毫秒,比之前的純Python循環快了大概80倍。

使用Jupyter notebook單元編寫模塊的速度很可觀,它可以與其它 Python 模塊和函數自然地連接:在 20 毫秒內掃描大約 170 萬個單詞,這意味著我們每秒能夠處理高達 8 千萬個單詞。

如果你已經了解C語言,Cython還允許訪問C代碼,而Cython的創建者還沒有為這些代碼添加現成的聲明。例如,使用以下代碼,可以為C函數生成Python包裝器并將其添加到模塊dict中。

%%cython

cdef extern from"math.h":

cpdef double sin(double x)

Cython注意的坑

1、.pyx中用CDEF定義的東西,除類以外對的.py都是不可見的。

2、.c中是不能操作C類型的,如果想在.py中操作C類型就要在.pyx中從python對象轉成C類型或者用含有set / get方法的C類型包裹類。

3、雖然Cython能對Python的str和C的“char *”之間進行自動類型轉換,但是對于“char a [n]”這種固定長度的字符串是無法自動轉換的。需要使用Cython的libc.string .strcpy進行顯式拷貝。

4、回調函數需要用函數包裹,再通過C的“void *”強制轉換后才能傳入C函數。

Cython相關資料(下載)

0、其他:

https://cython.org/?source=post_page---------------------------

1、官方文檔:

2、參考書籍(文末下載):

書籍下載

在后臺輸入(嚴格大小寫)

責任編輯:

總結

以上是生活随笔為你收集整理的cython python3_30倍!使用Cython加速Python代码的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。