Ching-Chuan Chen's Blogger

Statistics, Machine Learning and Programming

0%

PyCharm and Cython

這篇文章主要介紹PyCharm跟Cython

先寫下我的檔案架構

1
2
3
4
5
6
7
8
.
+-- main.py
+-- fib
| +-- __init__.py
| +-- cfib.c
| +-- cfib.h
| +-- fib.pyx
| +-- setup.py

每一個檔案的用途:

  1. main.py是主程式
  2. __init__.py是套件引用檔案
  3. cfib.c是用c寫成的fib函數
  4. cfib.h是cfib的header檔,供fib.pyx參照
  5. fib.pyx是Cython檔
  6. setup.py則是拿來編譯整個Cython的

cfib.c內容如下:

1
2
3
4
5
6
7
8
9
#include "cfib.h"

unsigned long fib(unsigned long n) {
unsigned long a=0, b=1, i, tmp;
for (i=0; i<n; ++i) {
tmp = a; a = a + b; b = tmp;
}
return a;
}

cfib.h內容如下:

1
2
3
4
5
6
#ifndef __CFIB_H__
#define __CFIB_H__

unsigned long fib(unsigned long n);

#endif

基本上cfib.ccfib.h就是很標準的c程式寫法,就不贅述,主要是拿來比performance用的

下面則是fix.pyx檔案,fib_c是定義引用的c函數

fib_cython是未優化過的cython程式,基本上就是python樣子

fib_cython_optimized是加上型別的cythoin程式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
cdef extern from "cfib.h":
unsigned long _fib "fib"(unsigned long n)

def fib_c(n):
''' Returns the nth Fibonacci number.'''
return _fib(n)

def fib_cython(n):
'''Returns the nth Fibonacci number.'''
a, b = 0, 1
for i in range(n):
a, b = a + b, a
return a

def fib_cython_optimized(unsigned long n):
'''Returns the nth Fibonacci number.'''
cdef unsigned long a=0, b=1, i
for i in range(n):
a, b = a + b, a
return a

__init__.py檔案內容:

1
from .fib import *

setup.py檔案內容如下:

1
2
3
4
from distutils.core import setup, Extension
from Cython.Build import cythonize

setup(ext_modules = cythonize(Extension(name="fib", sources=["cfib.c", "fib.pyx"])))

有了這幾個檔案之後,我們先來設定PyCharm

打開PyCharm,到settings => Tools => External Tools

然後點+,新增一個工具,設定如下:

Porgram設定$ModuleSdkPath$Arguments設定$FilePath$ build_ext --inplaceWorking directory設定$FileDir$

這樣基本上就大功告成了,然後回到Project對setup.py點右鍵,選External Tools,選CythonBuild就可以看到開始跑了

如果設定對的話,就可以看到編譯成功的訊息

最後就可以來跑跑看啦,main.py如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def fib_python(n):
'''Returns the nth Fibonacci number.'''
a, b = 0, 1
for i in range(n):
a, b = a + b, a
return a

if __name__ == '__main__':
print("##### check result #####")
import fib
print("fib(47) in python:", fib_python(47))
print("fib.fib_c(47):", fib.fib_c(47))
print("fib.fib_cython(47):", fib.fib_cython(47))
print("fib.fib_cython_optimized(47):", fib.fib_cython_optimized(47))

print("\n##### performace benchmark #####")
import timeit
python_setup = "from __main__ import fib_python"
cython_setup = "import fib"
print("Python code: ", timeit.timeit('fib_python(47)', setup=python_setup), "seconds")
print("Cython code: ", timeit.timeit('fib.fib_cython(47)', setup=cython_setup), "seconds")
print("Optimized Cython code: ", timeit.timeit('fib.fib_cython_optimized(47)', setup=cython_setup), "seconds")
print("C code: ", timeit.timeit('fib.fib_c(47)', setup=cython_setup), "seconds")

輸出結果就會是下面這樣:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
C:\Users\*****\Miniconda3\envs\idp\python.exe D:/Dropbox/Data/programming/Python/Cython/1_fib/main.py
##### check result #####
fib(47) in python: 2971215073
fib.fib_c(47): 2971215073
fib.fib_cython(47): 2971215073
fib.fib_cython_optimized(47): 2971215073

##### performace benchmark #####
Python code: 2.9352622053858113 seconds
Cython code: 1.7331176511158422 seconds
Optimized Cython code: 0.14643933094340067 seconds
C code: 0.11884286952119272 seconds

Process finished with exit code 0