我每秒下载数千个文件。每个文件大约5KB,总下载速度约为200Mb / s。我需要保存所有这些文件。
下载过程分为数千个正在运行的异步任务。当他们完成下载文件并想要保存文件时,他们会将其添加到要保存的文件队列中。
以下是这个类的内容。我在一开始就创建了这个类的实例,让我的任务添加需要保存到队列中的文件。
Public Class FileSaver
Structure FileToSave
Dim path As String
Dim data() As Byte
End Structure
Private FileQueue As New Concurrent.BlockingCollection(Of FileToSave)
Sub New()
Task.Run(
Async Function()
While 1
Dim fl As FileToSave = FileQueue.Take()
Using sourceStream As New FileStream(fl.path, FileMode.Append, FileAccess.Write, FileShare.None, bufferSize:=4096, useAsync:=True)
Await sourceStream.WriteAsync(fl.data, 0, fl.data.Length)
End Using
End While
End Function
)
End Sub
Public Sub Add(path As String, data() As Byte)
Dim fl As FileToSave
fl.path = path
fl.data = data
FileQueue.Add(fl)
End Sub
Public Function Count()
Return FileQueue.Count
End Function
End Class
此类只有一个实例,只有一个队列。每个任务都不创建一个单独的队列。这个类的一个全局实例有一个内部队列,我的所有任务都将文件添加到这个队列中。
我已经将ConcurrentQueue
替换为默认BlockingCollection
,它应该像ConcurrentQueue
一样工作,但允许我从集合中阻止Take()
,而不必经常循环。
我使用的硬盘支持~180MB / s的最大读/写速度。我只是以200Mb / s的速度下载,而且随着队列的不断增长,我似乎无法足够快地保存数据。有些事情是错的,我似乎无法弄清楚是什么。
这是最好(最快)的方式吗?我可以在这里创造任何改进吗?
编辑:这个问题被搁置了,我不能用我想出的答案发表我自己的答案。我会在这里发布。这里的问题是,虽然写入文件是一个相对便宜的过程,但打开文件进行写入却不是。由于我下载了数千个文件,我分别保存了每个文件,这显着损害了性能。
我所做的是将多个下载的文件(当它们仍然在RAM中)组合成一个文件(带分隔符),然后将该文件写入磁盘。我正在下载的文件有一些属性,允许它们以这种方式进行逻辑分组,并且以后仍然使用。比例约为100:1。
我似乎不再受写限制,而且我目前正在以~40MB / s的速度保存,如果我达到另一个过早的限制,我会更新它。希望这有助于某人。
EDIT2:我更快地实现目标的进展。
由于我现在将多个文件合并为一个,这意味着我正在执行总共1次打开(CreateFile)操作,然后多次写入打开的文件。这很好,但仍然不是最佳的。最好做一次10MB写入而不是十次1MB写入。多次写入速度较慢,导致磁盘碎片,后来也会减慢读取速度。不好。
所以解决方法是将所有(或尽可能多的)下载文件缓冲到RAM中,然后一旦我达到某一点,用一个Write操作将它们全部写入单个文件。我有~50GB的RAM,所以这对我很有用。
然而,现在还有另一个问题。由于我现在手动缓冲我的写入数据以尽可能少地执行写入操作,因此Windows缓存变得有些多余,实际上开始放慢速度并消耗RAM。让我们摆脱它。
解决方法是执行Windows的CreateFile()支持的无缓冲(和异步)I / O.但在.NET中不容易支持。我必须使用一个库(唯一一个似乎存在的库)来完成这个,你可以在这里找到:http://programmingaddicted.blogspot.com/2011/05/unbuffered-overlapped-io-in-net.html
允许来自.NET的简单无缓冲异步IO。唯一的要求是你现在必须手动扇区对齐你的byte()缓冲区,否则WriteFile()将失败并出现“Invalid Parameter”错误。在我的情况下,这只需要将我的缓冲区与512的倍数对齐。
完成所有这些后,我能够以大约110MB / s的速度写入我的驱动器。比我预期的要好得多。
答案 0 :(得分:2)
我建议您查看TPL DataFlow。您似乎想要创建producer/consumer。
在当前实施中使用TPL DataFlow的好处在于您可以Specify the degree of parallelism。这样您就可以使用这些数字来最好地调整解决方案以满足您的需求。
正如@Graffito所提到的,如果你正在使用旋转盘片,写入可能会受到同时写入的文件数量的限制,这使得这是一次试错,以最好地调整性能。
当然,您可以编写自己的机制来限制并发性。
我希望这会有所帮助。
[附加]我在一家存档电子邮件的公司工作,该电子邮件具有类似写入磁盘的要求。当目录中的文件太多时,该公司遇到了io速度问题。因此,他们选择将文件限制为每个目录1000个文件/文件夹。这个决定是在我的时间之前,但可能与您的项目有关。