Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vb.net/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在VB.NET中保存数千个文件的最快方法?_.net_Vb.net_Performance_File_File Io - Fatal编程技术网

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

在VB.NET中保存数千个文件的最快方法?,.net,vb.net,performance,file,file-io,.net,Vb.net,Performance,File,File Io,我每秒下载数千个文件。每个文件大约5KB,总下载速度约为200Mb/s。我需要保存所有这些文件 下载过程分为数千个正在运行的异步任务。当他们下载完一个文件并想要保存它时,他们会将它添加到一个要保存的文件队列中 下面是这门课的内容。我在一开始就创建了这个类的一个实例,并让我的任务添加需要保存到队列中的文件 Public Class FileSaver Structure FileToSave Dim path As String Dim data() As Byte End St

我每秒下载数千个文件。每个文件大约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

由于我现在将多个文件合并为一个文件,这意味着我总共要执行一个打开(CreateFile)操作,然后对一个打开的文件进行多次写入。这是好的,但仍然不是最优的。一次10MB写入比十次1MB写入更好。多次写入速度较慢,并会导致磁盘碎片,从而降低读取速度。不太好

因此,解决方案是将所有(或尽可能多的)下载的文件缓冲在RAM中,然后一旦达到某个点,通过一次写入操作将它们全部写入单个文件。我有大约50GB的RAM,所以这对我来说非常有用

然而,现在还有另一个问题。由于我现在手动缓冲写数据以尽可能少地执行写操作,Windows缓存变得有些冗余,实际上开始降低速度,并消耗RAM。让我们把它处理掉

解决方案是执行无缓冲(异步)I/O,这是Windows的CreateFile()支持的。但在.NET中不容易支持。我必须使用一个图书馆(似乎唯一存在的图书馆)来完成这项工作,你可以在这里找到:

这允许从.NET进行简单的无缓冲异步IO。唯一的要求是您现在必须手动对字节()缓冲区进行扇区对齐,否则WriteFile()将失败并出现“无效参数”错误。在我的例子中,这只需要将缓冲区调整为512的倍数


在所有这些之后,我的驱动器的写入速度达到了~110MB/s。比我预想的要好得多。

我建议你调查一下。看起来您想创建一个新的

在当前实现中使用TPL数据流的好处在于您可以。这将允许您使用数字来优化您的解决方案,以满足您的需求

正如@Graffito提到的,如果您使用的是旋转盘片,那么写入可能会受到并发写入的文件数量的限制,这使得这是一个尝试和错误,以获得最佳的调优性能

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

我希望这会有所帮助


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

您的磁盘IO子系统是否支持200MB/s?请阅读并观察,在构造函数中执行I/O本身就是一种糟糕的做法。@Remus,可能不是。但我也不会以200MB/s的速度下载。如果文件复制到一个硬盘上,而不是SSD上,那么并行写入文件会降低性能,而不会因为磁头移动而提高性能。如果有多个目标磁盘,则每个磁盘使用一个线程。