【问题标题】:Poor HttpWebRequest performance when downloading a file下载文件时 HttpWebRequest 性能不佳
【发布时间】: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-Progressslooooooooooow,删除它:)跨度>
  • 也许使用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


【解决方案1】:

我绝对会在这些应用程序中使用 BITS。 可靠,您可以控制油门。

先睹为快:
https://docs.microsoft.com/en-us/windows/win32/bits/using-windows-powershell-to-create-bits-transfer-jobs

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多