fast-woothee-pythonを書いていました

少し前の話ですが、 8月頃にwoothee-goにプルリクを送った 流れから中の人に提案していただいて、 woothee-rust をwootheeファミリに追加していただけました。

fast_woothee

その流れからwootheeまわりを調べていると、 速度を求めてか woothee-rustのRubyバインディングfast_woothee というものがありまして、 ほぉという感じで眺めていました。 少し時が経って、じゃぁ実際どれくらい速いのか確認したくなったので、 確認してみるとRubyネイティブ実装の woothee-ruby より遅かったのです。 どうしたものかと思って、 woothee-rustのdatasetまわりのコードをlazy_staticでラップして高速化 しました。 結果2倍ほど高速になりました 。

[before]
$ ruby bench.rb
                 user     system      total        real
woothee      2.270000   0.020000   2.290000 (  2.309573)
fast-woothee  2.710000   0.020000   2.730000 (  2.758268)

[after]
$ ruby bench.rb
                 user     system      total        real
woothee      2.240000   0.020000   2.260000 (  2.261497)
fast-woothee  1.100000   0.010000   1.110000 (  1.134596)

fast-woothee-python

さてここからが本題なのですが、fast_wootheeの流れを受けて Pythonでも同じようなことをしたくなったのでさっくり実装してみました。

fast-woothee-python

モチベーションとしては、以下のようなものでした。

  • Pythonでも同等の高速化が期待できる
  • 一定API自体は固まっておりほぼ変わらないので、 一度拡張を書いてしまえばwoothee-rustをメンテナンスすることで変更に対応でき、 メンテナンスコストを低減できる。
  • RustなPython拡張の事例作り(今後プロダクションで使っていきたい)

結果はこちらも 約3倍程度の高速化が実現 できました。

## benchmarker:         release 4.0.1 (for python)
## python version:      2.7.13
## python compiler:     GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)
## python platform:     Darwin-15.6.0-x86_64-i386-64bit
## python executable:   ./.virtualenvs/py2713/bin/python
## cpu model:           Intel(R) Core(TM) i5-5257U CPU @ 2.70GHz
## parameters:          loop=100000, cycle=1, extra=0

##                                       real    (total    = user    + sys)
uap                                    0.0813    0.0800    0.0800    0.0000
uap(non-cache)                        64.3114   63.5200   63.2800    0.2400
woothee                                3.2219    3.1200    3.1000    0.0200
fast-woothee                           1.0989    1.1000    1.0900    0.0100

## Ranking                               real
uap                                    0.0813  (100.0) ********************
fast-woothee                           1.0989  (  7.4) *
woothee                                3.2219  (  2.5) *
uap(non-cache)                        64.3114  (  0.1)

## Matrix                                real    [01]    [02]    [03]    [04]
[01] uap                               0.0813   100.0  1352.1  3964.4 79132.1
[02] fast-woothee                      1.0989     7.4   100.0   293.2  5852.5
[03] woothee                           3.2219     2.5    34.1   100.0  1996.1
[04] uap(non-cache)                   64.3114     0.1     1.7     5.0   100.0

uap-python (uap)はキャッシュを内部で持っていて、 一度パースした結果をすぐに返せるので非常に高速になってます。 キャッシュを使わない場合も参考に載せてます。uap(non-cache)

implementation

fast-woothee-pythonはRustコードを書くことでPython拡張が書ける PyO3/pyo3 を使っています。 同じ用途として dgrunwald/rust-cpython が存在するのですが、 私が試した時はPython2向けのビルドがエラーになってしまったのと、 Python3でベンチマークをとってみてもオーバーヘッドがどちらも同じくらいだったので、 pyo3を使った経緯があります。 pyo3の方が活発に開発が行われていますが、rust-cpythonも開発が止まっているわけではないので、 今は状況が変わっているかもしれません。

parse メソッドと is_crawler メソッドの2つしかなく最小限の構成になっているので、 RustなPython拡張の雛形として参考にしていただければと。