Hyper-Vの仮想マシンにファイルをコピーする時、方法によってどれくらいコピー時間が変わってくるのか気になったので計測しました。
※どれも一度だけ計測した結果で環境によっても違うかもしれないので参考程度です。
計測環境
- ホスト
- OS: Windows 11 (Build 23615.1000)
- CPU: Intel Core i7-7700K
- RAM: DDR4-2133 32GB
- SSD: Samsung 960EVO 500GB
- ゲスト
- OS: Windows 11 (Build 22621.3007)
- CPU: 4コア割り当て
- RAM: 8GB割り当て
計測方法
PowerShellのスクリプト等を使って以下のA., B.の組み合わせについて確認した。なお、アーカイブの作成にはCompress-Archive -CompressionLevel NoCompression
を使用した。
- A. 中身が乱数の10GB分のファイルを以下の容量・数で生成する
- 1GB x 10ファイル
- 1MB x 10000ファイル
- B. コピーの方法
- Copy-VHD
- ホスト側で仮想マシンにコピーしたいファイルが入ったVHDを作成後、仮想マシンにVHDをマウントしてコピー
- Copy-VMFile
- ファイルを1つのファイルにアーカイブ後1、
Copy-VMFile
を使ってコピー
- ファイルを1つのファイルにアーカイブ後1、
- Copy-Item
-
Copy-Item
の-ToSession
を使ってコピー
-
- Copy-Item-Smb
-
Copy-Item
を使って仮想マシンの共有フォルダにコピー - 仮想マシン側では事前に
C:\
を読み書き可能な形で共有しておく
-
- Copy-Item-Compress
- A.のファイルを1つのファイルにアーカイブ後、
Copy-Item
の-ToSession
を使ってコピー
- A.のファイルを1つのファイルにアーカイブ後、
- Copy-Item-Smb-Compress
- A.のファイルを1つのファイルにアーカイブ後、
Copy-Item
を使って仮想マシンの共有フォルダにコピー
- A.のファイルを1つのファイルにアーカイブ後、
- Copy-VHD
検証に使用したコード
テスト用データの生成 (※WSLで実行する想定です)
generate.sh
#!/bin/sh
mkdir 1x10000
(
cd 1x10000
for i in $(seq 1 10000); do
dd if=/dev/urandom of=$i.bin bs=1M count=1
done
)
mkdir 1000x10
(
cd 1000x10
for i in $(seq 1 10); do
dd if=/dev/urandom of=$i.bin bs=1000M count=1
done
)
速度計測スクリプト
他の環境で動かすことを想定していないので、想定しない動作をしないか確認してから実行してください。
bench.ps1
$vmname = "Windows 11"
$username = "User"
$password = "user"
$vm = Get-VM -Name $vmname
$networkAdapter = Get-VMNetworkAdapter -VM $vm
$ipAddresses = $networkAdapter.IPAddresses
$ipAddress = $ipAddresses[0]
$hostWork = "C:\work"
$guestWork = "C:\work"
$hostCompressTmp = "$hostWork\tmp.zip"
$guestCompressTmp = "$guestWork\tmp.zip"
$vhdPath = "C:\work\tmp.vhdx"
$vhdSize = 15GB
$password = ConvertTo-SecureString "$password" -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential ($username, $password)
$session = New-PSSession -VMName $vmname -Credential $credential
function Initialize-WorkingDirectory
{
Invoke-Command -Session $session -ScriptBlock {
if (Test-Path $using:guestWork) {
Remove-Item -Path $using:guestWork -Recurse -Confirm:$false
}
New-Item -ItemType Directory -Path $using:guestWork > $null
}
}
function Bench-Copy-Item($dir)
{
$result = Measure-Command { Copy-Item -ToSession $session -Path $dir -Destination C:\work -Recurse }
return @{
"Result"=$result;
}
}
function Bench-Copy-Item-Compress($dir)
{
$result1 = Measure-Command { Prepare-Compress $dir }
$result2 = Measure-Command { Copy-Item -ToSession $session -Path $hostCompressTmp -Destination $guestCompressTmp }
$result3 = Measure-Command { Invoke-Command -Session $session -ScriptBlock { Expand-Archive -Path $using:guestCompressTmp -DestinationPath $using:guestWork } }
Release-Compress
return @{
"Compress"=$result1;
"Result"=$result2;
"Decompress"=$result3;
}
}
function Bench-Copy-VMFile($dir)
{
$result1 = Measure-Command { Prepare-Compress $dir }
$result2 = Measure-Command { Copy-VMFile $vmname -SourcePath $hostCompressTmp -DestinationPath $guestCompressTmp -CreateFullPath -FileSource Host }
$result3 = Measure-Command { Invoke-Command -Session $session -ScriptBlock { Expand-Archive -Path $using:guestCompressTmp -DestinationPath $using:guestWork } }
Release-Compress
return @{
"Compress"=$result1;
"Result"=$result2;
"Decompress"=$result3;
}
}
function Bench-Copy-Item-Smb($dir)
{
New-PSDrive -Name "Z" -PSProvider FileSystem -Root "\\$ipAddress\c" -Credential $credential > $null
$result = Measure-Command { Copy-Item -Path $dir -Destination Z:\work -Recurse }
Remove-PSDrive -Name "Z"
return @{
"Result"=$result;
}
}
function Bench-Copy-Item-Smb-Compress($dir)
{
New-PSDrive -Name "Z" -PSProvider FileSystem -Root "\\$ipAddress\c" -Credential $credential > $null
$result1 = Measure-Command { Prepare-Compress $dir }
$result2 = Measure-Command { Copy-Item -Path $hostCompressTmp -Destination Z:\work\tmp.zip }
$result3 = Measure-Command { Invoke-Command -Session $session -ScriptBlock { Expand-Archive -Path $using:guestCompressTmp -DestinationPath $using:guestWork } }
Release-Compress
Remove-PSDrive -Name "Z"
return @{
"Compress"=$result1;
"Result"=$result2;
"Decompress"=$result3;
}
}
function Bench-Copy-VHD($dir)
{
if (Test-Path $vhdPath) {
Remove-Item $vhdPath
}
$result1 = Measure-Command {
New-VHD -Path $vhdPath -SizeBytes $vhdSize -Dynamic > $null
$vhdx = Mount-VHD -Path $vhdPath -Passthru
$disk = Initialize-Disk -Number $vhdx.Number -PassThru | New-Partition -AssignDriveLetter -UseMaximumSize | Format-Volume -NewFileSystemLabel "Bench"
$driveLetter = ($disk | Get-Partition).DriveLetter
Copy-Item -Path $dir -Destination "${driveLetter}:\" -Recurse
Dismount-VHD -Path $vhdPath
}
$result2 = Measure-Command { Add-VMHardDiskDrive -VMName $vmName -Path $vhdPath }
$result3 = Measure-Command {
Invoke-Command -Session $session -ScriptBlock {
$driveLetter = Get-Volume | Where-Object { $_.FileSystemLabel -eq "Bench" } | Select-Object -ExpandProperty DriveLetter
Copy-Item -Path "${driveLetter}:\*" -Destination $using:guestWork -Recurse
}
}
$result4 = Measure-Command { Get-VMHardDiskDrive -VMName $vmName | Where-Object { $_.Path -eq $vhdPath } | Remove-VMHardDiskDrive }
return @{
"Prepare"=$result1;
"AttachVHD"=$result2;
"Result"=$result3;
"DetachVHD"=$result4;
}
}
function Prepare-Compress($dir)
{
if (Test-Path $hostCompressTmp) {
Remove-Item -Path $hostCompressTmp -Recurse -Confirm:$false
}
Compress-Archive -Path $dir -DestinationPath $hostCompressTmp -Force -CompressionLevel NoCompression
}
function Release-Compress
{
if (Test-Path $hostCompressTmp) {
Remove-Item -Path $hostCompressTmp -Recurse -Confirm:$false
}
}
$benchmarks = @{
"Copy-Item"=${function:Bench-Copy-Item};
"Copy-Item-Smb"=${function:Bench-Copy-Item-Smb};
"Copy-VMFile"=${function:Bench-Copy-VMFile};
"Copy-Item-Compress"=${function:Bench-Copy-Item-Compress};
"Copy-Item-Smb-Compress"=${function:Bench-Copy-Item-Smb-Compress};
"Copy-VHD"=${function:Bench-Copy-VHD};
}
$filePattern = @{
"1000x10"="C:\work\1000x10";
"1x10000"="C:\work\1x10000";
#"test"="C:\work\test";
}
foreach($benchName in $benchmarks.Keys) {
$func = $benchmarks[$benchName]
foreach ($fileName in $filePattern.Keys) {
$path = $filePattern[$fileName]
Initialize-WorkingDirectory
$results = & $func $path
Write-Output "$benchName($fileName)"
foreach ($resultName in $results.Keys) {
$result = $results[$resultName]
Write-Output "`t${resultName}: ${result}"
}
}
}
結果
1GB x 10ファイル | 1MB x 10000ファイル | |
---|---|---|
Copy-VHD | 00:01:26.22 | 00:01:18.84 |
Copy-VMFile | 00:06:12.45 | 00:07:33.64 |
Copy-Item | 00:19:19.66 | 00:28:11.62 |
Copy-Item-Smb | 00:00:34.31 | 00:01:47.18 |
Copy-Item-Compress | 00:21:12.26 | 00:23:54.06 |
Copy-Item-Smb-Compress | 00:01:59.29 | 00:04:30.71 |
- コピーの方法と合計サイズが同じでも、大きいファイルが少し、小さいファイルが大量で比較すると、方法によっては最大数倍時間が変わってくることがある。 (Copy-Item-Smb)
- Copy-Item + 1MB x 10000ファイルのパターンでしか早くなっていないので、小さいファイルが大量にあったとしても基本的にはそのままコピーするのが早い。
- 今回の結果からすると、仮想マシンへのファイルコピーで一番良いのはWindowsの共有経由でコピー、次点でVHDファイルを使ってコピー2が良さそう。
VHDを作成する計測の詳細
Copy-VHDでVHDの作成(VHDファイル自体の作成、フォーマット、VHDへのファイルのコピー)にかかった時間は以下の通り。
1GB x 10ファイル | 1MB x 10000ファイル | |
---|---|---|
Copy-VHD | 00:00:43.78 | 00:00:41.36 |
アーカイブを作成する計測の詳細
アーカイブを作成してから仮想マシンへコピーする計測の詳細は以下の通り。
作成 | ← | 展開 | ← | コピーのみ | ← | |
---|---|---|---|---|---|---|
1GB x 10ファイル | 1MB x 10000ファイル | 1GB x 10ファイル | 1MB x 10000ファイル | 1GB x 10ファイル | 1MB x 10000ファイル | |
Copy-VMFile | 00:00:54.63 | 00:03:05.60 | 00:01:40.63 | 00:01:20.90 | 00:03:37.18 | 00:03:07.14 |
Copy-Item-Compress | 00:00:52.37 | 00:02:28.02 | 00:00:38.73 | 00:01:23.23 | 00:19:29.68 | 00:19:56.84 |
Copy-Item-Smb-Compress | 00:00:53.81 | 00:02:36.31 | 00:00:48.77 | 00:01:20.91 | 00:00:28.18 | 00:00:39.45 |
- 同じ計測データを元にアーカイブを作成・展開しているのに、圧縮で30秒、展開で1分程度の違いが出ているので、やはり何度か計測した方が良さそう