在VB.NET中保存数千个文件的最快方法?

Dav*_*son 7 .net vb.net performance file-io file

我每秒下载数千个文件.每个文件大约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
Run Code Online (Sandbox Code Playgroud)

这个类只有一个实例,只有一个队列.每个任务都不会创建单独的队列.此类的一个全局实例具有内部队列,并且我的所有任务都将文件添加到此单个队列.

我已经ConcurrentQueue用默认值替换了BlockingCollection它,它应该像a一样工作ConcurrentQueue,但允许我Take()从集合中阻塞,而不必经常循环.

我正在使用的硬盘支持~180MB/s的最大读/写速度.我只是以200Mb/s的速度下载,而且随着队列的不断增长,我似乎无法足够快地保存数据.出了点问题,我似乎无法弄清楚是什么.

这是最好(最快)的方式吗?我可以在这里创造任何改进吗?


编辑:这个问题被搁置,我无法用我想出的答案发表我自己的答案.我会在这里发布.

这里的问题是,虽然写入文件是一个相对便宜的过程,但打开文件进行写入却不是.由于我下载了数千个文件,我分别保存了每个文件,这对性能造成了极大的影响.

我所做的是将多个下载的文件(当它们仍然在RAM中)组合成一个文件(带有分隔符),并将该文件写入磁盘.我正在下载的文件有一些属性,允许它们以这种方式进行逻辑分组,并且以后仍然使用.比例约为100:1.

我似乎不再受写入限制,而且我目前正在以~40MB/s的速度保存,如果我达到另一个过早的限制,我会更新它.希望这有助于某人.


EDIT2:我的目标是更快地改进IO.

由于我现在将多个文件合并为一个,这意味着我正在执行总共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的速度写入我的驱动器.比我想象的要好得多.

Phi*_*ens 2

我建议您研究一下TPL DataFlow。看起来您想创建一个生产者/消费者

与当前实现相比,使用 TPL DataFlow 的优点在于您可以指定并行度。这将使您能够利用数字来最好地调整您的解决方案以满足您的需求。

正如 @Graffito 提到的,如果您使用旋转盘片,写入可能会受到同时写入的文件数量的限制,这使得这是一次尝试和错误以最佳调整性能。

当然,您可以编写自己的机制来限制并发性。

我希望这个对你有用。

[附加]我在一家归档电子邮件的公司工作,该公司对写入磁盘有类似的要求。当目录中的文件太多时,该公司会遇到 io 速度问题。因此,他们选择将每个目录的文件数限制为 1000 个文件/文件夹。这个决定是在我之前做出的,但可能与你的项目相关。