ネコと和解せよ

pypyで高速化したらメモリリークしたお話

Pythonクラスを関数内で定義すると、pypyでの実行時にメモリリークが発生して実行速度が遅くなる。

以下のコードをPythonで実行する。

import time

class Leak:
    def proc(self):
        class Inner:
            def something(self):
                i=1+1
        for _ in range(10000):
            a=Inner()
            a.something()
        return

class Safe:
    class Inner:
        def something(self):
            i=1+1
    def proc(self):
        for _ in range(10000):
            a=Safe.Inner()
            a.something()
        return
print("Safe:")
a=Safe()
for _ in range(5):
    s=time.time()
    for _ in range(100):
        a.proc()
    print(time.time()-s)
print("Leak:")
a=Leak()
for _ in range(5):
    s=time.time()
    for _ in range(100):
        a.proc()
    print(time.time()-s)

Pythonなら結果はSafeでもLeakでも変わらない。

$ python3 pypybench.py
Safe:
0.1891028881072998
0.1931455135345459
0.1875746250152588
0.18657183647155762
0.19033050537109375
Leak:
0.18032550811767578
0.18199753761291504
0.1795341968536377
0.1779332160949707
0.18030810356140137

Pypyで実行。

pypy3 pypybench.py
Safe:
0.0041353702545166016
0.002458333969116211
0.0013346672058105469
0.0010471343994140625
0.0009150505065917969
Leak:
0.11974382400512695
0.2023618221282959
0.30752086639404297
1.0994617938995361
1.716996431350708

だんだん遅くなる。インスタンスをdelしても遅くなるので、クラス定義がクリーンアップされずに溜まっているご様子。
解決策としては、関数内でクラスを定義しないこと。