Microsoft が microsoft/mimalloc という新しいアロケータを公開しました。
https://www.microsoft.com/en-us/research/publication/mimalloc-free-list-sharding-in-action/ によると、
One increasing use-case for allocators is as back-end implementations of
languages, such as Swift and Python, that use reference counting to
automatically deallocate objects. We present mimalloc, a memory allocator
that effectively balances these demands, ...
ということで、 Swift や Python のような参照カウントを利用している言語向けにチューニングされているらしいです。
pyperformance という実際のライブラリを利用したベンチマークスイートを実行してみたところ、確かにいくつかのベンチマークが速くなっていました。
$ ./python -m pyperf compare_to pymalloc.json mimalloc.json -G --min-speed=3
Faster (10):
- spectral_norm: 199 ms +- 1 ms -> 182 ms +- 1 ms: 1.10x faster (-9%)
- mako: 23.2 ms +- 0.2 ms -> 21.6 ms +- 0.3 ms: 1.07x faster (-7%)
- regex_effbot: 3.62 ms +- 0.04 ms -> 3.41 ms +- 0.03 ms: 1.06x faster (-6%)
- json_dumps: 16.7 ms +- 0.2 ms -> 15.9 ms +- 0.1 ms: 1.05x faster (-4%)
- crypto_pyaes: 162 ms +- 1 ms -> 155 ms +- 1 ms: 1.05x faster (-4%)
- json_loads: 38.5 us +- 1.8 us -> 37.1 us +- 1.2 us: 1.04x faster (-4%)
- float: 156 ms +- 2 ms -> 150 ms +- 2 ms: 1.04x faster (-4%)
- pathlib: 29.2 ms +- 0.6 ms -> 28.1 ms +- 0.6 ms: 1.04x faster (-4%)
- scimark_fft: 498 ms +- 6 ms -> 483 ms +- 3 ms: 1.03x faster (-3%)
- regex_v8: 27.7 ms +- 0.1 ms -> 26.8 ms +- 0.4 ms: 1.03x faster (-3%)
Benchmark hidden because not significant (50): ...
できるだけ多くのユーザーにいろんなアプリを動かした時のパフォーマンスやメモリ使用量を見てもらいたいので、使い方をまとめておきます。 (ただし mimalloc は MADV_FREE を使うので見た目 RSS が大きくなることには注意してください。)
以下の手順は Linux 用になります。私は Ubuntu 19.04 で確認しました。
インストール
cmake と make を使います。次の例では CMAKE_INSTALL_PREFIX
を指定することで $HOME/local/lib
にインストールしていますが、デフォルトの /usr/local/lib
にインストールしたい場合は -D CMAKE_INSTALL_PREFIX=$HOME/local
を外し、 make install
の代わりに sudo make install
になります。
$ git clone https://github.com/microsoft/mimalloc.git
$ cd mimalloc
$ vim include/mimalloc-types.h # enable MI_STAT=2
$ mkdir build
$ cd build
$ cmake .. -D CMAKE_INSTALL_PREFIX=$HOME/local
...
$ make -j8
...
$ make install
-- Install configuration: "Release"
-- Installing: /home/inada-n/local/lib/mimalloc-1.0/libmimalloc.so
-- Installing: /home/inada-n/local/lib/mimalloc-1.0/libmimalloc.a
-- Installing: /home/inada-n/local/lib/mimalloc-1.0/include/mimalloc.h
-- Installing: /home/inada-n/local/lib/mimalloc-1.0/cmake/mimalloc-config.cmake
-- Installing: /home/inada-n/local/lib/mimalloc-1.0/cmake/mimalloc-config-version.cmake
-- Installing: /home/inada-n/local/lib/mimalloc-1.0/cmake/mimalloc.cmake
-- Installing: /home/inada-n/local/lib/mimalloc-1.0/cmake/mimalloc-release.cmake
-- Installing: /home/inada-n/local/lib/libmimalloc.so
-- Installing: /home/inada-n/local/lib/mimalloc-1.0/mimalloc.o
利用方法
ビルド済みの Python を利用する場合は、 LD_PRELOAD=$HOME/local/lib/libmimalloc.so python
のようにして実行することで、 glibc malloc をオーバーライドして利用することができます。
Python をビルドするときに静的リンクしたい場合は cp $HOME/local/lib/mimalloc-1.0/mimalloc.o .
してから、 configure 後に Makefile の次の部分を修正して mimalloc.o を追加すると libpython.a と python 実行ファイルに mimalloc をリンクすることができるはずです。
LIBRARY_OBJS= \
$(LIBRARY_OBJS_OMIT_FROZEN) \
Python/frozen.o \
mimalloc.o
ただしリンクしただけではデフォルトの pymalloc が使われます。 環境変数 PYTHONMALLOC=malloc
をすることで mimalloc によってオーバーライドされた malloc を利用することができます。
環境変数 MIMALLOC_SHOW_STATS=1
を設定しておくとプログラム終了時に rss などの統計情報が出力されるので、それをみて mimalloc がリンクされていることを確認することができます。 (詳しい統計情報を表示したい場合は、 mimalloc をインストールする前にソースコードから MI_STAT
を探して #define MI_STAT 1
に書き換えます。)
ちなみに pymalloc で統計情報を出力するには PYTHONMALLOCSTATS=1
です。こちらは終了時ではなくOSから追加でメモリを割り当ててもらう (mmap) たびに統計を stderr に出力します。これを使うことで pymalloc が使われなくなったことを確認することができます。
# mimalloc を静的リンクした場合
# PYTHONMALLOC=malloc を指定し忘れると pymalloc が使われる
$ MIMALLOC_SHOW_STATS=1 PYTHONMALLOCSTATS=1 ./python -c '[*map(str, range(1000))]'
...
# bytes in allocated blocks = 468,256
# bytes in available blocks = 456,256
153 unused pools * 4096 bytes = 626,688
# bytes lost to pool headers = 11,088
# bytes lost to quantization = 10,576
# bytes lost to arena alignment = 0
Total = 1,572,864 # ここまで pymallocの統計、以降 mimalloc の統計
heap stats: peak total freed unit count
elapsed: 0.012 s
process: user: 0.013 s, system: 0.000 s, faults: 0, reclaims: 884, rss: 8.0 mb
# PYTHONMALLOC=malloc を指定した場合、 pymalloc が使われなくなって mimalloc の統計だけが表示される。
$ MIMALLOC_SHOW_STATS=1 PYTHONMALLOCSTATS=1 PYTHONMALLOC=malloc ./python -c '[*map(str, range(1000))]'
heap stats: peak total freed unit count
elapsed: 0.009 s
process: user: 0.010 s, system: 0.000 s, faults: 0, reclaims: 868, rss: 7.9 mb