続 jemalloc について調べたのでまとめた(ビルドと組み込み方法)
※この記事は3回シリーズのうちの一つ
jemalloc について調べたのでまとめた - zonomasaの日記
続 jemalloc について調べたのでまとめた(ビルドと組み込み方法) - zonomasaの日記
jemalloc の使い方
前回に引き続きjemallocについて調べたのでまとめる。今回は主に使用方法についてである。
前回記事: jemalloc について調べたのでまとめた - zonomasaの日記
jemalloc のインストール
3つのインストール方法を挙げるが、このあと詳細に説明するのは2. の方法である。 (1. も大きくは変わりません。最初にautoconf するかどうかの違い程度)
- canonware.com のStable バージョンを取得しビルドする
- Github から開発版を取得しビルドする
- (Ruby アプリ限定)Github treasure-data/jemalloc-rb を利用する
Github から開発版を取得しビルドする
Github から取得し./configure && make && make install するだけである。ただし./configure の際にオプションを指定することで、jemallocの振る舞いを変えることができる。(後述)
Linux の場合、最後の一行は$ sudo make install
とするべきである。
これにより、Linux であれば/usr/local/lib/libjemalloc.so
もしくは/usr/local/lib/libjemalloc.a
がインストールされる。もちろん、make install 自体は必須ではない。後ほど説明する組み込み方法を見てもらえばわかるが、jemalloc を最もライトに使う方法はLD_PRELOAD を使うものであり、その範囲に限ればライブラリの置き場がどこでも良い。
./configure のオプション
./configure の際にはいくつかのオプションを取ることができる。これによりヒーププロファイリングなどの機能のON/OFF を行う。
ソースパッケージのINSTALL
に記載されているオプションのうち、有用なものを列挙する。
これらのオプションに関わる機能を使用する方法は次回以降に具体的に紹介する。
jemalloc をアプリケーションに組み込む
さて、ここまででjemallocのライブラリわけだが、アプリケーションに組み込まなくてはいけない。ドラクエ風に言えば、make しただけでは意味がない。linkしないとな
。
前回も述べたとおり、jemalloc はmalloc() free() などの標準関数の置き換えと、jemalloc 独自の関数群を併せ持っており、それらの使い方によって組み込み方法も変わってくる。
ここでは3つの方法を挙げる。
- LD_PRELOAD 環境変数を用いる。動作時に標準関数malloc(), free(), realloc() などの関数をjemalloc に置き換える。
- ビルド時にリンクする。アプリケーションのリンクの段階でjemalloc をリンクする。標準関数も置き換えられ、さらにjemalloc 独自の関数群も使用できる。
- --with-jemalloc-prefix=
でビルドし、標準関数とはことなる関数名にする。箇所によってjemalloc を使うかglibc を使うかを決めることができる。
1.
は最も簡単な使い方となる。LD_PRELOAD で指定したライブラリは、動作時に最も優先してシンボルが探される。つまりglibc のmalloc() の前に jemalloc のmalloc() が呼ばれる。
この方式の最も優れている点は、アプリケーションの再ビルドが不要であることだ。malloc() を使っているプログラムなら何でもいい。LD_PRELOAD で指定するだけで、jemalloc の恩恵に預かれる。
この恩恵が受けられるのはCプログラマに限った話ではない。Firefox などのリリースされたアプリケーションや、Rubyアプリケーション などの非C言語、フレームワークに対しても適用できる。
2.
の方法をとることで、jemalloc が用意する独自の関数群が利用できる。特に、jemalloc に対して様々な状態取得、動作モード指示が可能なmallctl() が使えるようになることが大変大きなメリットとなる。
ただしこの組み込み方法をとった場合、移植性を損なわないための工夫が必要である。具体的には非jemalloc 環境に備えて、jemalloc 独自関数を#ifde で囲うなどするとよい。
1.
2.
の方法には当然デメリットもある。これらの方法でjemalloc が適用されるのはプロセス全域である。プロファイル、リークチェック目的の場合、プロセスの規模が大きいと範囲が広くなりすぎてしまい、狙った部分の解析が思うようにできないかもしれない。
それに対して 3.
の手法を用いることで、きめ細やかな適用が可能となる。
jemalloc に含まれるmalloc() がhoge_malloc() (hoge 部分は自由に指定可能)としてライブラリ化される。これにより、実装者が意図的にjemalloc とglibc を切り替えることが可能となる。具体的には、自分の実装した部分だけ、jemalloc をリンクする、もしくは特定のモジュールのメモリ使用についてのみプロファイルを行う、などが可能となる。
以下では、それぞれの組み込み方法を具体的に簡単なコード例で紹介する。
LD_PRELOAD 環境変数を用いた組み込み
leak_memory.c というソースコードを作成し、leak_memory というアプリケーションをビルドする。
なおここで使うjemalloc は次のようにビルドした。
$ ./configure --enable-prof --enable-debug $ make
leak_memory 実行時にLD_PRELOAD 環境変数でjemalloc をロードする。
#include <stdio.h> #include <stdlib.h> #include <string.h> void leak_memory(void){ char *ptr1, *ptr2; ptr1 = (char *)malloc(32); ptr2 = (char *)malloc(1024); (void)ptr1; // For warnings (void)ptr2; // For warnings return; } int main(void) { printf("#### Leak Memory test ####\n"); leak_memory(); return 0; }
ビルドは普通に行う。jemalloc という文字はここまでどこにも現れていない。
$ g++ -g -Wall -Wextra -c leak_memory.c
$ g++ -o leak_memory leak_memory.o
次に実行する。ここでjemalloc が初めて出現する。
$ MALLOC_CONF="prof_leak:true,lg_prof_sample:1" LD_PRELOAD=./lib/libjemalloc.so ./leak_memory
結果、jemalloc によりメモリリークが検出されている。
#### Leak Memory test ####
<jemalloc>: Leak summary: 1056 bytes, 2 objects, 2 contexts
<jemalloc>: Run pprof on "jeprof.7693.0.f.heap" for leak detail
直接リンクによる組み込み
ソースコードは先ほど同様だが、main の終わりでjemalloc 独自のmalloc_stats_print()
関数を読んでみる。ただし移植性を保持するためマクロ(USE_JEMALLOC)で保護する。
#include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef USE_JEMALLOC #include <jemalloc/jemalloc.h> #endif /* USE_JEMALLOC */ void leak_memory(void){ char *ptr1, *ptr2; ptr1 = (char *)malloc(32); ptr2 = (char *)malloc(1024); (void)ptr1; // For warnings (void)ptr2; // For warnings return; } int main(void) { printf("#### Leak Memory test ####\n"); leak_memory(); #ifdef USE_JEMALLOC malloc_stats_print(NULL, NULL, NULL); #endif /* USE_JEMALLOC */ return 0; }
今回はMakefile も書いてみる。具体的には引数にJEMALLOC=yes
が付与された時のみjemalloc をリンクするようにしている。
CC = g++ LINK = g++ ifeq ($(JEMALLOC),yes) CFLAGS = -g -Wall -Wextra -I./include -DUSE_JEMALLOC LDLIBS = -Wl,-rpath,./lib -L./lib -ljemalloc malloc_conf = "prof_leak:true,lg_prof_sample:1" else CFLAGS = -g -Wall -Wextra LDLIBS = endif objs = leak_memory.o exe = leak_memory all:$(exe) $(exe_j) run $(exe):$(objs) $(LINK) -o $@ $< $(LDLIBS) run: @echo "Run leak_memory by jemalloc" MALLOC_CONF=$(malloc_conf) ./$(exe) .c.o: $(CC) $(CFLAGS) $(CDFLAGS) -c $<
実行はjemalloc を使うパターンと使わないパターンを行う。
まずはjemalloc を使わないパターン。
$ make g++ -g -Wall -Wextra -c leak_memory.c g++ -o leak_memory leak_memory.o Run leak_memory by jemalloc MALLOC_CONF= ./leak_memory #### Leak Memory test ####
jemalloc 特有のリークチェックや今回追加したmalloc_stats_print() の出力は見られない。
次にjemalloc を使う場合。
$ make JEMALLOC=yes (git)-[master] - --05/31/14 20:39-- g++ -g -Wall -Wextra -I./include -DUSE_JEMALLOC -c leak_memory.c g++ -o leak_memory leak_memory.o -Wl,-rpath,./lib -L./lib -ljemalloc Run leak_memory by jemalloc MALLOC_CONF="prof_leak:true,lg_prof_sample:1" ./leak_memory #### Leak Memory test #### ___ Begin jemalloc statistics ___ Version: 3.6.0-29-gb4d62cd61b46130b7947c3a427a2b007e7fa0eb8 Assertions enabled Run-time option settings: opt.abort: true opt.lg_chunk: 22 opt.dss: "secondary" opt.narenas: 8 opt.lg_dirty_mult: 3 opt.stats_print: false opt.junk: true opt.quarantine: 0 opt.redzone: false opt.zero: false opt.tcache: true opt.lg_tcache_max: 15 opt.prof: true opt.prof_prefix: "jeprof" opt.prof_active: true opt.lg_prof_sample: 1 opt.prof_accum: false opt.lg_prof_interval: -1 opt.prof_gdump: false opt.prof_final: true opt.prof_leak: true CPUs: 2 Arenas: 8 Pointer size: 8 Quantum size: 16 Page size: 4096 Min active:dirty page ratio per arena: 8:1 Maximum thread-cached size class: 32768 Average profile sample interval: 2 (2^1) Average profile dump interval: N/A Chunk size: 4194304 (2^22) Allocated: 258720, active: 262144, mapped: 8388608 Current active ceiling: 4194304 chunks: nchunks highchunks curchunks 2 2 2 arenas[0]: assigned threads: 1 dss allocation precedence: secondary dirty pages: 64:0 active:dirty, 0 sweeps, 0 madvises, 0 purged allocated nmalloc ndalloc nrequests small: 217760 334 0 0 large: 40960 3 0 3 huge: 0 0 0 0 total: 258720 337 0 3 active: 262144 mapped: 4194304 bins: bin size regs pgs allocated nmalloc ndalloc nrequests nfills nflushes newruns reruns curruns [0..4] 5 80 50 1 4000 50 0 0 1 0 1 0 1 6 96 84 2 8064 84 0 0 1 0 1 0 1 7 112 72 2 8064 72 0 0 1 0 1 0 1 [8..19] 20 1024 63 16 64512 63 0 0 1 0 1 0 1 [21..23] 24 2048 65 33 133120 65 0 0 1 0 1 0 1 [25..27] large: size pages nmalloc ndalloc nrequests curruns 4096 1 2 0 2 2 [6] 32768 8 1 0 1 1 [1008] --- End jemalloc statistics --- <jemalloc>: Leak summary: 1056 bytes, 2 objects, 2 contexts <jemalloc>: Run pprof on "jeprof.8836.0.f.heap" for leak detail
jemalloc のリークチェックと、malloc_stats_print() の出力(長い。。)が得られた。
--with-jemalloc-prefix= を使った組み込み
事前にjemalloc を--with-jemalloc-prefix=
$ ./configure --with-jemalloc-prefix=hoge_ --enable-prof $ make
これでhoge_malloc() というシンボル名になったjemalloc ができる。
これを次のようなコードに適用する。ayac_malloc() はメモリリーク等を調べたい箇所、other_malloc() はまあ大丈夫だろう。という箇所。 (今回はother_malloc()もわざとリークされている)
USE_JEMALLOC の時はayac_malloc() だけjemalloc 内のhoge_malloc() に置き換えている。これにより、調査範囲が狭まるはずだ。
#include <stdio.h> #include <stdlib.h> #include <string.h> #ifdef USE_JEMALLOC #include <jemalloc/jemalloc.h> #define ayac_malloc(sz) hoge_malloc(sz) #else #define ayac_malloc(sz) malloc(sz) #endif /* USE_JEMALLOC */ #define other_malloc(sz) malloc(sz) void leak_memory(void){ char *ptr1, *ptr2; ptr1 = (char *)ayac_malloc(32); ptr2 = (char *)ayac_malloc(1024); (void)ptr1; // For warnings (void)ptr2; // For warnings return; } void other_func(void){ char *ptr; ptr = (char *)other_malloc(32); (void)ptr; // For warnings return ; } int main(void) { printf("#### Leak Memory test ####\n"); leak_memory(); other_func(); #ifdef USE_JEMALLOC hoge_malloc_stats_print(NULL, NULL, NULL); #endif /* USE_JEMALLOC */ return 0; }
Makefile は先ほどと一緒だが1点だけ、24行目が異なる。
HOGE_MALLOC_CONF=$(malloc_conf) ./$(exe)
prefix はオプションを渡す環境変数にまで及んでいる点に注意。
これを実行する。
$ make JEMALLOC=yes 途中省略 ### Leak Memory test #### ___ Begin jemalloc statistics ___ Version: 3.6.0-29-gb4d62cd61b46130b7947c3a427a2b007e7fa0eb8 Assertions enabled Run-time option settings: opt.abort: true opt.lg_chunk: 22 途中省略 --- End jemalloc statistics --- <jemalloc>: Leak summary: 1056 bytes, 2 objects, 2 contexts <jemalloc>: Run pprof on "jeprof.27137.0.f.heap" for leak detail
見事にhoge_malloc() に置き換えた箇所のリークだけ検出されている。(1024byte + 32byte で1056byteが最後に指摘されている。 )
もちろん先ほどと同様、makeのみでビルドした場合、jemalloc は一切の関与を行わない。
終わりに
jemalloc をビルドし、いくつかの方法でアプリケーションに組み込んだ。
組み込み方法には一長一短があり、目的と対象によって使い分けると良いだろう。
次回はjemalloc がもつ様々なオプションをより詳細に調査したい。
- 作者: Michael Kerrisk,千住治郎
- 出版社/メーカー: オライリージャパン
- 発売日: 2012/12/01
- メディア: 大型本
- クリック: 14回
- この商品を含むブログ (5件) を見る
省メモリプログラミング―メモリ制限のあるシステムのためのソフトウェアパターン集 (Software patterns series)
- 作者: ジェイムズノーブル,チャールズウィアー,James Noble,Charles Weir,安藤慶一
- 出版社/メーカー: ピアソンエデュケーション
- 発売日: 2002/06/20
- メディア: 単行本
- 購入: 1人 クリック: 100回
- この商品を含むブログ (35件) を見る