如何加快创建FileStream的速度

11

我的应用程序需要打开很多小文件,比如说有1440个文件,每个文件包含一分钟的数据,以读取某一天的所有数据。每个文件只有几KB大小。这是一个GUI应用程序,所以我希望用户(==我!)不必等待太长时间。

结果发现打开这些文件相当慢。经过研究,大部分时间都花费在为每个文件创建FileStream(OpenStream = new FileStream)上。示例代码:

// stream en reader aanmaken
FileStream OpenStream;
BinaryReader bReader;

foreach (string file in files)
{
    // bestaat de file? dan inlezen en opslaan
    if (System.IO.File.Exists(file))
    {
        long Start = sw.ElapsedMilliseconds;

        // file read only openen, anders kan de applicatie crashen
        OpenStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

        Tijden.Add(sw.ElapsedMilliseconds - Start);

        bReader = new BinaryReader(OpenStream);

        // alles in één keer inlezen, werkt goed en snel
        // -bijhouden of appenden nog wel mogelijk is, zonodig niet meer appenden
        blAppend &= Bestanden.Add(file, bReader.ReadBytes((int)OpenStream.Length), blAppend);

        // file sluiten
        bReader.Close();
    }
}

使用秒表计时器,我发现大多数(>80%)的时间都花在为每个文件创建FileStream上。创建BinaryReader并实际读取文件(Bestanden.add)几乎不需要时间。

我对此感到困惑,无法找到加快创建FileStream速度的方法。我该怎么办?

问题更新:

  • 这种情况在Windows 7和Windows 10上都发生
  • 文件是本地的(位于SSD磁盘上)
  • 目录中仅有1440个文件
  • 奇怪的是,稍后再次读取相同的文件时,创建FileStream突然几乎不需要时间。某处操作系统会记住文件流。
  • 即使关闭应用程序并重新启动它,“再次”打开文件也几乎不需要时间。这使得查找性能问题变得非常困难。我不得不复制许多目录来一遍又一遍地重现这个问题。

似乎是可能的操作系统问题。您正在访问哪种类型的操作系统?它是本地的还是在网络上(不是运行应用程序的计算机上)?目录中是否包含其他文件(例如,Windows对每个目录中的文件数量有建议的限制)? - Igor
这个问题出现在Windows 7和Windows 10上。这些文件都是本地的,存储在一个只包含1440个文件的目录中。我刚才意识到我忘了提到一些事情:
  • 只有第一次读取文件时速度很慢
  • 如果我再次从应用程序读取文件,创建FileStream的时间突然几乎不耗费任何时间(这是怎么回事?操作系统是否记住了文件句柄?我的应用程序肯定没有)。
  • 如果我关闭应用程序并重新启动,再次读取相同的文件几乎不需要任何时间。操作系统中必须有某种缓冲/内存机制。
- wvl_kszen
2
Windows会将文件缓存在内存中,因此更快的后续访问并不令人惊讶。您可以使用https://technet.microsoft.com/en-us/sysinternals/ff700229.aspx清除备用列表。 - user6144226
我知道Windows可以缓存文件。但只有创建FileStream才会使第二次读取更快,而不是实际的文件读取(这发生在Bestanden.Add()中)。创建FileStream是否也会读取文件的前几个K?这可能解释了发生的情况,因为每个文件只有2-3kB。 - wvl_kszen
使用SysInternal的DiskMon和ProcessMonitor,可能可以获取关于操作系统正在执行什么操作的更多细节。无论如何,此时您的C#应用程序只是一个测试平台,因为该问题可能与您的C#代码无关。 - Igor
显示剩余4条评论
2个回答

2

正如你在评论中提到FileStream读取首个4K到缓冲区的对象一样。您可以更改此缓冲区的大小以反映数据的更好大小(例如,如果文件比缓冲区小,则减少)。如果您按顺序读取文件,则可以通过FileOptions向操作系统提示此信息。另外,您可以避免使用BinaryReader,因为您完全读取了文件。

    // stream en reader aanmaken
    FileStream OpenStream;

    foreach (string file in files)
    {
        // bestaat de file? dan inlezen en opslaan
        if (System.IO.File.Exists(file))
        {
            long Start = sw.ElapsedMilliseconds;

            // file read only openen, anders kan de applicatie crashen
            OpenStream = new FileStream(
                file,
                FileMode.Open,
                FileAccess.Read,
                FileShare.ReadWrite,
                bufferSize: 2048, //2K for example 
                options: FileOptions.SequentialScan);

            Tijden.Add(sw.ElapsedMilliseconds - Start);

            var bufferLenght = (int)OpenStream.Length;
            var buffer = new byte[bufferLenght];
            OpenStream.Read(buffer, 0, bufferLenght);

            // alles in één keer inlezen, werkt goed en snel
            // -bijhouden of appenden nog wel mogelijk is, zonodig niet meer appenden
            blAppend &= Bestanden.Add(file, buffer, blAppend);
        }
    }

我不知道Bestanden对象的类型。但如果这个对象有从数组中读取数据的方法,你也可以重复使用缓冲区来处理文件。

    //the buffer should be bigger than the biggest file to read
    var bufferLenght = 8192;
    var buffer = new byte[bufferLenght];

    foreach (string file in files)
    {
        //skip 
        ...
        var fileLenght = (int)OpenStream.Length;
        OpenStream.Read(buffer, 0, fileLenght);

        blAppend &= Bestanden.Add(file, /*read bytes from buffer */, blAppend);

我希望能对您有所帮助。

我还没有注意到我可以设置缓冲区大小。这(标准缓冲区大小为4K)解释了所有流式读取示例,它们一直读取4K的片段。我并行打开了所有文件,但我的Bestanden类不是线程安全的,这花费了我一些时间。我觉得对于小文件,最好并行读取它们,对于大文件,则最好逐个读取。我可能需要进行实验。并行读取文件将速度提高了约3倍。 - wvl_kszen
通过构造函数参数 - bufferSize - Ivan R.

0

免责声明:此答案仅为一种(有根据的)猜测,更可能是Windows的错误而非您可以使用不同代码修复的问题。

因此,这种行为可能与在此处描述的Windows错误有关:“24核CPU,我无法移动鼠标”

这些进程都是从NtGdiCloseProcess中释放锁定的。

因此,如果FileStream在操作系统中使用并持有这样一个关键锁,则对于每个文件它都会等待几微秒,这将累加成数千个文件。 它可能是不同的锁,但上述提到的错误至少添加了类似问题的可能性。

要证明或否定这个假设,需要深入了解内核的内部工作原理。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接