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

标签 .net vb.net performance file file-io

关闭。这个问题是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/

相关文章:

c# - 如何在 Entity Framework 中处理空值?

vb.net - VB.NET 中令人困惑的逻辑运算符

Java性能: why a method call can be quicker than direct calculations in if conditional and array indexing?

java - 多线程性能

ios - 为了性能,在 iOS 上隐藏或删除 CALayers 哪个更好?

c#.net 源代码

.net - 什么是最好的 .NET 游戏开发框架?

asp.net - "Invalid attempt to read when no data is present"

c# - 如何引用公共(public)部分类 MainWindow : Window from other static class? 的成员

c# - 如何打开或运行转换为 byte[] 的未知文件