Windows環境の仮想ディスクIOの速度を調べてみる

# 背景

いろいろな検証でWindowsPC（にも限りませんが）に仮想化環境を立てることが多々あります。
昔はそこまで選択肢もなく、無償で使えるVirtualboxでいいかーという感じだったのですが、最近は同じ無償でも使える選択肢が増えてきました。

一方、Virtualboxの場合、重たいデータをディスクに書き込む際には目に見えて重い、、、という感触を得ることも少なからずあり。
また、Windows10あたりから、Windowsを「仮想マシン」として導入した時にまっとうに動かなくなった悪印象も強く。。（最近は調べてないので、改善してるかもわからんのですが）

さておき、一回腰を据えて調べてみようかと思います。

# 先に結論

Virtualboxは仮想ディスクへの読み書きはそこまででもないですが、ホスト領域をマウントした時の性能劣化が致命的なレベル（30倍近く時間がかかる！！）という状態でした。

VMWareはさすがにそこまでの低下はなし。
Hyper-V/WSLも、悪くない感じ。

積極的に使う仮想化環境についていろいろ考えなおさないといけませんね。。
Extension Pack?今回は調べてません。これ使うと劇的に改善するなどあるならすみません。

# 調べること

以下の、Windows上で動作する仮想基盤について、一定サイズのデータを書き込んでみた場合の、書き込みにかかる速度を見てみます。
実はWindowsからrsyncをかけたいという要望もあって、その際に使用するので、仮想マシン上でrsyncを起動してWindowsホストの領域にデータを書き込むという用途での使い勝手も把握したいと思ってます。

ということで、仮想マシンの仮想ディスク自体への書き込みと、ホストWindowsの領域を何らかの形でマウントした場合の書き込み速度を比較してみます。

## 調査対象

- (参考) ホストOS上
- Virtualbox
- WSL2
- Hyper-V
- VMWare Workstation

## 調査条件

### PC環境

- OS: Windos 11, pro
- CPU: Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz   1.80 GHz
- Memory: 8G
- ディスク: SAMSUNGの256GB SSD

総じて、5年くらい前に買った一般的なスペックのPCですね。
他、常駐しているプログラムがいくつかあるのですが、、今回はそこまでは気にしないことにします。

### 共通条件

- 仮想基盤上のLinuxは、Ubuntu 24.04を使う。Memoryは1536MB、CPUは1。
- データは、/dev/randomを流し込んで作る5GBのダミーファイル（5G.dummy）
- 計測するのは以下
    - 仮想ディスクへの書き込み
        - 書き出した5G.dummyを5G.dummy2にコピー(①）
    - ホスト領域
        - 書き出した5G.dummyを5G.dummy2にコピー(②）
    - 双方マタギ
        - 仮想ディスク側の5G.dummyをホスト領域へコピー(③）
        - 上記の逆（④）
    - 参考
        - Read分のみになることを期待して cat 5G.dummy > /dev/null

今回みたいのは書き込みですが、それだけをきれいに計測する方法が思いつかないので、読み書きセットにしています。
計測が入り組んでますが、一応算数的には、
- ①=仮想ディスクRead＋仮想ディスクWrite + オーバヘッド
- ②=ホストディスRead＋ホストディスクWrite + オーバヘッド
- ③=ホストディスクRead＋仮想ディスクWrite + オーバヘッド
- ④=仮想ディスクRead＋ホストディスクWrite + オーバヘッド
- 
ということで、③-①＝ホストと仮想のディスクReadの差分、②-④＝Write差分が出せるかな、、ということで４つとります。

速度の基準としては、ホスト上でシンプルにファイルをコピーした場合（②）しかとれないため、他のケースであれこれ算数したって比較ができないから微妙なのですが、まぁ、参考情報ということで。

## 試験方法

環境によって若干修正を入れるのですが、ざっくりこんな感じのスクリプトを走らせることになります。

```
LOCALDIR=$( eval echo "~" )
HOSTDIR="/mnt/c/Users/user01/Downloads"

function do_copy() {
    SRCFILE="$1"
    DSTFILE="$2"

    if [ ! -f "$SRCFILE" ]; then
      dd if=/dev/random of="$SRCFILE" bs=1024k count=5000 1>/dev/null 2>&1
    fi
    if [ -f "$DSTFILE" ]; then
      rm "$DSTFILE"
    fi

    START=$(date +%s)
    cp "$SRCFILE" "$DSTFILE"
    END=$(date +%s)
    echo $(( END - START))

    rm "$DSTFILE"
}

function do_read() {
    SRCFILE="$1"

    if [ ! -f "$SRCFILE" ]; then
      dd if=/dev/random of="$SRCFILE" bs=1024k count=5000 1>/dev/null 2>&1
    fi

    START=$(date +%s)
    cat $SRCFILE > /dev/null
    END=$(date +%s)
    echo $(( END - START))
}

echo "-- 1. Local to Lccal --"

SRCFILE="${LOCALDIR}/5G.dummy"
DSTFILE="${LOCALDIR}/5G.dummy2"

for i in {1..5}; do
  echo -n "$i "
  do_copy "$SRCFILE" "$DSTFILE"
  sleep 5
done

if [ -f "$SRCFILE" ]; then
  rm "$SRCFILE"
fi
if [ -f "$DSTFILE" ]; then
  rm "$DSTFILE"
fi


echo "-- 2. Host to Host --"

SRCFILE="${HOSTDIR}/5G.dummy"
DSTFILE="${HOSTDIR}/5G.dummy2"

for i in {1..5}; do
  echo -n "$i "
  do_copy "$SRCFILE" "$DSTFILE"
  sleep 5
done

if [ -f "$SRCFILE" ]; then
  rm "$SRCFILE"
fi
if [ -f "$DSTFILE" ]; then
  rm "$DSTFILE"
fi


echo "-- 3. Local to Host --"

SRCFILE="${LOCALDIR}/5G.dummy"
DSTFILE="${HOSTDIR}/5G.dummy2"

for i in {1..5}; do
  echo -n "$i "
  do_copy "$SRCFILE" "$DSTFILE"
  sleep 5
done

if [ -f "$SRCFILE" ]; then
  rm "$SRCFILE"
fi
if [ -f "$DSTFILE" ]; then
  rm "$DSTFILE"
fi


echo "-- 4. Host to Local --"

SRCFILE="${HOSTDIR}/5G.dummy"
DSTFILE="${LOCALDIR}/5G.dummy2"

for i in {1..5}; do
  echo -n "$i "
  do_copy "$SRCFILE" "$DSTFILE"
  sleep 5
done

if [ -f "$SRCFILE" ]; then
  rm "$SRCFILE"
fi
if [ -f "$DSTFILE" ]; then
  rm "$DSTFILE"
fi


echo "-- 5. Read from Local --"

SRCFILE="${LOCALDIR}/5G.dummy"

for i in {1..5}; do
  echo -n "$i "
  do_read "$SRCFILE"
  sleep 5
done

if [ -f "$SRCFILE" ]; then
  rm "$SRCFILE"
fi


echo "-- 6. Read from Host --"
SRCFILE="${HOSTDIR}/5G.dummy"

for i in {1..5}; do
  echo -n "$i "
  do_read "$SRCFILE"
  sleep 5
done

if [ -f "$SRCFILE" ]; then
  rm "$SRCFILE"
fi
```

### ホストOS上 の個別条件

特になし

### Virtualboxの個別条件

ホスト領域は、共有フォルダとしてホストのDownloadsをDownloadsとしてマウント。

```
$ sudo mount -t vboxsf Downloads /shared
```

### WSLの個別条件

```
> wsl --install -d Ubuntu
> wsl -d Ubuntu
```

ホスト領域は、自動で/mnt/c/Users/<ユーザ名>/Downloadsにマウントされるので、これを使用。

### Hyper-vの個別条件

Hyper-Vは、ローカルPCのフォルダをマウントするなどの機能はないっぽい！！（知らんかった、、、）
CIFSなどを使う方法をとるしかないらしい。
ということで、Hyper-Vでは仮想ディスク書き込みだけ調べます。

### VMWareWorkstation Pro

17。
共有フォルダの設定箇所がちょっと見つけにくかったですが、仮想マシンの設定の「オプション」タブから。
コマンドはたたかずとも/mnt/hgfsにマウントされる。

ちなみに今回はほかの環境と並行で検証した関係上、インストール時はWHPとやらをインストールする方法をとっています。
他に、Hyper-Vを無効化して導入する方法があるようですが、そちらを選ぶとまた特性が変わるのかも、、
今回は手が回らないのでやりませんが、一応留意だけしておきます。

# 試験結果

## まとめ

以下、秒数です。
5回計測した所要秒数の平均値（Virtulboxだけは重すぎるので、２回平均。。）

|計測対象|ホストOS上|Virtualbox|WSL|Hyper-V|VMWareWorkstation|
|--|--|--|--|--|--|
|① 仮想ディスク to 仮想ディスク|-|60.6|62.2|44.0|42.4|
|② ホスト領域 to ホスト領域|25.6|739.0|129.0|-|79.6|
|③ 仮想ディスク to ホスト領域|-|422.5|68.4|-|52.2|
|④ ホスト領域 to 仮想ディスク|~|417.5|101.4|-|53.2|
|⑤ 仮想ディスク > /dev/null|-|25.8|9.6|6.6|12.2|
|⑥ ホスト cat > /dev/null|-|366.0|42.2|-|20.6|
|③ - ① (書き込み速度のホスト-仮想ディスク）|-|361.9|6.2|-|9.8|
|② - ④（読み込み速度のホスト-仮想ディスク) |-|321.5|27.6|-|26.4|

一応いろいろ取りましたが、ホストOS上の②を基準にして、ほかの環境の①と②だけ見ましょうか。

ホストマシン（共有フォルダ）へのアクセスについてVirtualBoxが以上に重いという認識はあったのですが、まさかここまでとは、、（30倍近い、、）
仮想ディスクの読み書きについては、2.5倍くらい。こっちは、まぁそんなもんかなという感じ。

VMWareさすがに早いですね。
仮想ディスクの読み書きが1.7倍、ホスト領域は3.1倍。

Hyper-Vは、ホスト領域のマウントという概念がなかった。。。
仮想ディスクの読み書きはVMWareとトントン。

WSLは、仮想ディスクへの読み書きが2.4倍、ホスト領域は5倍。
①についてHyper-Vに劣る点はちょと以外。

結論、やっぱりVirtualBoxは積極的には選びたくないなぁ、、という感じです。
性能的にはVMWareがやはりいいのですが、有償・無償の条件が結構変わる認識があり、正直積極的に使いたくはない。。

性能的に劣りはしますが、そこまでではないということで、用途で使い分けながらWSL or Hyper-Vがいいかなぁ、、というのがとりあえずの結論です。

ただし、いずれにしてもホスト領域への書き込みはネイティブな場合と比べて数倍かかるため、仮想環境なしでできる作業はネイティブでやるのがいいですね。
よくよく調べたらGit Bashでrsyncも使えるようですし、そっちを活用するかぁ。

## 詳細情報

### 参考情報　ホストOS上

```
# Local Test, From 5G.dummy分のみ
> date; cp .\5G.dummy .\5G.dummy2; date; rm ./5G.dummy2
2024年10月26日 11:02:31
2024年10月26日 11:02:56

> date; cp .\5G.dummy .\5G.dummy2; date; rm ./5G.dummy2
2024年10月26日 11:03:00
2024年10月26日 11:03:24

> date; cp .\5G.dummy .\5G.dummy2; date; rm ./5G.dummy2
2024年10月26日 11:03:29
2024年10月26日 11:03:54

> date; cp .\5G.dummy .\5G.dummy2; date; rm ./5G.dummy2
2024年10月26日 11:04:01
2024年10月26日 11:04:27

> date; cp .\5G.dummy .\5G.dummy2; date; rm ./5G.dummy2
2024年10月26日 11:04:44
2024年10月26日 11:05:12
```

### Virtualbox

```
-- 1. Local to Lccal --
1 58
2 63
3 59
4 62
5 61
-- 2. Host to Host --
1 742
2 736
-- 3. Local to Host --
1 410
2 435
-- 4. Host to Local --
1 435
2 400
-- 5. Read from Local --
1 25
2 26
3 25
4 25
5 26
-- 6. Read from Host --
1 366
2 366
```

### WSL

```
-- 1. Local to Lccal --
1 53
2 63
3 64
4 67
5 64

-- 2. Host to Host --
1 127
2 132
3 137
4 127
5 122

-- 3. Local to Host --
1 72
2 65
3 66
4 71
5 68

-- 4. Host to Local --
1 95
2 108
3 105
4 100
5 99

-- 5. Read from Host --
1 14
2 9
3 8
4 9
5 8

-- 6. Read from Local --
1 39
2 42
3 40
4 46
5 44
```

### Hyper-V

```
-- 1. Local to Lccal --
1 40
2 45
3 46
4 44
5 45
-- 5. Read from Local --
1 7
2 7
3 6
4 7
5 6
```

### VMWaraWorkstation Pro

```
-- 1. Local to Lccal --
1 49
2 42
3 37
4 35
5 49
-- 2. Host to Host --
1 88
2 79
3 77
4 78
5 76
-- 3. Local to Host --
1 55
2 52
3 54
4 50
5 50
-- 4. Host to Local --
1 55
2 50
3 54
4 54
5 53
-- 5. Read from Local --
1 14
2 13
3 12
4 11
5 12
-- 6. Read from Host --
1 22
2 20
3 22
4 20
5 19
```

ここまで。