【发布时间】:2021-08-12 00:23:06
【问题描述】:
在阅读了许多主题之后,我似乎没有找到我的问题的答案 - 所以就这样吧。
使用 HttpWebRequest 简单下载文件,性能很慢,似乎有 1.5 - 2 Mbps 的上限。
另一方面,WebClient.DownloadFile 表现良好,通过浏览器下载也是如此。
我正在努力思考正在发生的事情,以及我错过了什么。我正在用 Powershell 编写代码,所以那里可能有一些东西。
我想使用 HttpWebRequest 来保持对下载的控制,并跟踪进度,因为我要处理一些 30-70+ GB 的大文件
非常简单地说 - 我有以下代码 sn-p - 为简单起见,我写入内存流,使用常用的 111MB Nvidia 下载,并包含一些性能测量逻辑:
$Buffer = New-Object -TypeName "Byte[]" -ArgumentList 65536
$FilePath = "e:\temp\test.exe"
$Request = [System.Net.HttpWebRequest]::Create("https://download.nvidia.com/gfnpc/GeForceNOW-release.exe")
$Request.set_Timeout(15000)
$Response = $Request.GetResponse()
$TotalLength = $Response.get_ContentLength()
$TotalLengthKB = [System.Math]::Floor($Response.get_ContentLength()/1024)
$ResponseStream = $Response.GetResponseStream()
$TargetStream = New-Object System.IO.MemoryStream
$TargetStream.SetLength($TotalLength)
$DownloadBytes = 0
$DownloadKB = 0
$ObservedCounts = @{}
$StopWatch = New-Object System.Diagnostics.Stopwatch
$StopWatch.Start()
Try {
Do {
# Fill download buffer for this request
$Count = $ResponseStream.Read($Buffer, 0, $Buffer.Length)
If ($ObservedCounts.ContainsKey($Count)) {
$ObservedCounts[$Count]++
} Else {
$ObservedCounts[$Count] = [Int32]1
}
# Update downloaded bytes and progress for this request
$DownloadedBytes = $DownloadedBytes + $Count
$DownloadedKB = [System.Math]::Floor($DownloadedBytes/1024)
$Progress = [System.Math]::Floor($DownloadedKB / $TotalLengthKB * 100)
Write-Progress -Activity "Downloading" -Id 1 -PercentComplete $Progress -Status "Downloaded $($DownloadedKB) KB of $($TotalLengthKB) KB"
# Write data to the file for this request.
$TargetStream.Write($Buffer, 0, $Count)
} Until ($Count -eq 0 -or $StopWatch.Elapsed.Seconds -ge 15)
Write-Host "Done"
} Catch {
# There was an error during processing of this request
Throw $_
} Finally {
if ($TargetStream.CanWrite) {
$TargetStream.Flush()
$TargetStream.Close()
}
$TargetStream.Dispose()
$ResponseStream.Dispose()
$StopWatch.Stop()
Write-Host ('Stopped after {0}' -f $StopWatch.Elapsed.ToString())
Write-Host ('Downloaded: {0} KB' -f $DownloadedKB)
Write-Host "Observed Counts:"
@($ObservedCounts)|Sort-Object Value -Descending|Format-Table|Out-String -Stream
}
15 秒后下载停止,并显示统计信息:
PS E:\temp> .\test.ps1
Done
Stopped after 00:00:15.0142467
Downloaded: 17407 KB
Observed Counts:
Name Value
---- -----
16383 1088
16 67
3 2
PS E:\temp>
与 WebClient.DownloadFile() 相同的文件:
PS E:\temp> Measure-Command {$WebClient.DownloadFile("https://download.nvidia.com/gfnpc/GeForceNOW-release.exe", "e:\temp\blah.exe")}
Days : 0
Hours : 0
Minutes : 0
Seconds : 7
一个简单的 WebClient.DownloadFile() 在大约 7 秒内完成 111MB 的下载,使用 HttpWebRequest 在 15 秒内我几乎管理了 17MB。
我怀疑来自 ResponseStream 的 .Read 似乎忽略了缓冲区大小,因为大多数情况下它返回 16383 字节 - 它真的不在乎我将缓冲区设置为什么大小,除非我降低小于 16383 字节。
我尝试在读/写流之间放置一个 BufferedStream,但并没有真正改变任何东西。
我编写了相当多的 Powershell 脚本,并且经常使用 .NET 类,但对 C# 或其他 .NET “真正的”语言并不熟练,所以我很可能正在做一些我根本看不到的新事物,所以希望一个友好的灵魂可以帮助我发现我的方式的错误。
这显然是我的代码中的某些内容,因为 WebClient 在后台使用相同的类,并且工作得很好。
提前致谢。
【问题讨论】:
-
1) 你奇怪的缓冲区大小会导致糟糕的硬件缓存行为(将其增加到 65536),并且 2)
Write-Progress是 slooooooooooow,删除它:)跨度> -
也许使用Start-BitsTransfer 可以为您加快速度。在那里查看
-Asynchronous开关 -
写入内存流也可能会导致 30-70gb 文件出现问题,尽管您可以可能通过使用带有 @ 的构造函数将性能提高一点点987654328@ 参数,因此它不必在大小增加时保留额外的内存...docs.microsoft.com/en-us/dotnet/api/…
-
@MathiasR.Jessen 缓冲区大小应该已经是那个大小(错字 - 我会更新问题) - 并且 - Write-Progress 可以跟踪进度 - 我试过没有它现在,它没有任何区别。
-
@mclayton MemoryStream 仅用于在这方面的简单性以消除其他地方的任何可能的瓶颈(我不会将 30GB 以上的文件下载到内存流中)。在代码中,当我检索到内容的大小时,MemoryStream 在初始请求之后被调整为最终大小 - 所以大小不应该有任何持续增加。
标签: performance powershell download httpwebrequest webclient