Cython筆記

在一些高性能計(jì)算式砂轻,python的速度往往不能滿足需求,你可以使用一些方法提高運(yùn)算速度搔涝,比如使用Numba或者Cython厨喂,這里簡要介紹一下Cython的基本用法,詳細(xì)使用說明還是建議看文檔

本代碼使用jupyter notebook中運(yùn)行庄呈,需要安裝cython

pip install cython

1.基本用法

我們先測試一下杯聚,這是一個斐波那契數(shù)列
from time import time
def fib_loop(n):
    a = 0
    b = 1
    for i in range(n + 1):
        a, b = b, a + b
    return a
start = time()
result = fib_loop(10000)
end = time()
print(f'用時: {end-start}')

[out]:
用時: 0.00708460807800293

接下來我們做一些小小的修改, 我們將指定數(shù)據(jù)類型和返回值類型,代碼分散在多個框內(nèi)抒痒,模擬jupyter notebook的格式

from time import time
%load_ext Cython
1.在jupyer編寫Cython是需要使用%%cython讓jupyer識別該對話框內(nèi)是Cython代碼
2.如果需要使用python調(diào)用Cython函數(shù)則需要使用 cpdef 來定義函數(shù)
3.盡可能事先定義數(shù)據(jù)類型(使用 cdef)幌绍,和返回值類型, 這樣程序會更快的執(zhí)行
%%cython
cpdef int c_fib_loop(int n):
    cdef int a = 0
    cdef int b = 1
    for i in range(n + 1):
        a, b = b, a + b
    return a
start = time()
ret = c_fib_loop(10000)
end = time()
print(f'用時: {end-start}')
[out]:
用時: 0.00011348724365234375

可以看出速度提升了0.0070/0.000113=61(倍)故响,提升巨大(實(shí)際運(yùn)行中每次有誤差)

2.調(diào)用子函數(shù)和numpy

這是一個堆排序的純python代碼
import random
from time import time

# 子函數(shù)
def heapify(arr, n, i): 
    largest = i  
    l = 2 * i + 1     # left = 2*i + 1 
    r = 2 * i + 2     # right = 2*i + 2 
    if l < n and arr[i] < arr[l]: 
        largest = l 
    if r < n and arr[largest] < arr[r]: 
        largest = r 
    if largest != i: 
        arr[i],arr[largest] = arr[largest],arr[i]  # 交換
        heapify(arr, n, largest)

def heapSort(arr): 
    n = len(arr) 
    for i in range(n, -1, -1): 
        heapify(arr, n, i) 
    for i in range(n-1, 0, -1): 
        arr[i], arr[0] = arr[0], arr[i]   # 交換
        heapify(arr, i, 0)
        

arr = [x for x in range(0, 10000)]
random.seed(42)
random.shuffle(arr)

start = time()
heapSort(arr)
end = time()
print(f'用時: {end-start}')
[out]:
用時: 0.14600777626037598

改寫上述算法為Cython代碼

這里我們將子函數(shù) heapify 使用cdef定義傀广,python無法直接調(diào)用該子函數(shù)
%%cython

import numpy as np
cimport numpy as np

cdef c_heapify(np.int32_t[:] arr, np.int32_t n, np.int32_t i):
    cdef np.int32_t l, r, largest
    largest = i
    l = 2 * i + 1     # left = 2*i + 1 
    r = l + 2     # right = 2*i + 2 
    if l < n and arr[i] < arr[l]: 
        largest = l 
    if r < n and arr[largest] < arr[r]: 
        largest = r 
    if largest != i: 
        arr[i], arr[largest] = arr[largest], arr[i]  # 交換
        c_heapify(arr, n, largest)
        
cpdef c_heapSort(np.int32_t[:] arr):
    cdef np.int32_t i, n
    n = len(arr)
    for i in range(n, -1, -1):
        c_heapify(arr, n, i)
    for i in range(n-1, 0, -1):
        arr[i], arr[0] = arr[0], arr[i]   # 交換
        c_heapify(arr, i, 0)
import random
from time import time

arr = [x for x in range(0, 10000)]
random.seed(42)
random.shuffle(arr)
arr = np.array(arr, dtype=np.int32)

start = time()
c_heapSort(arr)
end = time()
print(f'用時: {end-start}')
[out]:
用時: 0.003444671630859375

注意事項(xiàng):

1.cdef定義的函數(shù)無法直接在python代碼中調(diào)用,可以在cpdef定義的函數(shù)中調(diào)用
2.在Cython調(diào)用numpy時需要同時cimport numpy
3.python的int相當(dāng)于int32彩届,numpy的定義需要再后面加_t
比如: np.int32_t    # numpy的32位整形
     np.int32_t[:] # numpy的32位整形一維數(shù)組
     np.int32_t[:, :] # numpy的32位整形二維數(shù)組

詳細(xì)對應(yīng)關(guān)系如下

NumPy dtype          Numpy Cython type         C Cython type identifier

np.bool_             None                      None
np.int_              cnp.int_t                 long
np.intc              None                      int       
np.intp              cnp.intp_t                ssize_t
np.int8              cnp.int8_t                signed char
np.int16             cnp.int16_t               signed short
np.int32             cnp.int32_t               signed int
np.int64             cnp.int64_t               signed long long
np.uint8             cnp.uint8_t               unsigned char
np.uint16            cnp.uint16_t              unsigned short
np.uint32            cnp.uint32_t              unsigned int
np.uint64            cnp.uint64_t              unsigned long
np.float_            cnp.float64_t             double
np.float32           cnp.float32_t             float
np.float64           cnp.float64_t             double
np.complex_          cnp.complex128_t          double complex
np.complex64         cnp.complex64_t           float complex
np.complex128        cnp.complex128_t          double complex

3.編譯

編譯時需要兩個文件伪冰,setup.py文件和.pyx文件
.pyx文件就是剛才編譯的Cython代, 這里我們將該文件命名為c_func.pyx

cpdef int c_fib_loop(int n):
    cdef int a = 0
    cdef int b = 1
    for i in range(n + 1):
        a, b = b, a + b
    return a

setup.py文件

# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   File Name:     setup
   Description :
   Author :        Asdil
   date:          2018/11/28
-------------------------------------------------
   Change Activity:
                   2018/11/28:
-------------------------------------------------
"""
__author__ = 'Asdil'
from distutils.core import setup
from Cython.Build import cythonize

setup(name='c_func',
      ext_modules=cythonize("c_func.pyx"))

# 如果.pyx 文件中使用了cimport numpy as np
# cimport numpy 在pyx不能注釋,可以使用下面代碼替換掉上面的代碼:

# from distutils.core import setup
# from Cython.Build import cythonize
# import numpy as np
# import os
# os.environ["C_INCLUDE_PATH"] = np.get_include()
# setup(name='c_func', ext_modules=cythonize("c_func.pyx"))

# 到這兩個文件的目錄在命令行輸入:
# python setup.py build_ext --inplace

在命令行這兩份文件的目錄輸入:
python setup.py build_ext --inplace
即可編譯


編譯前

編譯后

在編譯完成后目錄中會出現(xiàn)幾個文件
1.build文件樟蠕,這個不用管
2.c_func.c文件, 這個也不用改
3.c_func.cpython-36m-x86_64-linux-gnu.so(根據(jù)你cython版本不同文件名可能不一樣)贮聂,這個文件是我們需要的
4.代碼已經(jīng)上傳到gitlab大家可以下載下來編譯一下,然后在命令行運(yùn)行

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末寨辩,一起剝皮案震驚了整個濱河市吓懈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌靡狞,老刑警劉巖耻警,帶你破解...
    沈念sama閱讀 211,290評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異甸怕,居然都是意外死亡甘穿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人梁钾,你說我怎么就攤上這事∧寂校” “怎么了缸榛?”我有些...
    開封第一講書人閱讀 156,872評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長兰伤。 經(jīng)常有香客問我内颗,道長,這世上最難降的妖魔是什么敦腔? 我笑而不...
    開封第一講書人閱讀 56,415評論 1 283
  • 正文 為了忘掉前任均澳,我火速辦了婚禮,結(jié)果婚禮上符衔,老公的妹妹穿的比我還像新娘找前。我一直安慰自己,他們只是感情好判族,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評論 6 385
  • 文/花漫 我一把揭開白布躺盛。 她就那樣靜靜地躺著,像睡著了一般形帮。 火紅的嫁衣襯著肌膚如雪槽惫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,784評論 1 290
  • 那天辩撑,我揣著相機(jī)與錄音界斜,去河邊找鬼。 笑死合冀,一個胖子當(dāng)著我的面吹牛各薇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播君躺,決...
    沈念sama閱讀 38,927評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼峭判,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了棕叫?” 一聲冷哼從身側(cè)響起林螃,我...
    開封第一講書人閱讀 37,691評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谍珊,沒想到半個月后治宣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體急侥,經(jīng)...
    沈念sama閱讀 44,137評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡砌滞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了坏怪。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贝润。...
    茶點(diǎn)故事閱讀 38,622評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖铝宵,靈堂內(nèi)的尸體忽然破棺而出打掘,到底是詐尸還是另有隱情华畏,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評論 4 329
  • 正文 年R本政府宣布尊蚁,位于F島的核電站亡笑,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏横朋。R本人自食惡果不足惜仑乌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望琴锭。 院中可真熱鬧晰甚,春花似錦、人聲如沸决帖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽地回。三九已至扁远,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刻像,已是汗流浹背穿香。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绎速,地道東北人皮获。 一個月前我還...
    沈念sama閱讀 46,316評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像纹冤,于是被迫代替她去往敵國和親洒宝。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評論 2 348