Cython(3) 型宣言をしてみる


2022年 07月 07日

Cythonは動的型付け言語なので変数の型宣言はなく、適当に処理してくれるので、変数の型を意識することがない。
しかし、C言語では、全ての変数に型を指定する必要があり、Cythonを高速に、つまりCのように高速に動かすには、変数にCのような型宣言をしなければいけない。

次が、前回のCythonプログラムである。

# [Cython]  1からnまでの和を求めるだけのテストプログラム
def sumtest(n):
    print("Cython sumtest2.sumtest({}) が呼ばれた".format(n))
    sum = 0
    for i in range(1,n+1):
            sum += i
    return sum

このプログラムの変数は、引数のn、ローカル変数のi,sum の3つであり、すべて整数である。
ということで、int型の宣言を追加してみた。

# [Cython]  1からnまでの和を求めるだけのテストプログラム
def sumtest(int n):
    cdef int sum, i
    
    print("Cython sumtest3.sumtest({}) が呼ばれた".format(n))
    sum = 0
    for i in range(1,n+1):
        sum += i
    return sum

これをコンパイルして、動かしてみた。

$ python3 sumtestcheck.py
Cython sumtest2.sumtest(1000000) が呼ばれた
sumtest(1000000) = 1784293664    経過時間 = 0.00038695335388183594 秒

経過時間が 0.0259 秒から0.000387 秒になったので、67倍高速になったので、かなり高速になったことが分かる。
しかし、なぜか総和が1784293664になってしまった。500000500000が正しい値であるが、何が起きたのであろうか。

int型の最大値は2147483647なので、どうやら和が途中でオーバーフローしてしまったようだ。
Ubuntu/Linuxの場合、limits.h に各型の値の最大値・最小値が明記されている。

ということで、intではなくlongに変更しよう。64ビット版のCなら、long の最大値は9223372036854775807であり、オーバーフローは起きないはずである。

# [Cython]  1からnまでの和を求めるだけのテストプログラム
def sumtest(int n):
    cdef int    i
    cdef long   sum
    
    print("Cython sumtest3.sumtest({}) が呼ばれた".format(n))
    sum = 0
    for i in range(1,n+1):
        sum += i
    return sum
$ python3 sumtestcheck.py
Cython sumtest3.sumtest(1000000) が呼ばれた
sumtest(1000000) = 500000500000    経過時間 = 0.00038552284240722656 秒

経過時間はほとんど同じで、総和も正しく計算できたみたいだ。
これで、Pythonを簡単に67倍速にできることが分かった。

と言っても、非常に簡単な整数の和の計算をしたに過ぎない。
次回は、浮動小数点数について試してみよう。

Pythonの整数

ところで、Pythonでは全然オーバーフローが起きなかったが、大きな整数の計算はどうなっているだろうか。

Pythonで2のべき乗がどうなるか、試してみた。

In [1]: for i in range(10,200,10):
   ...:     print(2**i)
   ...: 
1024
1048576
1073741824
1099511627776
1125899906842624
1152921504606846976
1180591620717411303424
1208925819614629174706176
1237940039285380274899124224
1267650600228229401496703205376
1298074214633706907132624082305024
1329227995784915872903807060280344576
1361129467683753853853498429727072845824
1393796574908163946345982392040522594123776
1427247692705959881058285969449495136382746624
1461501637330902918203684832716283019655932542976
1496577676626844588240573268701473812127674924007424
1532495540865888858358347027150309183618739122183602176
1569275433846670190958947355801916604025588861116008628224

こんな感じで、2の190乗まで表示してみた。実際には、2の1000乗でも1000000乗でも表示できる。

Pythonの整数には、上限値は存在しない任意長整数を使っているので、無限に大きな数を扱うことができるが、現実にはメモリの制限や、計算時間の問題があるが、そこまで大きな数を扱う必要はまず無いだろう。