关闭。这个问题是opinion-based .它目前不接受答案。
想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题.
6年前关闭。
Improve this question
我每秒下载数千个文件。每个文件约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:在我实现更快 IO 的目标上取得更多进展。
由于我现在将多个文件合并为一个,这意味着我总共执行 1 个打开 (CreateFile) 操作,然后多次写入打开的文件。这很好,但仍然不是最佳的。最好进行一次 10MB 写入而不是十次 1MB 写入。多次写入速度较慢,并导致磁盘碎片化,随后也会减慢读取速度。不好。
因此,解决方案是在 RAM 中缓冲所有(或尽可能多)下载的文件,然后一旦达到某个点,通过一次写入操作将它们全部写入单个文件。我有大约 50GB 的 RAM,所以这对我很有用。
然而,现在还有另一个问题。由于我现在手动缓冲我的写入数据以尽可能少地执行写入操作,因此 Windows 缓存变得有些多余,实际上开始减慢速度并消耗 RAM。让我们摆脱它。
对此的解决方案是进行无缓冲(和异步)I/O,Windows 的 CreateFile() 支持该 I/O。但在 .NET 中不容易支持。我不得不使用一个库(似乎是唯一一个)来完成这个,你可以在这里找到:http://programmingaddicted.blogspot.com/2011/05/unbuffered-overlapped-io-in-net.html
这允许来自 .NET 的简单无缓冲异步 IO。唯一的要求是您现在必须手动对 byte() 缓冲区进行扇区对齐,否则 WriteFile() 将因“无效参数”错误而失败。在我的情况下,这只是需要将我的缓冲区对齐到 512 的倍数。
在这一切之后,我的驱动器写入速度达到了 ~110MB/s。比我预期的要好得多。
最佳答案
我建议您查看 TPL DataFlow .看起来您要创建一个 producer/consumer .
在您当前的实现中使用 TPL DataFlow 的好处在于您可以 Specify the degree of parallelism .这将允许您使用数字来最好地调整您的解决方案以满足您的需求。
正如@Graffito 所提到的,如果您使用的是旋转盘片,则写入可能会受到同时写入的文件数量的限制,这使得这成为最佳调整性能的反复试验。
当然,您可以编写自己的机制来限制并发。
我希望这个对你有用。
[附加] 我在一家存档电子邮件的公司工作,该公司对写入磁盘有类似的要求。当目录中有太多文件时,该公司会遇到 io 速度问题。因此,他们选择将文件限制为每个目录 1000 个文件/文件夹。这个决定在我之前,但可能与您的项目有关。
关于.net - 在 VB.NET 中保存数千个文件的最快方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31391652/