🐳

Docker for Macの新しいファイルシステムVirtioFSを試してみた

2021/12/02に公開

この記事はUMITRON Advent Calendar 2021 2日目の記事です。

背景

Docker for Macのファイルアクセスは遅く、本家のGitHubにもissueがあり長年議論が重ねられてきています。

これらの経緯は以前Qiitaにまとめたので興味があれば参照ください。

Docker for Macチームも改善を重ねてきていますが、現在デフォルトのファイルシステムのgRPC-FUSEでもまだユーザの満足を得られる速さとは言えません。
最近この問題を改善したVirtioFSというファイルシステムを実装したDocker for Macの実験ビルドが公開されました。以下のリンク先からダウンロードし、指示通り設定すれば試すことが出来ます。

このVirtioFSでファイルシステムパフォーマンスがどれほど改善されたかベンチマークを取ってみました。

ベンチマーク手法

弊社では一部でDocker+Railsを使っていて、今のDocker for Macそのままではテスト実行が遅く開発がスピーディに行えないと感じることがあります。このテストの速さが改善されるか直接試したかったのですが、現状公開されてるビルドではVirtioFSはまだ完全ではなく、弊社のRailsのテストは動きませんでした。
Railsのテストが遅いのは多くのファイルを読み込む必要があるのが大きな要因だと考えられるので、多くのファイルを読み込む時間を測ることにします。具体的には5KBのファイルを2万個用意し、ひたすらこれを順々に読み込む時間を測ります。テストに使ったコードはここで確認できます。

Intel MacとM1 Macどちらも試します。また、ついでに一時期Docker for MacのEdgeリリースにも入っていたmutagen.ioも試します。

ベンチマーク結果

Machine File System Time(seconds)
M1 gRPC-FUSE 17.934
M1 VirtioFS 5.239
M1 mutagen(gRPC-FUSE) 0.4772
Intel gRPC-FUSE 24.974
Intel VirtioFS 37.843
Intel mutagen(gRPC-FUSE) 0.7402

まとめ

M1 MacではVirtioFSがgRPC-FUSEの3.4倍速く、Intel Macでは逆に1.5倍遅い結果になりました。また、それでもmutagenと比べると全然遅いことも合わせてわかりました。
Dockerの中の人によるとIntelチップでは新しいvirtualization frameworkにスピードの問題が確認されているとのことなので、それが関係していると思われます。

これはあくまで簡単なベンチマークですしまだ開発中のものなので、安定版がリリースされ実際のワークロードで実行した場合にどれほど変わるのか分かりません。ですが、もし実際にM1 Macで数倍速くなるのであれば使える日が楽しみです。

補遺

ベンチマークログ

M1, gRPC-FUSE

$ docker compose run --rm bench hyperfine --warmup 2 './justread.sh files'
Benchmark 1: ./justread.sh files
  Time (mean ± σ):     17.934 s ±  1.493 s    [User: 0.618 s, System: 2.744 s]
  Range (min … max):   16.703 s … 21.484 s    10 runs

M1, virtiofs

$ docker compose run --rm bench hyperfine --warmup 2 './justread.sh files'
Benchmark 1: ./justread.sh files
  Time (mean ± σ):      5.239 s ±  0.115 s    [User: 0.671 s, System: 1.745 s]
  Range (min … max):    5.101 s …  5.517 s    10 runs
 
  Warning: Statistical outliers were detected. Consider re-running this benchmark on a quiet PC without any interferences from other programs. It might help to use the '--warmup' or '--prepare' options.

M1, mutagen(gRPC-FUSE)

$ docker compose run --rm bench hyperfine --warmup 2 './justread.sh files'
Benchmark 1: ./justread.sh files
  Time (mean ± σ):     477.2 ms ±   4.9 ms    [User: 248.1 ms, System: 310.5 ms]
  Range (min … max):   468.0 ms … 483.4 ms    10 runs

Intel, gRPC-FUSE

$ docker compose run --rm bench hyperfine --warmup 2 './justread.sh files'
Benchmark 1: ./justread.sh files
  Time (mean ± σ):     24.974 s ±  1.081 s    [User: 1.089 s, System: 2.359 s]
  Range (min … max):   23.706 s … 27.347 s    10 runs

Intel, virtiofs

$ docker compose run --rm bench hyperfine --warmup 2 './justread.sh files'
Benchmark 1: ./justread.sh files
  Time (mean ± σ):     37.843 s ±  6.001 s    [User: 6.608 s, System: 14.749 s]
  Range (min … max):   33.198 s … 54.131 s    10 runs

Intel, mutagen(grpc-fuse)

$ docker compose run --rm bench hyperfine --warmup 2 './justread.sh files'
Benchmark 1: ./justread.sh files
  Time (mean ± σ):     740.2 ms ±  45.4 ms    [User: 457.4 ms, System: 362.8 ms]
  Range (min … max):   700.1 ms … 834.8 ms    10 runs

書き込み速度

単純な1ファイル書き込みに関しては、特にM1においてかなり速いようです。M1ではVirtioFSがgRPC-FUSE17倍、Intelでは2.9倍速い結果になりました。

Machine File System Time(seconds)
M1 VirtioFS 3.371
M1 gRPC-FUSE 58.657
Intel VirtioFS 18.581
Intel gRPC-FUSE 54.549

書き込みベンチマークログ

M1, VirtioFS

$ docker compose run --rm bench hyperfine --warmup 1 'dd if=/dev/zero of=speedtest bs=1024 count=100000'
Benchmark 1: dd if=/dev/zero of=speedtest bs=1024 count=100000
  Time (mean ± σ):      3.371 s ±  0.392 s    [User: 0.161 s, System: 1.254 s]
  Range (min … max):    2.784 s …  3.962 s    10 runs

M1, gRPC-FUSE

$ docker compose run --rm bench hyperfine --warmup 1 'dd if=/dev/zero of=speedtest bs=1024 count=100000'
Benchmark 1: dd if=/dev/zero of=speedtest bs=1024 count=100000
  Time (mean ± σ):     58.657 s ±  2.796 s    [User: 0.258 s, System: 4.499 s]
  Range (min … max):   55.249 s … 64.318 s    10 runs

Intel, VirtioFS

$ docker compose run --rm bench hyperfine --warmup 1 'dd if=/dev/zero of=speedtest bs=1024 count=100000'
Benchmark 1: dd if=/dev/zero of=speedtest bs=1024 count=100000
  Time (mean ± σ):     18.581 s ±  0.968 s    [User: 2.302 s, System: 9.771 s]
  Range (min … max):   17.017 s … 19.893 s    10 runs

Intel, gRPC-FUSE

$ docker compose run --rm bench hyperfine --warmup 1 'dd if=/dev/zero of=speedtest bs=1024 count=100000'
Benchmark 1: dd if=/dev/zero of=speedtest bs=1024 count=100000
  Time (mean ± σ):     54.549 s ±  1.123 s    [User: 0.325 s, System: 4.679 s]
  Range (min … max):   53.137 s … 56.360 s    10 runs

参考

テスト環境

M1 Mac: MacBook Pro(16-inch, 2021), Apple M1 Pro, メモリ32GB, macOS 12.0.1
Intel Mac: MacBook Pro(15-inch, 2017), 2.9 GHz クアッドコアIntel Core i7, メモリ16GB, macOS 12.0.1
Docker for Macの設定(共通): CPUs 4, Memory 6GB, Swap 1.5GB

Discussion