Hy(Hylang)をCythonでコンパイル

Hythonコミュニティの id:rokujyouhitoma です。こんにちわ!

Hythonコミュニティ

Hythonコミュニティは不定期的でHython(廃村)-hack-a-thonと称して、廃村めぐりを開催しています。Pythonで知り合った人たちがほとんどです。

前回参加時は、たしか...猿島?の要塞跡を見学しました。それ廃村じゃ無いじゃん、廃墟じゃん。というツッコミはあるかと思います。
前々回は、奥多摩の廃村だったと記憶してます。山奥でさまよってやっとのこと住居跡を見つけたのですよ!

で?

さて、現在開催中のPython その2 Advent Calendar 2015でcsakatokuさんがHy記事で無双(?)してるのをみてHyに興味をもちました。

Hythonと語呂がちょうどあってそうだし、Pythonで書かれたLisp系処理系のHy(Hylang)を触らないのはもったいないかなー。

さて、前置きが長くなりましたがHythonコミュニティと全く関係ない、本題を...

HyをCythonでコンパイル

というわけでPythonで書かれたLisp処理系の1つHyについてです。

HyPythonで書かれてるので、Hyの処理系自体をCythonで*.soにすれば、速くなるかな?というのを試します。

環境

$ Python -V
Python 2.7.9
$ uname -s
Darwin

検証コード

実際にインストールしたい人は下記でインストール可能です。

cd <上記のリポジトリ(feature/cythonizeブランチ)>
pip install -e .

結果

Cythonizeは成功。作業時間はこの記事書くの含めて2h。実作業には1.5hくらい。

  • 単純な実行だと速度改善する。
  • ただし、本家Hyだとこけないテストが、自前のCythonize版ではそもそもエラー&コケる...。

単純なコード

例えば、map処理

オリジナルをCPythonで
time hy -c "(map inc [1 2 3])"
hy -c "(map inc [1 2 3])"  0.92s user 0.07s system 98% cpu 1.001 total
Cythonバージョン
$ time hy -c "(map inc [1 2 3])"
hy -c "(map inc [1 2 3])"  0.69s user 0.11s system 79% cpu 0.997 total
単純なコードでの結果

33%速度改善がみられた。Cythonのアノテーションなしで33%の改善なら悪くない。

オリジナをCPythonで Cythonバージョン
0.92s 0.69s 1.333

コケる

オリジナルをCPythonで

$ time make test
(省略)
Ran 419 tests in 57.520s

OK
make test  53.09s user 3.23s system 96% cpu 58.337 total
Cythonバージョン
$ time make test
(省略)
Ran 419 tests in 41.521s

FAILED (errors=1, failures=4)
make: *** [test] Error 1
make test  37.97s user 3.21s system 96% cpu 42.509 total
ERROR

Errorが1件。致命的。
うーん。not iteratableなオブジェクトが来てる...なぜ。

======================================================================
ERROR: NATIVE: test the iteration behavior of cons
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/rokujyouhitoma/.virtualenvs/cythonize-hy/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/Users/rokujyouhitoma/.virtualenvs/cythonize-hy/lib/python2.7/site-packages/nose/util.py", line 620, in newfunc
    return func(*arg, **kw)
  File "/Users/rokujyouhitoma/workspace/github.com/hy/tests/native_tests/cons.hy", line 291, in test_cons_iteration
  File "hy/models/cons.py", line 85, in __iter__ (hy/models/cons.c:2403)
    for i in iterator:
  File "hy/models/cons.py", line 79, in genexpr (hy/models/cons.c:2186)
    iterator = (i for i in self.cdr)
  File "hy/models/cons.py", line 85, in __iter__ (hy/models/cons.c:2403)
    for i in iterator:
  File "hy/models/cons.py", line 79, in genexpr (hy/models/cons.c:2186)
    iterator = (i for i in self.cdr)
  File "hy/models/cons.py", line 85, in __iter__ (hy/models/cons.c:2403)
    for i in iterator:
  File "hy/models/cons.py", line 79, in genexpr (hy/models/cons.c:2186)
    iterator = (i for i in self.cdr)
  File "hy/models/cons.py", line 85, in __iter__ (hy/models/cons.c:2403)
    for i in iterator:
  File "hy/models/cons.py", line 79, in genexpr (hy/models/cons.c:2186)
    iterator = (i for i in self.cdr)
  File "hy/models/cons.py", line 85, in __iter__ (hy/models/cons.c:2403)
    for i in iterator:
  File "hy/models/cons.py", line 79, in genexpr (hy/models/cons.c:2156)
    iterator = (i for i in self.cdr)
TypeError: 'HyInteger' object is not iterable
FAIL

Failは4件。そこそこ致命的。

前半2つはkoanというマクロが起因でコケてる。
後半2つは、前半2つと同様koanか、ideasというマクロが起因でコケる。

======================================================================
FAIL: tests.test_bin.test_bin_hy_stdin
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/rokujyouhitoma/.virtualenvs/cythonize-hy/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/Users/rokujyouhitoma/workspace/github.com/hy/tests/test_bin.py", line 60, in test_bin_hy_stdin
    assert "monk" in ret[1]
AssertionError: 
    (0, u'\x1b[?1034h=> => ', u'hy 0.11.0 using CPython(default) 2.7.9 on Darwin\n  File "<input>", line 1, column 1\n\n  (koan)\n  ^----^\nHyMacroExpansionError: expanding `koan\': <cyfunction koan_macro at 0x10311e1d0> is not a Python function\n\n\n') = run_cmd("hy", '(koan)')
    assert (0, u'\x1b[?1034h=> => ', u'hy 0.11.0 using CPython(default) 2.7.9 on Darwin\n  File "<input>", line 1, column 1\n\n  (koan)\n  ^----^\nHyMacroExpansionError: expanding `koan\': <cyfunction koan_macro at 0x10311e1d0> is not a Python function\n\n\n')[0] == 0
>>  assert "monk" in (0, u'\x1b[?1034h=> => ', u'hy 0.11.0 using CPython(default) 2.7.9 on Darwin\n  File "<input>", line 1, column 1\n\n  (koan)\n  ^----^\nHyMacroExpansionError: expanding `koan\': <cyfunction koan_macro at 0x10311e1d0> is not a Python function\n\n\n')[1]
    

======================================================================
FAIL: tests.test_bin.test_bin_hy_cmd
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/rokujyouhitoma/.virtualenvs/cythonize-hy/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/Users/rokujyouhitoma/workspace/github.com/hy/tests/test_bin.py", line 65, in test_bin_hy_cmd
    assert ret[0] == 0
AssertionError: 
    (1, u'\x1b[?1034h', u'  File "<stdin>", line 1, column 1\n\n  (koan)\n  ^----^\nHyMacroExpansionError: expanding `koan\': <cyfunction koan_macro at 0x10311e1d0> is not a Python function\n\n') = run_cmd("hy -c \"(koan)\"")
>>  assert (1, u'\x1b[?1034h', u'  File "<stdin>", line 1, column 1\n\n  (koan)\n  ^----^\nHyMacroExpansionError: expanding `koan\': <cyfunction koan_macro at 0x10311e1d0> is not a Python function\n\n')[0] == 0
    assert "monk" in (1, u'\x1b[?1034h', u'  File "<stdin>", line 1, column 1\n\n  (koan)\n  ^----^\nHyMacroExpansionError: expanding `koan\': <cyfunction koan_macro at 0x10311e1d0> is not a Python function\n\n')[1]
    

======================================================================
FAIL: tests.test_bin.test_bin_hy_icmd
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/rokujyouhitoma/.virtualenvs/cythonize-hy/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/Users/rokujyouhitoma/workspace/github.com/hy/tests/test_bin.py", line 78, in test_bin_hy_icmd
    assert "monk" in output
AssertionError: 
>>  assert "monk" in u'\x1b[?1034h=> => '
    assert "figlet" in u'\x1b[?1034h=> => '
    

======================================================================
FAIL: tests.test_bin.test_bin_hy_icmd_file
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/rokujyouhitoma/.virtualenvs/cythonize-hy/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/Users/rokujyouhitoma/workspace/github.com/hy/tests/test_bin.py", line 87, in test_bin_hy_icmd_file
    assert "Hy!" in output
AssertionError: 
>>  assert "Hy!" in u'\x1b[?1034h=> => '

まとめ

  • Hyとっても面白い。コードを読み進めたい& Land of Lisp のコードをHyで書きたい。
  • 簡単にCythonでビルドできた。Cython有能。
  • ちょっと検証コードが少ないので、もう少し試そうかな。
  • PyPyでHyの速度は気になるから、PyPyでも試してみたい。