cythonでpythonの計算時間を100倍に 〜(1)cythonのcdefを使う〜

概要

ある単純な足し算の計算速度向上をcythonの2つの機能を使うことで試みた。

(1)cdefを使う
純粋なpythonを使った場合と、cythonを使った場合で計算時間を比較を行った。
その結果、cythonで、cdef利用した場合には(ある条件下では)50倍程度高速化された。
(後述の環境では、5.7[sec]かかっていたものが、100[msec]程となった。)

(2)nogilを使う
cythonのwith nogilを使うことで、GIL(Global Interpreter Lock)の制限解除を行うことが出来る。
(<--pythonAPIは一切使えない、thread切り替えのoverheadがあるなど、注意点もあるとのことだが)
GIL解除+multithreadingで、複数コア環境で更に倍速にすることを試みた。

"(2)"は近日中記載予定。
以下では、"(1)"について記載する。

背景

PyCon2013(2013/09/14-15@新宿/工学院大学)で、cythonを使うと、高速化できるという話を伺ったので、遊んでみようと簡単な例を試してみたという次第。
聴講したセッションは、以下。
「Cythonによる拡張モジュール開発」(Atsuo Ishimoto氏)
http://apac-2013.pycon.jp/zh/program/sessions.html#session-14-1630-rooma0765-ja2-en
資料:http://www.slideshare.net/atsuoishimoto/introduction-to-cython
動画:http://www.youtube.com/watch?v=eo6YcwQZV8k
# 48'06"あたりから、自分の質問も残っていたりw

環境

OS:(仮想系内)Ubuntu10.04
CPU:(仮想系内)Intel(R) Core(TM) i7-2670QM CPU @ 2.20GHz * 2
Mem:(仮想系内)4GB

コード

C,python,cython+pythonのコードを用意し、単純な足し算での速度比較を行ってみた。

C言語

simple_add_pure_c.c

#include <stdio.h>
int main(){
	long long a=0;
	long long i=0;
	for(i=0; i<100000000; i++){
		a += i;
	}
        printf("%lld\n",a);
	return 0;
}
python

simple_add.py

#! /usr/bin/env python
# -*- coding: utf-8 -*-

def simple_add():
    a = 0
    for i in range(100000000):
        a += i
    return a
if __name__ == "__main__":
    print simple_add()
python+cython(cdefなし)

simple_add2.pyx

def simple_add():
    a = 0
    for i in range(100000000):
        a += i
    return a

simple_add2_cython.py(cythonモジュールを呼ぶ人)

#! /usr/bin/env python
# -*- coding: utf-8 -*-

if __name__ == "__main__":
    import simple_add2
    print simple_add2.simple_add()
python+cython(cdefあり)

simple_add2_cdef.pyx

def simple_add():
    cdef int i,a
    a = 0
    for i in range(100000000):
        a += i
    return a

simple_add2_cython.py(cythonモジュールを呼ぶ人)

#! /usr/bin/env python
# -*- coding: utf-8 -*-

if __name__ == "__main__":
    import simple_add2_cdef
    print simple_add2_cdef.simple_add()

実行(ビルド/時間測定)

cのコード --> 2[msec]ほど

$ gcc -O2 simple_add_pure_c.c -o simple_add_pure_c
$ time ./simple_add_pure_c

real	0m0.002s
user	0m0.010s
sys	0m0.000s

python --> 8[sec]ほど

$ time ./simple_add.py

real	0m8.138s
user	0m6.660s
sys	0m1.470s

cython(cdefなし) --> 5[sec]ほど

$ cython simple_add2.pyx
$ export PYTHON_H_DIR=/usr/include/python2.6
$ gcc -c -O2 -fPIC -I$PYTHON_H_DIR simple_add2.c -o simple_add2.o
※最適化オプション(-O2)つけないと、あまり速くならなかった。(あたりまえといえばあたりまえ)
$ gcc -shared -o simple_add2.so simple_add2.o
$ time ./simple_add2_cython.py

real	0m5.516s
user	0m4.290s
sys	0m1.220s

cython(cdefあり) --> 100[msec]ほど

$ cython simple_add.pyx
$ export PYTHON_H_DIR=/usr/include/python2.6
$ gcc -c -O2 -fPIC -I$PYTHON_H_DIR simple_add2_cdef.c -o simple_add2_cdef.o
$ gcc -shared -o simple_add2_cdef.so simple_add2_cdef.o

real	0m0.100s
user	0m0.090s
sys	0m0.020s

参考

Pythonを高速化するCythonを使ってみた
http://kesin.hatenablog.com/entry/20120306/1331043675

追記

2013/09/18 Mi.

"実行"の"最適化オプション"について補足

2013/10/18 Fr.

社内勉強会で発表。指摘事項を反映。
int -> long longに変更(overflowしていたorz)
結果も確認出来るように、print文を追加。
aの初期化漏れ対応。a=0を追加。