1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

eMMC書き込み量を減らすのは commit か dirty_writebackか? 実測比較

1
Last updated at Posted at 2026-03-03

はじめに

IoT エッジデバイスの長期運用では、eMMC への書き込み量は信頼性と寿命を左右する重要な要素です。
本記事では、アプリケーション側で過剰なfsync()を抑制した状態を前提として、

Linuxカーネルパラメータの調整により、eMMC書き込み量をどこまで削減できるのか

を実測で検証しました。

評価軸は以下の2つです。

評価ポイント 評価軸 対象
ページキャッシュ制御 vm.dirty_background_ratio / vm.dirty_ratio / vm.dirty_writeback_centisecs / vm.dirty_expire_centisecs
ジャーナル制御 ext4 マウントオプション commit

本検証の結果、

  • ext4 commitを5秒 → 60秒に延長しても削減率は3.7%
  • dirty_writeback最適化では約28%削減

という差を確認しました。


書き込みの流れとチューニングポイント

対策を正しく評価するために、アプリケーションの書き込みが物理ストレージに到達するまでの経路を整理します。ページキャッシュからeMMCへの書き込み経路は 非同期パス と 同期パス の2系統があります。

image.png

パス 経路 vm.dirty_writeback の影響 ext4 commit の影響
非同期 fwrite → fflush → ページキャッシュ → writeback → journal → eMMC あり あり
同期 fsync → ページキャッシュ → 即時フラッシュ → eMMC なし なし

fsync() はページキャッシュ上の変更を同期的に書き出して完了を待つため、writeback による集約(dirty_* のコアレッシング)が効きにくいと考えられます。また ext4 の commit= は「最大遅延」を決めるパラメータであり、fsync() はこのタイマを待たずに永続化を進めるため、commit変更の効果は出にくいです。

2つのチューニングポイントはいずれも非同期パスにのみ作用します。どちらが影響が大きいかを今回の実測で確認します。


用語解説

Dirty(ダーティページ)

ページキャッシュ上にあり、まだブロックデバイスへ書き出されていない変更済みのメモリページです。

アプリが write() を呼ぶと、データは即座に eMMC へは書かれず、まず DRAM 上のページキャッシュに保存されます。この「未書き出し状態」のページが Dirty ページです。

$ grep Dirty /proc/meminfo
Dirty:   2048 kB

Dirty 量が増えすぎると、カーネルは書き込みプロセスをスロットリング(リソースの流量を制限してシステムがパンクするのを防ぐ制御)して書き込みを抑制します。


Writeback(ライトバック)

Dirty ページをブロックデバイスへ書き出すカーネルの非同期処理です。kworker スレッドが担当します。

writeback は以下の条件でトリガーされます。

トリガー 条件
量超過 Dirty 量が dirty_background_ratio を超えた
定周期 dirty_writeback_centisecs タイマー発火
期限切れ dirty_expire_centisecs を超えた古い Dirty ページが存在

複数の write() が同じページに対して連続して発生した場合、writeback が発火するまでの間に後の書き込みが前の書き込みを上書きし、最終的に1回の物理 I/O にまとまります。これをコアレッシングといいます。これは例えると、小さな液滴や気泡が互いにくっついて、一つの大きな塊になる現象のようなイメージです。


fflush

C 標準ライブラリ(stdio)が保持するユーザー空間バッファをカーネルのページキャッシュへ転送します。ストレージへの永続化は保証しません。

fflush() : ユーザーバッファ → ページキャッシュ(DRAM 止まり)

fsync

対象ファイルのダーティページを即座にストレージへ書き出すよう強制します。writeback スケジューラを待たず同期的に実行され、完了後はストレージへの永続化が保証されます。

fsync() : ページキャッシュ → eMMC(同期確定)

fsync() を多用すると vm.dirty_* によるコアレッシング効果が失われます。本記事のワークロードでは、アプリケーション側でこの fsync() 呼び出し頻度をあらかじめ最適化済みの状態を前提としています。


vm.dirty_background_ratio

バックグラウンド writeback を開始する Dirty 量の閾値(システム RAM に対する割合)です。

例)RAM 1GB × dirty_background_ratio 10% = 約 100MB

この値を超えると、カーネルがバックグラウンドで非同期に(eMMCへ)書き出しを開始します。これによりアプリの書き込みはブロックされません。


vm.dirty_ratio

アプリの書き込みを同期的に抑制しスロットリングする Dirty 量の上限(RAM に対する割合)です。dirty_background_ratio より大きい値を設定します。

例)RAM 1GB × dirty_ratio 20% = 約 200MB
パラメータ 役割 超えたときの動作
dirty_background_ratio バックグラウンド writeback 開始ライン kworker が書き出しを開始(アプリは継続)
dirty_ratio アプリ書き込み抑制ライン write() がスリープしてスロットリング発生。アプリが急に重くなる現象が発生。

vm.dirty_writeback_centisecs

writeback デーモンを起動する周期です。単位は centisecs(1/100 秒)です。

500  → 5秒周期(デフォルト)
3000 → 30秒周期

長くするほど Dirty ページが蓄積されてからまとめて書き出されるため、コアレッシング効果が増します。一方で、電源断時の未書き込みデータ滞留時間も増加します。


vm.dirty_expire_centisecs

Dirty ページを「古い」と見なし、次回 writeback 時に強制的に書き出す期限です。単位は centisecs です。

3000  → 30秒(デフォルト)
10000 → 100秒

この値を延ばすと、同一ページへの複数更新が1回の writeback にまとめられやすくなります。


ext4 commit

ext4 のジャーナル(jbd2)がコミットを行う最大遅延時間(秒)です。マウントオプションとして指定します。

commit=5  → 5秒ごとにジャーナルコミット(デフォルト)
commit=60 → 最大60秒遅延

commit周期を延ばすと、短時間に発生するメタデータ更新をまとめてジャーナルに書けるため、メタデータ I/O 回数を削減できる可能性があります。ただし、コミット前に電源断が発生するとジャーナル上のメタデータが失われるリスクがあります。


実験設計

実験は、ページキャッシュ制御やジャーナル制御によるeMMC書き込み負荷量を評価するため、実験環境の制御パラメータを変えた状態で、アプリケーション評価ツールによるワークロード中のセクタ書き込み量を評価しました。

実験環境

項目
ボード ARMボード(Armadillo IoT G3)
RAM 1GB
eMMC 4GB
ファイルシステム ext4(data=ordered)
カーネル 6.1系

評価ワークロード

今回の検証では、実運用に近い固定ワークロードを再現するために、以下仕様の自作の負荷生成ツールを使用しました。

  • 5000個のデータファイル(64KB)を周期更新
  • 各データファイルを100スレッドで並列更新
  • 各データファイルを30秒周期で書き込みfflush()
  • 管理情報ファイル(440KB)のみ30秒周期でfsync()

この構成は、IoTエッジ機器における

  • センサデータファイルの周期更新
  • 状態ファイルの上書き
  • 管理情報の同期保存

を模擬したものです。

この目的は、ワークロードを固定した状態でカーネルパラメータのみを変更して因子を切り分けることです。
データファイルはページキャッシュ経由のため vm.dirty_* の影響を受けます。管理情報ファイル は fsync() のため vm.dirty_* の影響を受けません。

ワークロード生成ツールの詳細実装は以下を参照してください。

計測方法

書き込み量の指標として、/sys/block/mmcblk2/stat の sectors_written を使用しました。

評価ポイントの条件毎に、

  • ワークロード実行中に
  • 1分間の sectors_written の差分を取得 × 10回繰り返し平均を算出

としました。

1 sector = 512 byteとし、

  • MiB/min = sectors / 2048
  • GiB/day = MiB/min × 1440 / 1024

で換算しています。

ワークロードは固定として、カーネルパラメータのみを変更因子として評価しました。


実測結果

Default(ベースライン)

# カーネルデフォルト値
vm.dirty_background_ratio    = 10
vm.dirty_ratio               = 20
vm.dirty_writeback_centisecs = 500   # 5秒
vm.dirty_expire_centisecs    = 3000  # 30秒
# ext4 commit=5(デフォルト)
指標
平均書き込み量 62.98 MiB/min
推定 eMMC 書き込み量 88.56 GiB/day

Pattern A — バランス型

writeback 周期と expire を2倍に緩め、dirty_ratio を少し拡大した「バランス型」です。

sysctl -w vm.dirty_background_ratio=10
sysctl -w vm.dirty_ratio=30
sysctl -w vm.dirty_writeback_centisecs=2000  # 20秒
sysctl -w vm.dirty_expire_centisecs=6000     # 60秒
指標 vs Default
平均書き込み量 52.22 MiB/min −17%
推定 eMMC 書き込み量 73.47 GiB/day

writeback 周期を 5秒 → 20秒、expire を 30秒 → 60秒 に延ばすことで、データ ファイルへの複数回書き込みが1回の writeback にまとめられる頻度が増しました。


Pattern B — 集約型

さらにコアレッシングを積極化した「集約型」です。expire を 100秒に延ばし、複数周分の更新をまとめて書き出す設定です。

sysctl -w vm.dirty_background_ratio=15
sysctl -w vm.dirty_ratio=40
sysctl -w vm.dirty_writeback_centisecs=3000  # 30秒
sysctl -w vm.dirty_expire_centisecs=10000    # 100秒
指標 vs Default
平均書き込み量 45.19 MiB/min −28%
推定 eMMC 書き込み量 63.54 GiB/day

dirty_expire_centisecs を長くすると「古いdirtyを強制的に書き出すまでの猶予」が伸びるため、同一ページへの上書きが発生しやすくなり、結果としてコアレッシングが効きやすくなりました。


commit=60(ジャーナル周期のみ変更)

vm.dirty_* はデフォルトのまま、ext4 の commit オプションのみを 5秒 → 60秒 に変更しました。

# 動的に再マウント
mount -o remount,commit=60 /

# /etc/fstab で永続化する場合
# /dev/mmcblkXpY / ext4 defaults,commit=60 0 1
指標 vs Default
平均書き込み量 60.63 MiB/min −3.7%
推定 eMMC 書き込み量 85.34 GiB/day

削減率はわずか 3.7% に留まりました。commit の変更だけではほとんど効果がありません。


Pattern B + commit=60(組み合わせ)

Pattern B に commit=60 を加えた場合です。

指標 vs Default
平均書き込み量 45.14 MiB/min −28%
推定 eMMC 書き込み量 63.47 GiB/day

Pattern B 単体(45.19 MiB/min)と実質同等でした。commit=60 の追加効果はほぼゼロです。


比較表

パターン 平均書き込み (MiB/min) GiB/day Default比
Default 62.98 88.56
Pattern A(バランス型) 52.22 73.47 −17%
Pattern B(集約型) 45.19 63.54 −28%
commit=60 のみ 60.63 85.34 −3.7%
Pattern B + commit=60 45.14 63.47 −28%

考察

なぜ vm.dirty_* の影響が大きいのか

本ワークロードの書き込みを経路別に整理します。

ファイル 書き込み方法 ページキャッシュ経由 dirty_* の影響
データ ファイル(5,000個) fflush,fclose() のみ ○ あり
管理情報(1個、440KB) fsync() あり × なし(即時物理 I/O) なし

書き込み量の大半を占める データ ファイルはページキャッシュを経由するため、writeback タイミングを制御することでコアレッシング効果が発揮されます。

dirty_expire_centisecs = 100秒(Pattern B)の場合:

  データ ファイル更新 1周目(0〜30秒)  → 各ページがダーティ化
  データ ファイル更新 2周目(30〜60秒) → 同ページを上書き(まだダーティ状態)
  データ ファイル更新 3周目(60〜90秒) → 同ページを上書き(まだダーティ状態)
  expire 到達(〜100秒後)            → 最新データのみ eMMC へ書き出し
                                        ↑ 3周分の write が 1回の物理 I/O に集約

dirty_expire_centisecs=10000(100秒)により、データファイルの30秒周期の更新が最大3周分まとめて書き出されるようになりました。

なぜ ext4 commit の影響が小さいのか

ext4 のジャーナル(jbd2)は主にメタデータ(inode、ディレクトリエントリ等)を管理します。data=ordered モード(ext4 デフォルト)では、データブロック自体はジャーナルに書かれず直接 eMMC へ書き出されます。

data=ordered モードでのファイル更新フロー:

  1. データブロックを writeback(ページキャッシュ → eMMC)← 大半の I/O
  2. ジャーナルにメタデータを記録                          ← ごく少量
  3. journal commit                                        ← commit 周期が作用するのはここ

本ワークロードはファイルの新規作成・削除ではなく既存ファイルのデータブロック上書きが主体であるため、ジャーナルに記録されるメタデータは少量です。commit 周期を延ばしてもデータブロックの書き込み量は変わりません。削減できるのはメタデータ分のジャーナル書き込みのみであり、全体に対して微小なため約 3.7% の削減に留まりました。

コアレッシング効果の限界

管理情報ファイルへの fsync() は dirty_writeback による制御の外にあります。30秒周期の fsync() は 1分間の計測窓内で2回発生し、1回 440KB を即時書き出します。

管理情報 の書き込み: 440KB × 2回/min = 0.86 MiB/min
データ ファイルの書き込み:  62.98 − 0.86 ≈ 62.12 MiB/min(Default 時)

データファイルが全体の約 98.6% を占めるため、管理情報分はほぼ無視できる規模です。逆に言えば、今回の削減効果(Pattern B で −28%)はほぼすべて データファイルへのコアレッシング改善によるものです。


トレードオフ

dirty_* を緩めることには明確なリスクがあります。それは電源断時に RAM 上の Dirty ページは失われるため、dirty_expire_centisecs が長いほど失われうるデータ量が増加することです。

パターン 削減効果 電源断時の最大データ損失期間
Default 最大 30秒
Pattern A −17% 最大 60秒
Pattern B −28% 最大 100秒
commit=60 のみ −3.7% 最大 60秒(メタデータのみ)
Pattern B + commit=60 −28% 最大 100秒

Pattern B を採用する場合、データ消失リスクを最小化するために以下の電源断・瞬停対策が望ましい。

対策 内容
UPS(無停電電源装置) 停電時に電源を維持し、正常シャットダウンで sync を完了させる
瞬停対策(コンデンサ・バッテリー) 数秒〜数十秒の瞬時電圧低下に対してダーティページのフラッシュ完了まで電源を保持
シャットダウンスクリプト 電源断検知時に sync コマンドを実行してダーティページを強制フラッシュ

今回の結論

今回のワークロードでは、eMMC書き込み量に最も影響していたのはext4 commitではなく、dirty_writeback制御でした。

観点 結論
影響因子 vm.dirty_* がジャーナル(ext4 commit)より影響が大きい
commit=60 の効果 約 3.7%(誤差レベル)
最大削減率 Pattern B で約 28%削減
追加組み合わせ効果 Pattern B + commit=60 はほぼ Pattern B 単体と同等

削減効果(Default比)は以下の通りです。

優先度 施策 削減率
1 vm.dirty_* チューニング 最大 28%
2 ext4 commit 延長 約 3.7%

eMMC 寿命試算(本記事のワークロード)

ここでは、eMMC の理論総書き込み許容量(TBW)を 120TB と仮定し、
日次書き込み量(GiB/day)から単純計算で推定寿命を算出しました。

計算式
推定寿命(年) ≒ (122,880 ÷ GiB/day) ÷ 365

※ 計測値はバイナリ単位(MiB/min → GiB/day)のため、1TiB = 1,024GiB として換算
120TiB の eMMC 理論寿命に対して:

パターン GiB/day 推定寿命(日) 推定寿命(年)
Default 88.56 1,387 約 3.8 年
Pattern A 73.47 1,673 約 4.6 年
Pattern B 63.54 1,934 約 5.3 年

アプリケーション最適化済みのワークロードに Pattern B を追加適用することで、推定寿命がさらに約 1.5 年延びる計算になります。


今回わかったこと

  • 書き込み削減の主因は「ジャーナル」ではありませんでした。
  • データ主体のワークロードでは writeback 制御の方が影響が大きいことが分かりました。
  • commit延長は、メタデータ主体のワークロードでない限り効果は限定的と考えられます。

次回確認すること

次回は、今回の結論をさらに深めるために以下を確認したいと思います。

  • fsync() の周期を変えた場合(毎回 / 30秒 / writeback周期と同期)の相互作用
  • 書き込みサイズや更新局所性を変えた場合(小サイズ高頻度 / 大サイズ低頻度)
  • nr_dirty / nr_writeback / sectors written の時系列可視化による因果の補強

注意事項

  • 本記事の設定値・計測値は特定の実験環境での結果であり、環境によって異なります。
  • dirty_* パラメータを緩めると電源断耐性が低下するため、UPS・瞬停対策が整った環境での適用を推奨します。
  • ext4 commit 延長も同様に、コミット前の電源断でジャーナル上のメタデータが失われるリスクがあります。

参考資料

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?