PowerShell:为何文件对比耗时差异如此巨大?
PowerShell文件对比耗时差异大,如何准确统计实际耗时?
问题场景
我正在编写脚本对比两个文件夹的内容,当文件大小和时间戳相同时会进行二进制对比,并希望监控对比速度,但得到的结果差异极大。
测试代码片段
$sFolder_1 = "<folder 1 path, include trailing \>" $sFolder_2 = "<folder 2 path, include trailing \>" Get-ChildItem -Path $sFolder_1 -Recurse | ForEach-Object { $oItem_1 = $_ $sItem_1 = $oItem_1.FullName $sItem_rel = $sItem_1.Substring($nLen_1) $sItem_2 = Join-Path $sFolder_2 $sItem_rel $sFile = if (Test-Path -Type Container $sItem_1) { "" } else { "F" } # 检查文件夹2中是否有对应项 if (-not (Test-Path $sItem_2)) { $sResult = "Not in 2" } else { # 如果是文件则进行对比 if ($sFile -eq "") { $sResult = "Found" } else { $nSize_1 = $oItem_1.Length $dTimeStart = Get-Date $nKb = ($nSize_1 / 1024) Write-Output "$dTimeStart : Checking file ($nKb kb)" if (Compare-Object (Get-Content $sItem_1) (Get-Content $sItem_2)) { $sResult = "Dif content" } else { $sResult = "Same" } $nTimeElapsed = ((Get-Date) - $dTimeStart).Ticks / 1e7 $nSpeed = $nKb / $nTimeElapsed Write-Output "$nKb kb in $nTimeElapsed seconds, speed $nSpeed kb/sec." } } Write-Output $sResult }
运行输出(四个GVI视频文件)
08/05/2023 08:58:41 : Checking file (75402.453125 kb) 75402.453125 kb in 37.389018 seconds, speed 2016.70054894194 kb/sec. Same 08/05/2023 08:59:18 : Checking file (67386.28515625 kb) 67386.28515625 kb in 22.6866484 seconds, speed 2970.30588071573 kb/sec. Same 08/05/2023 08:59:41 : Checking file (165559.28125 kb) 165559.28125 kb in 5.6360258 seconds, speed 29375.1815774158 kb/sec. Same 08/05/2023 08:59:47 : Checking file (57776.244140625 kb) 57776.244140625 kb in 2.059942 seconds, speed 28047.5101437929 kb/sec. Same
可以看到第三、第四个文件的对比速度比前两个快十倍,不符合常理,猜测是PowerShell的进程优化机制导致,请问如何准确统计每次对比的实际耗时?
原因分析与解决方案
耗时差异的核心原因
不是PowerShell的优化机制,而是系统文件缓存和低效的文件读取/对比方式共同导致:
- 前两个文件首次读取时,数据从磁盘加载到内存缓存;后续文件直接从缓存读取,速度自然大幅提升。
Get-Content默认按文本行读取,对二进制视频文件完全不适用,会额外增加解析开销,首次读取时这个开销加上磁盘IO导致耗时很长。Compare-Object需要把两个文件的全部内容加载到内存后再逐元素对比,对大文件来说内存占用高、效率低。
准确统计耗时的方法
用Measure-Command替代Get-Date计时,它是PowerShell专门用于测量命令执行时长的工具,精度更高,且能直接返回耗时的TimeSpan对象,无需手动计算。
优化后的对比脚本
下面是兼顾准确计时和高效二进制对比的代码:
$sFolder_1 = "<folder 1 path, include trailing \>" $sFolder_2 = "<folder 2 path, include trailing \>" $nLen_1 = $sFolder_1.Length Get-ChildItem -Path $sFolder_1 -Recurse | ForEach-Object { $oItem_1 = $_ $sItem_1 = $oItem_1.FullName $sItem_rel = $sItem_1.Substring($nLen_1) $sItem_2 = Join-Path $sFolder_2 $sItem_rel if (Test-Path -Type Container $sItem_1) { if (Test-Path $sItem_2) { Write-Output "Found" } else { Write-Output "Not in 2" } return } # 只处理文件 if (-not (Test-Path -Type Leaf $sItem_2)) { Write-Output "Not in 2" return } $nSize_1 = $oItem_1.Length $nSize_2 = (Get-Item $sItem_2).Length if ($nSize_1 -ne $nSize_2) { Write-Output "Dif size" return } $nKb = $nSize_1 / 1024 Write-Output "$(Get-Date) : Checking file ($nKb kb)" # 用Measure-Command计时,同时用高效的二进制对比 $timeTaken = Measure-Command { $bytes1 = [System.IO.File]::ReadAllBytes($sItem_1) $bytes2 = [System.IO.File]::ReadAllBytes($sItem_2) $isSame = [System.Linq.Enumerable]::SequenceEqual($bytes1, $bytes2) } $sResult = if ($isSame) { "Same" } else { "Dif content" } $nTimeElapsed = $timeTaken.TotalSeconds $nSpeed = $nKb / $nTimeElapsed Write-Output "$nKb kb in $nTimeElapsed seconds, speed $nSpeed kb/sec." Write-Output $sResult }
额外说明
如果需要排除系统缓存的影响(测试真实磁盘读取速度),可以在每次文件对比前执行以下命令清空缓存(需要管理员权限):
Clear-DiskCache -DevicePath "\\?\Volume{your-volume-guid}"
日常场景中缓存是正常的系统优化,无需刻意清空,除非是专门的磁盘性能测试。
内容的提问来源于stack exchange,提问作者NewSites
相关产品推荐
相关产品推荐

