この文書では、Linux カーネルの機能を中心に、 huge page に関連するメモ リ管理の機能についてまとめる。概観とキーワードの把握、およびリファレン スの提供を目的である。特に non-trasparent な huge page と transparent huge page の違いとそれらの境界を把握することをひとつの主眼としている。 そのため、詳細さと網羅性はスコープ外である。
- Intel x86_64 上で Linux を動かす状況を想定している。アーキテクチャ 依存、カーネル依存な事項は、一部触れられている部分もあるが、それ以上 ではない。
- Linux kernel について、バージョン依存、config 依存な部分はスコープ外 である。動作確認は CentOS 7.2 で行った。
Table of Contents
- Huge Page まとめ
- 目次
- 略語, 特殊な言葉使い
- メモリ管理におけるページとそのサイズ
- メモリ管理1: プロセッサ
- メモリ管理2: カーネル
- hugetlbpage support
- Transparet Huge Pages (THP)
- 落ち穂拾い
- References
一般的な略語:
- TLB: translation lookaside buffer
- THP: transparent huge page
以下は、このドキュメントでつかう特殊であろう言葉使い:
- ユーザーアプリケーション: ユーザー空間で動作するプログラム。単にアプ リケーションと書くこともある。また動作している状態を指すときには、ユー ザープロセスと書くかもしれない。
- THP ページ: THP 機能で管理されている huge page ページ。THP の P が page の頭文字なのでページがカブっているが、hugetlb support (=non-transparent huge page) 管理下の huge page と区別するために あえてカブせる。
- huge page: カタカナで書くと "ヒュージページ" があまりにもっさりするので これは英語にしておく。複数形は気にしない。
x86_64 上の Linux では、通常 4KB 単位にメモリを分割し管理していた。こ の 4KB の管理単位をページと呼ぶ。通常、ページサイズはプロセッサにより 決まっている。その後、メモリ容量の増大に伴い、メモリ管理の負荷軽減(メ モリ消費量、TLB miss の削減)のため、プロセッサはより大きなページサイズ をサポートするようになり、現在では複数のページサイズが利用可能なことが 多い。Linux では 4KB を超えるページを huge page と呼んでいる。
なお、Linux では huge page と呼ばれるが、FreeBSD では superpage, Windows ではlarge page と呼ばれるようである [1] [2]。 Intel における呼 称としては、 [3] に 2MB and/or 1GB のページサイズに対して、large page が登場している。
まずハードウェアの観点から始めよう。プロセッサにより、利用できるメモリ のページサイズが決まり、プロセッサのメモリ管理、TLB キャッシュミスの処 理が関係してくる。
Intel x86_64 は TLB miss の処理をハードウェアで行うため [3] , カーネ ル(ソフトウェア)はページエントリの配置を正しく行うこと [4]、そしてコン テキストスイッチの際などに TLB をクリアすることなどが仕事になる。蛇足 だが、ページフォルト処理のうち、スワップアウトの場合もカーネルの仕事で ある。
現状、Intel x86_64 では、4KB に加えて、2MB と 1GB のページサイズをサ ポートしている。例として [5] から抜粋すると、Intel Skylake microarchitecture における TLB は、キャッシュ階層と類似してLevel 1 が instruction と data に分かれており、Level 2 は統合して(unified)使われ ている。L1 instrunction と data はそれぞれ ITLB と DTLB、また L2 TLBは STLB (second level TLB)と呼ばれることもある [7]。カッコ内は entry 数を 示している。
- ITLB: 4KB (128), 2MB/4MB (8/thread) の2種類
- DTLB: 4KB (64), 2MB/4MB (32), 1GB (4) の3種類
- STLB: 4KB と 2MB/4MB の共有 (1536), 1GB (16) の2種類
簡単な算数をしてみると、STLBの 1536 エントリを 4KB で埋めると 6MB 分の メモリが、2MB で埋めると 3GB 分のメモリが TLB に乗ることになる。
同ドキュメントの Haswell microarchitecture からも同様に抜粋する [6]。
- ITLB: 4KB (128), 2MB/4MB (8/thread) の2種類
- DTLB: 4KB (64), 2MB/4MB (32), 1GB (4) の3種類
- STLB: 4KB と 2MB/4MB の共有 (1024)
Haswell から Skylake へ、以下の変更点がある。
- ITLB, DTLB は (associativity を除き)同じ構成
- STLB における "4KB と 2MB/4MB の共有" のエントリ数が 1024 -> 1536 と増加
- STLB に 1GB ページサイズが新設
ちなみに Itanium 2 (IA-64) では、4K, 8K, 16K, 64K, 256K, 1M, 4M, 16M, 256M バイトのページサイズがサポートされている(らしい) [8]。
huge page に関する Linux カーネル の機能(インターフェース)は大きくふた つあり、それぞれ "hugetlbpage support" と "Transparet Huge Pages" (THP) と呼ばれる。まず、"hugetlbpage support" は、プロセッサの複数ペー ジサイズサポートを、実直にユーザ空間から利用可能にする機能である [9]。 この機能の範囲内では、huge page の利用には、次の2つの明示的なステップ が必要となる。
/proc/sys/vm/nr_hugepages
を通じた事前のアロケーションとmmap(2)
への明示的なフラグ追加、または 特殊なファイルシステムhugetlbfs
を通じたメモリ確保
つまり、この hugetlbpage support では huge page に割り当てるメモリ量の 制御がユーザアプリケーション実行前に必要であることと、ユーザアプリケー ション側で huge page を想定したメモリアロケーションが必要となる。例え ば、データベースアプリケーションのように、自身が動作するカーネル管理下 のほぼすべてのリソースを使用し、かつメモリ使用が性能に大きく影響するタ イプのアプリケーションでは huge page を直接利用することが望ましい。一 方で、複数の種類のアプリケーションが共存し、利用可能なリソースが動的に 変わる場合や、アプリケーション自体の対応がなされていない場合には hugetlbpage support からは huge page の恩恵を受けることが出来ない。
そこでもう一つのインターフェースとして Transparent Huge Pages (THP) が 存在する [10]。これは、事前アロケーションが不要であり、またユーザアプ リケーションから huge page 利用の明示がなくとも huge page を仮想メモリ のバックエンドとして割り当てる機能である。そのため、THP の内部では、メ モリ確保要求に応じて huge page 割り当てるだけではなく、huge page 割り 当てが出来ない場合にnormal page 割り当てへのフォールバック、メモリの断 片化の監視、必要に応じたコンパクション(デフラグ)など、複雑な処理が行わ れている。
明示的なインターフェースである hugetlbpage support を見てみる [9]。 "hugetlbpage support" は長ったらしいので簡潔な名前がほしいがいまいち統 一的な明確な呼び名が分からない。シンプルに huge pages と呼びたいが、後 述の THP でも "huge page" という使い方をしている。区別したい場合は non-transparent huge page と呼ばれることもある。
ユーザアプリケーション側で明示的に huge page を指示するので、コードサ ンプルを見るのがわかりやすい。Linux カーネル内のサンプル ([9] の Examples より抜粋) として、次のものがある。
- mmap の例 http://lxr.free-electrons.com/source/tools/testing/selftests/vm/map_hugetlb.c#L65
- shmget/shmat の例 http://lxr.free-electrons.com/source/tools/testing/selftests/vm/hugepage-shm.c
mmap の例から抜粋すると、骨は次のようになる。
#define MAP_HUGETLB 0x40000 /* arch specific */
#define FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB)
int main(void) {
void *addr;
addr = mmap(ADDR, LENGTH, PROTECTION, FLAGS, 0, 0);
unsigned long i;
for (i = 0; i < LENGTH; i++)
*(addr + i) = (char)i;
一方、shared memory (shm) の例は次のとおりである。
#define ADDR (void *)(0x0UL)
#define SHMAT_FLAGS (0)
...
int main(void) {
...
shmid = shmget(2, LENGTH, SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W);
...
shmaddr = shmat(shmid, ADDR, SHMAT_FLAGS);
...
for (i = 0; i < LENGTH; i++) {
shmaddr[i] = (char)(i);
}
まず、もっともざっくりした huge page の使用状況は procfs
/proc/meminfo
から取得できる。
$ cat /proc/meminfo
[snip]
HugePages_Total: 1024
HugePages_Free: 1024
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
[snip]
各項目の意味は次の通りである。
HugePages_Total
: huge page プールのサイズ。HugePages_Free
: プール内の allocate されていないページ数。HugePages_Rsvd
:Rsvd
は reserved の略。プール内で、allocate され 得ることが決まっているが、まだ allocate されていない page 数。 reserved なページは、ページフォルト時にアロケートできることを保証する。HugePages_Surp
: プール内で/proc/sys/vm/nr_hugepages
の数を超えているページ数。/proc/sys/vm/nt_overcommit_hugepages
でコントロールされる。Surp
は surplus (余剰の)の略。Hugepagesize
: デフォルトの huge page のページサイズ
例として、mmap(2)
から huge page をマップした場合を考える。この場合、
CoW (copy-on-write) として動作するため[11]、 mmap
呼び出し時には実メ
モリは確保されず、HugePages_Rsvd
がカウントアップされるが、
HugePages_Free
は変更されない。その後、メモリ領域への書き込みアクセス
が発生すると、page fault 処理の中で実メモリのアロケートが行われ、
HugePages_Rsvd
は減算され、またHugePages_Free
も減算される。
利用する huge page 数の上限は /proc/sys/vm/nr_hugepages
で設定する。例:
$ echo 1024 > /proc/sys/vm/nr_hugepages
NUMA 環境において、huge page 確保のメモリポリシーは、
nr_hugepages_mempolicy
を変更するタスクの NUMA メモリーポリシーで制
御できる。次に例を示す。
$ numactl --interleave <node-list> echo 20 \
/proc/sys/vm/nr_hugepages_mempolicy
メモリポリシーモードは任意のもの bind
, preferred
, local
,
interleave
が使用できる。
注意 huge page はメモリが不足しそうになってもスワップアウトされない。
環境により、2MB と 1GB など、複数のサイズをもつ huge page を同時に利用 することが出来る。
Linux on x86_64 の場合、プロセッサがサポートするページサイズは
/proc/cpuinfo
で確認できる [2] 。 2MB ページサイズサポートの場合は
pse
、1GB ページサイズサポートの場合は pdpe1gb
が出力の flags
に
含まれる。例を示す。
% grep pse /proc/cpuinfo | uniq
flags : [...] pse [...]
各ページサイズの huge page の設定、および使用状況確認には、
/sys/kernel/mm/hugepages/hugepages-<size>kB
以下のファイルを読み書きする。
$ ls /sys/kernel/mm/hugepages/hugepages-2048kB/
free_hugepages nr_hugepages nr_hugepages_mempolicy
nr_overcommit_hugepages resv_hugepages surplus_hugepages
$ ls /sys/kernel/mm/hugepages/hugepages-1048576kB/
free_hugepages nr_hugepages nr_hugepages_mempolicy
nr_overcommit_hugepages resv_hugepages surplus_hugepages
デフォルトのページサイズは /proc/meminfo
の Hugepagesize
に示され
る。また上述の /proc/sys/vm/nr_hugepages
はデフォルトサイズの huge
page のページ数指定である。
NUMA ノードごとの情報は /sys/devices/system/node/node[0-9]*/hugepages/
から
取得できる。
$ ls /sys/devices/system/node/node0/hugepages/hugepages-2048kB/
free_hugepages nr_hugepages surplus_hugepages
free_hugepages
とsurplus_hugepages
は読み取り専用であり、対応ノー ドの利用可能ページ数と overcommit されたページ数を返す。nr_hugepages
は対応するノードの huge page 数を返す。書き込みが発生 した場合、対応するノードの huge page 数がその値に調整される。- ここで、
resv_hugepages
はグローバルな情報なため、このリストには無 いことを注意しておく。
TODO 原文[9]は "Note that the number of overcommit and reserve pages
remain global quantities, ...", とあるが、 直前で surplus_hugepages
は
ノード単位と言っている。ノード単位のほうが正しそうに思えるが要確認
上述の CoW による実メモリアロケーションの際、カーネルはメモリ領域をゼ ロクリアした後にユーザ空間へ渡す。
この時のスタックはページフォルト処理を含め、次のようになる(環境により異なる)。
`<pagefault 発生>` → `handle_mm_fault` →
`do_huge_pmd_anonymous_page` → `clear_page_c_e`
このとき、ページをゼロクリアする際にグローバルな mutex により、実質シ ングルスレッドでの実行になってしまう重大な "バグ" [12][13] がある。こ の状況にあたると、マルチコア環境で System CPU が 1 コアのみ 100% とな り、他のコアが活用されない症状が発生する。このバグは Linux 3.15 で修正 されている。Red Hat Enterprise Linux 7 系列は 3.10.x であるため、この バグの影響を受ける。
2016-07-19 時点でメジャーな他のディストリビューションは、Ubuntu 16.04 (LTS) は kernel 4.4, Debian 8 (jessie) は kernel 3.16, Fedora 24 が kernel 4.5 と、すでに修正済みのバージョンを使用している。
上記の hugetlbpage support では、ユーザアプリケーション自体の対応が必 要になる。その解決策/回避策として Transparent Huge Pages (THP) では、 ユーザアプリケーションの変更なしに、確保されるメモリのバックエンドを通 常(4KB)ページと huge page を自動で切り替えることを主眼としている。
malloc
でメモリを確保しただけで、カーネルが状況に応じて huge page の
実メモリを確保してユーザに見せるマッピングを作ってくれる。ユーザアプリ
ケーション側ではpage size へのアラインメントやその整数倍のメモリ領域を
要求するといったロジックは不要である。その反面、メモリのコンパクション、
マイグレーションなどの処理を負うことになる。
以下、[10] にある設定項目、監視項目を見ていく。
まず THP 全体に対する有効、無効の設定がある。
/sys/kernel/mm/transparent_hugepage/enabled
に以下の値を書き込むこと
で切り替えできる。
$ echo always > /sys/kernel/mm/transparent_hugepage/enabled
$ echo never > /sys/kernel/mm/transparent_hugepage/enabled
同じ項目に対して、もうひとつの可能な値は madvise
である。
$ echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
この場合、THP は madvise(2)
から MADV_HUGEPAGE
が付与された領域の
みで有効になる[14]。特に、huge page の対応をしていないアプリケーション
が確保したメモリ領域に関しては THP は無効になる。
次にページアロケート要求時のデフラグに関する設定を見てみる。THP では、 ページアロケート要求のタイミングで THP が即座にアロケートできない場合 にいくつか選択肢があり、次の値で切り替える。
echo always >/sys/kernel/mm/transparent_hugepage/defrag
echo never >/sys/kernel/mm/transparent_hugepage/defrag
echo madvise >/sys/kernel/mm/transparent_hugepage/defrag
echo defer >/sys/kernel/mm/transparent_hugepage/defrag
always
: THP が有効。この設定では、アプリケーションがメモリを要求し たが THP が即座にアロケートできない時に、アプリケーションを待たせた 状態で reclain/compaction を行い THP をアロケートしようとする。never
: デフラグが無効。madvise
:MADV_HUGEPAGE
の領域のみデフラグを行う。その領域ではalways
と同様。defer
: THP が有効。alyways
と異なり、THP が即座にアロケートでき ない場合はnormal page にフォールバックし、 relcaim/compaction は kcompact に任せる。
NOTE defer
は パッチ [15] (メールの日付は Fri, 26 Feb 2016) で追加
され、 4.6 で入ったもよう[未確認]。CentOS 7.2 (kernel 3.10.0) では設定
できない。
上記のアロケート要求時に加え、バックグラウンドで khugepaged
でもデフ
ラグ処理が行われる。これは次のように有効、無効を設定できる。
echo 0 >/sys/kernel/mm/transparent_hugepage/khugepaged/defrag
echo 1 >/sys/kernel/mm/transparent_hugepage/khugepaged/defrag
ページアロケートに際して、デフォルト設定では、THP はまず huge zero page (read only のゼロ埋めされたページ)を使い、CoW で実メモリをアロケー トしようとする。このように zero page を使うか否かは次のように設定でき る。
echo 0 >/sys/kernel/mm/transparent_hugepage/use_zero_page
echo 1 >/sys/kernel/mm/transparent_hugepage/use_zero_page
NOTE なお、huge page の説明中で言及したページゼロクリア時のカーネ ルバグ (< 3.15) はTHP 利用時にも該当する。
システム全体で使われている THP のサイズは /proc/meminfo
の
AnonHugePages
から取得できる。
$ cat /proc/meminfo | grep AnonHugePages
AnonHugePages: 1533952 kB
プロセスごとの THP サイズは /proc/<PID>/smaps
から AnonHugePages
を合計することで求めることが可能である。しかし smaps
の読み込は高価
なため頻繁なアクセスは負荷をかけることになる。
$ sudo cat /proc/15511/smaps | grep AnonHugePages | \
awk '{sum+=$2; unit=$3;}END{print sum, unit}'
180224 kB
TODO /proc/vmstat
の nr_anon_transparent_hugepages
に huge page
size を掛けると AnonHugePages
になるようだが、前者のドキュメントは検
索した限り存在しないようだ。ソース[16]を読み解けば分かるはずではあるが。
/proc/vmstat
から幾つかピックアップして監視すると良い(かもしれない)
項目を記す。[10] は(完全ではないが)より網羅的なリストを含んでいるので
ここにない項目はまずそちらを参照するのが良い。
thp_fault_alloc
: THP ページがアロケートされたときにインクレメント される。ゼロページ使用時(use_zero_page=1
, CentOS 7.2 ではデフォル ト)には、CoW として、最初の書き込みアクセス時にページフォルト処理の 中でアロケートされ、 この数値がカウントアップされる。thp_collapse_alloc
:khugepaged
が一つの THP ページにまとめられる normal pages の範囲を見つけ、実際に THP ページをアロケートできた時に インクレメントされる。thp_fault_fallback
: ページフォルト時に THP ページアロケートに失敗 してsmall page (=nomal page) にフォールバックした時にインクレメント される。thp_split
: huge page が normal page に分割されるときにインクレメン トされる。compact_stall
: ユーザプロセスがメモリコンパクションでストールする ときにインクレメントされる。compact_pages_moved
: ページが移動された時にインクレメントされる。 この値が急速に上昇している場合は、データコピーが頻発しているため、 そのコストが TLB miss のコストを上回っている可能性がある。
NOTE thp_split
は kernel 4.5 で thp_split_page
,
thp_split_fail
, thp_split_pmd
の3つに分割された。RHEL 7 系列の場合
は kernel 3.10 の Document [19] を参照すると良い。
CPUID 情報(CPUID(4)
, /dev/cpu/CPUNUM/cpuid
)をテキスト形式変換して
表示してくれるコマンドである。CentOS 7.2 では cpuid
パッケージに含ま
れる。この中には Level ごとの TLB の情報も含まれる。
Portable Hardware Locality (hwloc) に含まれるユーティリティーである。
CentOS 7.2 では hwloc
パッケージには text 版の lstopo-no-graphics
が、hwloc-gui
に lstopo
が含まれている。NUMA ノードやコア、キャッ
シュの改装をグラフィカルに表示してくれる。しかし TLBの情報は載らないよ
うだ。公式サイトにいくつかのサンプルが置いてある [18]。
$ cat /proc/meminfo | grep Huge
AnonHugePages: 1540096 kB
HugePages_Total: 1024
HugePages_Free: 1024
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
$ cat /proc/vmstat | egrep '(hugepage|thp|compact)'
nr_anon_transparent_hugepages 752
compact_migrate_scanned 183738178
compact_free_scanned 18461965374
compact_isolated 28161384
compact_stall 49123
compact_fail 34706
compact_success 14417
thp_fault_alloc 4350509
thp_fault_fallback 404443
thp_collapse_alloc 125552
thp_collapse_alloc_failed 17
thp_split 131276
thp_zero_page_alloc 37
thp_zero_page_alloc_failed 84
-
[1] Page (computer memory) - Wikipedia, the free encyclopedia https://en.wikipedia.org/wiki/Page_(computer_memory)#Huge_pages
-
[2] Hugepages - Debian Wiki https://wiki.debian.org/Hugepages
-
[3] 仮想メモリ方式の分類 http://www.nminoru.jp/~nminoru/programming/arch/virtual_memory.html
-
[4] 2章 Linuxカーネル - メモリ管理1 http://www.slideshare.net/mao999/2-1-58047548
-
[5] Intel® 64 and IA-32 Architectures Optimization Reference Manual http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-optimization-manual.html
- CHAPTER 2 64 AND IA-32 PROCESSOR ARCHITECTURES
- 2.1 THE SKYLAKE MICROARCHITECTURE
- 2.1.3 Cache and Memory Subsystem
- Table 2-5. TLB Parameters of the Skylake Microarchitecture
-
[6] 同 [5]
- CHAPTER 2 64 AND IA-32 PROCESSOR ARCHITECTURES
- 2.2 THE HASWELL MICROARCHITECTURE
- 2.2.4 Cache and Memory Subsystem
- Table 2-10. TLB Parameters of the Haswell Microarchitecture
-
[7] Translation lookaside buffer - Wikipedia, the free encyclopedia https://en.wikipedia.org/wiki/Translation_lookaside_buffer の中では、ITLB1, DTLB1, TLB2 という書き方をしているがここ以外で見ない記法のようだ。
-
[8] Intel® Itanium® 2 Processor Reference Manual for Software Development and Optimization http://www.intel.com/design/itanium2/manuals/251110.htm "3.1 Page Size Supported"
-
[9] Huge TLB page https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt
Linux kernel ツリーに含まれている Huge TLB サポート機能のドキュメン ト。ユーザ/ユーザランド目線で丁寧に書かれており、平易なスタートから、 NUMA の説明まで充実している。一読をおすすめする。
-
[10] Transparent Hugepage Support https://www.kernel.org/doc/Documentation/vm/transhuge.txt
同じく Linux kernel ツリーから THP のドキュメント。こちらはカーネル 開発者向きの話と sysfs 等のユーザランドへのインターフェースが混じっ ている。用語も統一感がなく、少々読みにくいものになっている。THP 自体 が刻々と更新されているのも一因かも知れないが。
-
[11] Huge pages part 1 (Introduction) [LWN.net] https://lwn.net/Articles/374424/
-
[12] Linux 3.15 - Linux Kernel Newbies https://kernelnewbies.org/Linux_3.15#head-73741bc00ce13369767b5a47dfbcfe07ad68f507
-
[13] kernel/git/torvalds/linux.git - Linux kernel source tree https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=8382d914ebf72092aa15cdc2a5dcedb2daa0209d
huge page のゼロクリア時に、単一 mutex だった箇所に複数 mutex を導入 してハッシュで分散させる修正。
-
[14] madvise(2): give advice about use of memory - Linux man page http://linux.die.net/man/2/madvise
-
[15] LKML: Mel Gorman: [PATCH 1/1] mm: thp: Set THP defrag by default to madvise and add a stall-free defrag option https://lkml.org/lkml/2016/2/26/647
-
[16] Linux/mm/vmstat.c - Linux Cross Reference - Free Electrons http://lxr.free-electrons.com/source/mm/vmstat.c
-
[17] Linux 4.5 - Linux Kernel Newbies https://kernelnewbies.org/Linux_4.5#head-0a1509acd283c7caf60491d4ab56fb8c16b7b91b
-
[18] Portable Hardware Locality (hwloc) https://www.open-mpi.org/projects/hwloc/lstopo/
-
[19] kernel/git/torvalds/linux.git - Linux kernel source tree https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/vm/transhuge.txt?h=v3.10
以下、本文から参照していないが、huge page まわりで有用な文献をリストする。
-
Linux tuning to improve PostgreSQL performance http://www.slideshare.net/PostgreSQL-Consulting/linux-tuning-to-improve-postgresql-performance
PostgreSQL を Linux で使う時のチューニング項目集。NUMA, huge page, swappiness, disk IO (kernel buffer) に関して推奨のチューニング方法が コンパクトにまとまっている。条件や症状が書かれている項目もある。
-
JVM Anatomy Park #2: Transparent Huge Pages https://shipilev.net/jvm-anatomy-park/2-transparent-huge-pages/
huge page の有無によるマイクロベンチマーク。 その他、JVM の
-XX
オプションの説明も有用。