概要
Raspberry Pi 4 上で 6種のメーカー (Kingston, Transcend, SUNEAST, Gigastone, KIOXIA, San Disk) のSDメモリカードの読み込み/書き込み性能を比較した。 その結果、下記のような傾向を確認することができた。
- Raspberry Pi 4 (Raspberry Pi OS) では、カタログの最大書き込み・読み込み性能に達することは難しい。
- Raspberry Pi 4 (Raspberry Pi OS) では、ファイルシステムを介することによる書き込み性能が大きく低下するが、読み込み性能はそこまでの低下は見られなかった。
- SDメモリカードによって特性が異なり、ランダムな書き込みによって性能が大きく低下するものとそうではないものがあった。
背景
SDメモリカードは、家庭用ゲーム機やデジタルカメラなど外部ストレージとして広く使われている。 SDメモリカードは、SD Associationによって定めたSD標準規格を基に様々なメーカーから販売されている。 各メーカーは、大容量化・高耐久性・省コスト化など様々な工夫によって他のメーカーとの差別化を目指している。 そのため、同じ要領のSDメモリカードでも異なる特性を持つことがある。 そこで複数のmicroSDカードの読み込み・書き込み性能を測定することで、それらの傾向を確認する。
目的
実行環境
Raspberry Pi 4 Model B (Raspberry Pi 4) は USBフラッシュドライブ経由でRaspberry Pi OSを起動させる。 測定用のSDカードは Raspberry Pi 4の microSD card slot に挿入する。
ここで使用するRaspberry Pi 4のスペックについて、必要な情報だけ抜粋したものを下記に示す。
項目 | Raspberry Pi 4 |
---|---|
CPU | Cortex-A72 (ARM v8) 1.5GHz |
メモリ | 4GB LPDDR4-3200 |
OS | Raspberry Pi OS (Dec 5th 2023) |
ケース | 陽極酸化アルミニウム製ヒートシンクケース |
今回の調査には、下記6種類の未使用のmicroSDカードを6種類を使用し、Raspberriy Pi 4上で計測する。 (カタログに記載されていないものは、空欄としている)
Canvas Select Plus microSD | Transcend USD300S | SUNEAST ULTIMATE PRO | GIGASTONE Prime 600X | EXCERIA HIGH ENDURANCE | SanDisk MAX ENDURANCE | |
---|---|---|---|---|---|---|
型番号 | KF-C4032-7I | TS32GUSD300S-A | SE-MSDU1032C180 | GJMX-32GU190R | KEMU-A032G | SDSQQVR-032G-JN3ID |
スピードクラス | C10 | C10 | C10 | C10 | C10 | C10 |
UHSスピードクラス | U1 | U1 | U1 | U1 | U1 | U3 |
ビデオスピードクラス | V10 | V10 | V10 | V30 | ||
アプリケーションパフォーマンスクラス | A1 | A1 | A1 | A1 | ||
インタフェース | UHS-I | UHS-I | UHS-I | UHS-I | UHS-1 | UHS-1 |
容量 | 32GB | 32GB | 32GB | 32GB | 32GB | 32GB |
最大読み出し速度 | 100 MB/s | 100 MB/s | 98 MB/s | 90 MB/s | 100 MB/s | 100 MB/s |
最大書き込み速度 | 85 MB/s | 90 MB/s | 30MB/s | 40 MB/s |
RAWデバイスのパフォーマンスをベンチマークする
まずは、簡単な性能測定として、ファイルシステムと Page Cache を介さないような測定パターン "RAWデバイスに対してダイレクト I/O" でベンチマークする。ここで計測するパターンは下記の4種類とする。
- I/O サイズを 1 MB でシーケンシャルな書き込み
- I/O サイズを 1 MB でシーケンシャルな読み込み
- I/O サイズを 4 KB でランダムな書き込み
- I/O サイズを 4 KB でランダムな読み込み
これらがどのようなI/Oフローでデータアクセスするかを考える。
fio から I/Oサイズごとにread/writeが発行されると、VFS からブロックレイヤにリクエストを要求する。ブロックレイヤに渡された bio は ioスケジューラなどによってマージやスプリットされる。ioスケジューラからディスパッチされた ioリクエストはブロック単位に書き込み・読み込みをする。また、連続するブロックに対するアクセスはマルチブロック読み込み・書き込みコマンドとして発行することもできる。
I/O サイズを 1 MB でシーケンシャルな書き込み
sudo fio --name=write_bandwidth_test \ --filename=/dev/mmcblk0 --filesize=16G \ --time_based --ramp_time=2s --runtime=60 \ --ioengine=sync --direct=1 --bs=1M --iodepth=1 numjobs=1 \ --rw=write --fsync_on_close=1 --invalidate=1
このコマンドを10回 実行させた結果と標準偏差は次の通りとなった。
SDメモリカード | 平均スループット(MB/s) | スループット標準偏差 | 平均IOPS | IOPS標準偏差 |
---|---|---|---|---|
kingston | 22.2 | 0.1 | 22 | 0 |
gigastone | 18.6 | 0.6 | 18 | 0 |
trancend | 18.1 | 0.1 | 17 | 0 |
suneast | 26.3 | 3.2 | 25 | 3 |
kioxia | 25.6 | 0.3 | 25 | 0 |
sandisk | 33.6 | 0.2 | 33 | 0 |
これらのスループットをグラフに描画してみると、次の通りとなった。
一つ目に、SDメモリカードによって性能差が出ていることに注目する。 測定結果の最低値と最大値を比較すると 2倍近くの差が出ていることがわかる。 これは、SDメモリカード内の構成やファームウェアの違いによるものと考えられる。
二つ目に、書き込み性能がカタログの最大性能に届かないことにも注目する。 SDメモリカードによっては、最大書き込み速度の半分以下の測定結果となっている。 これは、一般的なPCと比べると非力な Raspberry Pi 4 の Linux OS 上で計測しているため、SDメモリカードが処理しきれるだけのデータを転送できていない可能性がありそうである。
三つ目に、subeastを除き、標準偏差が小さいことにも注目する。 SUNEAST ULTIMATE PRO の結果を除き、標準偏差は 1%程度 に収まっている。 これは、購入直後に測定しているため、SDメモリカードの状態が安定していることを示しているだろう。
I/O サイズを 1 MB でシーケンシャルな読み込み
sudo fio --name=write_bandwidth_test \ --filename=/dev/mmcblk0 --filesize=16G \ --time_based --ramp_time=2s --runtime=60 \ --ioengine=sync --direct=1 --bs=1M --iodepth=1 numjobs=1 \ --rw=read --fsync_on_close=1 --invalidate=1
このコマンドを10回 実行させた結果と標準偏差は次の通りとなった。
SDメモリカード | 平均スループット(MB/s) | スループット標準偏差 | 平均IOPS | IOPS標準偏差 |
---|---|---|---|---|
kingston | 40.1 | 0.3 | 39 | 0 |
gigastone | 39.9 | 0.2 | 39 | 0 |
trancend | 41.3 | 0.2 | 41 | 0 |
suneast | 41.2 | 0.3 | 40 | 0 |
kioxia | 40.9 | 0.3 | 40 | 0 |
sandisk | 40.5 | 0.3 | 40 | 0 |
これらのスループットをグラフに描画してみると、次の通りとなった。
ここでは、SDメモリカードによって性能差が出ていないことに注目する。 シーケンシャルな書き込みとは異なり、異なるSDメモリカード間の読み込み速度に大きな差が見られなかった。 これは、一般的なPCと比べると非力な Raspberry Pi 4 の Linux OS 上で計測しているため、読み込み性能が何かしらに律速している可能性がある。
I/O サイズを 4 KB でランダムな書き込み
sudo fio --name=write_bandwidth_test \ --filename=/dev/mmcblk0 --filesize=16G \ --time_based --ramp_time=2s --runtime=60 \ --ioengine=sync --direct=1 --bs=4K --iodepth=1 numjobs=1 \ --rw=randwrite --fsync_on_close=1 --invalidate=1
このコマンドを10回 実行させた結果と標準偏差は次の通りとなった。
SDメモリカード | 平均スループット(MB/s) | スループット標準偏差 | 平均IOPS | IOPS標準偏差 |
---|---|---|---|---|
kingston | 3.7 | 0.0 | 947 | 3 |
gigastone | 1.0 | 0.0 | 254 | 4 |
trancend | 2.3 | 0.0 | 589 | 2 |
suneast | 5.8 | 0.3 | 1489 | 65 |
kioxia | 2.2 | 0.0 | 571 | 12 |
sandisk | 2.5 | 0.0 | 631 | 2 |
これらのスループットをグラフに描画してみると、次の通りとなった。
ここでは、シーケンシャルな書き込みと傾向が異なることに注目する。 シーケンシャルな書き込みの場合、EXCERIA HIGH ENDURANCE や SanDisk MAX ENDURANCE は高めの傾向が出ていたが、ランダムな書き込みではこれらの性能地が低めの傾向となった。 これらSDメモリカードが "高耐久" を特徴としていることから、SDメモリカード側のファームウェアが小さいブロックで疎らな要求をメモリセルの高耐久性に向けて制御しているのではないかと考えられる。
I/O サイズを 4 KB でランダムな読み込み
sudo fio --name=write_bandwidth_test \ --filename=/dev/mmcblk0 --filesize=16G \ --time_based --ramp_time=2s --runtime=60 \ --ioengine=sync --direct=1 --bs=4K --iodepth=1 numjobs=1 \ --rw=randread --fsync_on_close=1 --invalidate=1
このコマンドを10回 実行させた結果と標準偏差は次の通りとなった。
SDメモリカード | 平均スループット(MB/s) | スループット標準偏差 | 平均IOPS | IOPS標準偏差 |
---|---|---|---|---|
Canvas Select Plus microSD | 8.6 | 0.0 | 2212 | 3 |
GIGASTONE Prime 600X | 7.0 | 0.0 | 1788 | 2 |
Transcend USD300S | 12.0 | 0.0 | 3072 | 6 |
SUNEAST ULTIMATE PRO | 9.7 | 0.0 | 2475 | 4 |
EXCERIA HIGH ENDURANCE | 12.7 | 0.0 | 3240 | 5 |
SanDisk MAX ENDURANCE | 6.3 | 0.0 | 1601 | 2 |
これらのスループットをグラフに描画してみると、次の通りとなった。
ここでも、シーケンシャルな読み込みと傾向が異なることに注目する。 シーケンシャルな読み込みの場合、全体的に性能差はほとんどなく 40MB/s まで達していたが、ランダムな読み込みの場合、SDメモリカードによって性能差が出ており、10 MB/s あたりまで性能が低下している。 SDメモリカードで使用される NAND型フラッシュメモリでは、HDDと比較してシークタイムのロスタイムは小さいため、シークによるものとは考えにくい。 細かいI/Oを大量に発行したことで Raspberry Pi 側 に負荷がかかったことにより、SDメモリカード側のコントローラの違いが顕著に表れたとも考えられる。
vfatファイルシステムのパフォーマンスをベンチマークする
次に、 Page Cache を介さないような測定パターン "vfatファイルシステムに対してダイレクト I/O" でベンチマークする。ここで計測するパターンは下記の4種類とする。
- I/O サイズを 1 MB でシーケンシャルな書き込み
- I/O サイズを 1 MB でシーケンシャルな読み込み
- I/O サイズを 4 KB でランダムな書き込み
- I/O サイズを 4 KB でランダムな読み込み
これらがどのようなI/Oフローでデータアクセスするかを考える。
fio から I/Oサイズごとにread/writeが発行されると、VFS は vfatファイルシステムにwritepages(実データ)とwrite_inode(メタデータ)を呼び出す。 vfatファイルシステムはブロックレイヤにリクエストを要求する。 ブロックレイヤに渡された bio は ioスケジューラなどによってマージやスプリットされる。 ioスケジューラからディスパッチされた ioリクエストはブロック単位に書き込み・読み込みをする。 連続するブロックに対するアクセスはマルチブロック読み込み・書き込みコマンドとして発行することもできる。
その後、一定期間経過後にメタデータの書き込みを開始する。 今回の環境ではFAT32 (クラスタサイズは16 KB) であり、想定される書き込み対象は次の通りである。
この計測では、ダイレクト I/O かつ メモリにも十分な空きがあるはずなので、inode の expireされるまで遅延されるだろう。 デフォルトの値が 30s であるので 一回の計測で 2回程度の書き込みとなる。
I/O サイズを 1 MB でシーケンシャルな書き込み
sudo fio --name=write_bandwidth_test \ --directory=/$TEST_DIR --filesize=4095M \ --time_based --ramp_time=2s --runtime=60 \ --ioengine=sync --direct=1 --bs=1M --iodepth=1 numjobs=1 \ --rw=write --fsync_on_close=1 --invalidate=1
このコマンドを10回 実行させた結果と標準偏差は次の通りとなった。
SDメモリカード | 平均スループット(MB/s) | スループット標準偏差 | 平均IOPS | IOPS標準偏差 |
---|---|---|---|---|
kingston | 18.0 | 0.3 | 17 | 0 |
gigastone | 15.6 | 1.4 | 15 | 1 |
trancend | 15.5 | 0.8 | 15 | 0 |
suneast | 18.4 | 1.8 | 17 | 1 |
kioxia | 19.9 | 0.1 | 19 | 0 |
sandisk | 25.3 | 0.1 | 25 | 0 |
これらのスループットをRAWデバイスの結果と共にグラフに描画してみると、次の通りとなった。
ここでは、RAWデバイスから性能が低下していることに注目する。 RAWデバイスへの書き込みとは異なり、ファイルへの書き込みしているため実際のデータに加えて、メタデータの書き込みも発生している。 今回の環境では、1 GB のファイルは 65,536個のクラスタから構成される。 そのため、1 GB のファイルを書き込みする場合には、 256 KB + α の追加データを書き込みすることになる。 それに加えて、追加の計算量が発生することを加味すれば、これらオーバーヘッドが原因と考えることができるだろう。
I/O サイズを 1 MB でシーケンシャルな読み込み
sudo fio --name=write_bandwidth_test \ --directory=/$TEST_DIR --filesize=4095M \ --time_based --ramp_time=2s --runtime=60 \ --ioengine=sync --direct=1 --bs=1M --iodepth=1 numjobs=1 \ --rw=read --fsync_on_close=1 --invalidate=1
このコマンドを10回 実行させた結果と標準偏差は次の通りとなった。
SDメモリカード | 平均スループット(MB/s) | スループット標準偏差 | 平均IOPS | IOPS標準偏差 |
---|---|---|---|---|
kingston | 40.3 | 0.5 | 39 | 0 |
gigastone | 39.9 | 0.4 | 39 | 0 |
trancend | 41.4 | 0.3 | 41 | 0 |
suneast | 41.6 | 0.3 | 41 | 0 |
kioxia | 40.5 | 0.1 | 40 | 0 |
sandisk | 40.6 | 0.3 | 40 | 0 |
これらのスループットをRAWデバイスの結果と共にグラフに描画してみると、次の通りとなった。
ここでは、RAWデバイスの場合と同様の性能であることに注目する。 vfatファイルシステムのファイルを読み込む場合には、クラスタチェインを辿る必要がある。 しかし、この計測ではそういったオーバーヘッドは小さく、RAWデバイスの時と同様に何かしらで律速しているように見える。
I/O サイズを 4 KB でランダムな書き込み
sudo fio --name=write_bandwidth_test \ --directory=/$TEST_DIR --filesize=4095M \ --time_based --ramp_time=2s --runtime=60 \ --ioengine=sync --direct=1 --bs=4K --iodepth=1 numjobs=1 \ --rw=randwrite --fsync_on_close=1 --invalidate=1
このコマンドを10回 実行させた結果と標準偏差は次の通りとなった。
SDメモリカード | 平均スループット(KB/s) | スループット標準偏差 | 平均IOPS | IOPS標準偏差 |
---|---|---|---|---|
kingston | 540.3 | 253.9 | 134 | 63 |
gigastone | 43.0 | 18.5 | 10 | 4 |
trancend | 1.3 | 0.0 | 0 | 0 |
suneast | 772.9 | 338.7 | 192 | 84 |
kioxia | 487.5 | 149.8 | 121 | 37 |
sandisk | 717.5 | 113.5 | 179 | 28 |
これらのスループットをRAWデバイスの結果と共にグラフに描画してみると、次の通りとなった。
一つ目に、RAWデバイスから性能が大きく低下していることに注目する。 vfatファイルシステムではスパースファイルが未対応であるため、ランダムシークによって書き込まれていない領域 (ホール) に対して 0 を書き込む作業が発生する。 RAWデバイスに対する書き込みと比較して、書き込み総量が大きくなったことで、性能が低下しているのではないかと考えられる。
二つ目に、全体的に標準偏差が大きいことにも注目する。 上記の仮説と同様に、スパースファイルが未対応であるため、ランダムシークの傾向によっては ファイルサイズの2倍の書き込みが発生することもある。 そのため、ランダムシード (書き込みパターン) によって書き込み総量が変化することが原因ではないかと考えられる。
I/O サイズを 4 KB でランダムな読み込み
sudo fio --name=write_bandwidth_test \ --directory=/$TEST_DIR --filesize=4095M \ --time_based --ramp_time=2s --runtime=60 \ --ioengine=sync --direct=1 --bs=4K --iodepth=1 numjobs=1 \ --rw=randread --fsync_on_close=1 --invalidate=1
このコマンドを10回 実行させた結果と標準偏差は次の通りとなった。
SDメモリカード | 平均スループット(KB/s) | スループット 標準偏差 |
平均IOPS | IOPS 標準偏差 |
---|---|---|---|---|
kingston | 7.7 | 0.0 | 1973 | 7 |
gigastone | 5.4 | 0.0 | 1375 | 1 |
trancend | 10.5 | 0.0 | 2702 | 5 |
suneast | 10.4 | 0.1 | 2673 | 23 |
kioxia | 10.4 | 0.1 | 2674 | 11 |
sandisk | 8.6 | 0.0 | 2198 | 7 |
これらのスループットをRAWデバイスの結果と共にグラフに描画してみると、次の通りとなった。
ここでは、RAWデバイスの場合より性能が高くなるケースについて注目する。 通常であれば、RAWデバイスの計測した環境から vfatファイルシステムのレイヤーが追加されるため、オーバーヘッドの追加が想定される。 vfatファイルシステムの場合だと、ファイルの中身を読み込むためにはクラスタチェインを辿る必要がある。 しかし、SUNEAST ULTIMATE PRO と SanDisk MAX ENDURANCE については、その仮説通りとはいかなかった。 これは、ランダム読み込みで何かしらの情報をキャッシュすることで性能が向上したのではないかと考えられる。 しかし、vfatファイルシステムが持つキャッシュはクラスタチェインの連続性のみであり、read-ahead などの仕組みはダイレクトI/Oによってバイパスされているため、これらは無関係であると思われる。
変更履歴
- 2024/02/03: 記事公開
- 2024/03/01: 誤字修正