Cythonのパフォーマンス

2chの「pythonがこの先生きのこるには」というスレでfibでCythonがかなり遅い結果になっていたので調べてみた。


Pythonは、2.5.2、Cythonは、0.9.8.1.1を使用。


以下は、psycoを使用した通常のPython。 1.157秒。psycoを外すと 19.844秒。

# pyfib.py
def fib(n): 
    if n == 0 or n == 1: 
        return n 
    else: 
        return fib(n-1) + fib(n-2)
import time
import psyco
from pyfib import fib

psyco.full()

t1 = time.time()

for i in range(36): 
    print "n=%d => %d" % (i, fib(i)) 

t2 = time.time()
print t2 - t1, 'sec'  #=> 1.15700006485 sec

以下は、Cythonでfib.pydを作成し、それをPythonで利用。 0.922秒。Cythonの方がpsycoよりも若干速い。

# fib.pyx
def fib(int n):
    return cfib(n)

cdef long cfib(int n): 
    if n == 0 or n == 1: 
        return n 
    else: 
        return cfib(n-1) + cfib(n-2)
import time
from fib import fib

t1 = time.time()

for i in range(36): 
    print "n=%d => %d" % (i, fib(i)) 

t2 = time.time()
print t2 - t1, 'sec'  #=> 0.921999931335 sec

結論としてきちんとしたコードを書けば、Cythonの方が細かい指定できる分、自動で全て行ってしまうpsycoよりもパフォーマンスを調整できる可能性があると思われる。フィボナッチ数を計算する単純な例でもCythonの方が速くなったのでCythonは期待できる。マニュアルを見る限りかなりしっかりしているし、Pyrexに比べかなり制限もなくなってきていると思われるのでかなり使えそう。


以下は、純粋なCとCythonの比較。測定した速度はそれぞれ、Cが0.628秒で、Cythonが0.841秒。約1.34倍の速度差なのでオーバーヘッドとしては妥当だろうか。

/* fib.c */
#include <stdio.h>
#include <time.h>

long fib(int n)
{
    if (n==0 || n==1)
        return n;
    else
        return fib(n-1) + fib(n-2);
}

int main()
{
    int i;
    clock_t t1, t2;

    t1 = clock();
    for (i=0; i < 36; i++) {
        printf("n=%d => %d\n", i, fib(i));
    }
    t2 = clock();
    printf("%f sec\n", (double)(t2-t1)/1000.);  /*=> 0.628280 sec */
}
# fib.pyx
import time

t1 = time.clock()

cdef long fib(int n): 
    if n == 0 or n == 1: 
        return n 
    else: 
        return fib(n-1) + fib(n-2)

for i from 0 <= i < 36:
    print "n=%d => %d" % (i, fib(i))

t2 = time.clock()
print t2 - t1, 'sec'  #=> 0.841137996589 sec