如何用C#编写超高速文件流代码?

ala*_*ala 39 c# cpu streaming performance utilization

我必须将一个巨大的文件拆分成许多较小的文件.每个目标文件由偏移量和长度定义为字节数.我正在使用以下代码:

private void copy(string srcFile, string dstFile, int offset, int length)
{
    BinaryReader reader = new BinaryReader(File.OpenRead(srcFile));
    reader.BaseStream.Seek(offset, SeekOrigin.Begin);
    byte[] buffer = reader.ReadBytes(length);

    BinaryWriter writer = new BinaryWriter(File.OpenWrite(dstFile));
    writer.Write(buffer);
}
Run Code Online (Sandbox Code Playgroud)

考虑到我必须将此功能调用大约100,000次,因此速度非常慢.

  1. 有没有办法让Writer直接连接到Reader?(也就是说,实际上没有将内容加载到内存中的Buffer中.)

Jon*_*eet 46

我不相信.NET中有任何东西允许复制文件的一部分而不在内存中缓冲它.然而,无论如何,这对我来说是低效的,因为它需要打开输入文件并多次搜索.如果您只是拆分文件,为什么不打开输入文件一次,然后只写下:

public static void CopySection(Stream input, string targetFile, int length)
{
    byte[] buffer = new byte[8192];

    using (Stream output = File.OpenWrite(targetFile))
    {
        int bytesRead = 1;
        // This will finish silently if we couldn't read "length" bytes.
        // An alternative would be to throw an exception
        while (length > 0 && bytesRead > 0)
        {
            bytesRead = input.Read(buffer, 0, Math.Min(length, buffer.Length));
            output.Write(buffer, 0, bytesRead);
            length -= bytesRead;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这在每次调用时创建缓冲区的效率很小 - 您可能希望创建一次缓冲区并将其传递给方法:

public static void CopySection(Stream input, string targetFile,
                               int length, byte[] buffer)
{
    using (Stream output = File.OpenWrite(targetFile))
    {
        int bytesRead = 1;
        // This will finish silently if we couldn't read "length" bytes.
        // An alternative would be to throw an exception
        while (length > 0 && bytesRead > 0)
        {
            bytesRead = input.Read(buffer, 0, Math.Min(length, buffer.Length));
            output.Write(buffer, 0, bytesRead);
            length -= bytesRead;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这也会关闭原始代码没有的输出流(由于using语句).

重要的是,这将更有效地使用操作系统文件缓冲,因为您重用相同的输入流,而不是在开始时重新打开文件然后寻求.

认为它会明显加快,但显然你需要尝试看看......

当然,这假定是连续的块.如果您需要跳过文件的位,可以从方法外部执行此操作.此外,如果您正在编写非常小的文件,您可能也希望针对该情况进行优化 - 最简单的方法可能是引入BufferedStream包装输入流.

  • @ Smudge202:鉴于这是在执行IO,对Math.Min的调用肯定不会*在性能方面与*相关.同时具有长度参数和缓冲区长度的目的是允许您重用可能过大的缓冲区. (2认同)

Bob*_*yan 26

从C#执行文件I/O的最快方法是使用Windows ReadFile和WriteFile函数.我编写了一个C#类来封装这个功能,以及一个查看不同I/O方法的基准测试程序,包括BinaryReader和BinaryWriter.请参阅我的博文:

http://designingefficientsoftware.wordpress.com/2011/03/03/efficient-file-io-from-csharp/


Mar*_*ell 6

有多大length?您可以更好地重复使用固定大小(中等大小但不淫秽)的缓冲区,并忘记BinaryReader...只需使用Stream.ReadStream.Write.

(编辑)类似于:

private static void copy(string srcFile, string dstFile, int offset,
     int length, byte[] buffer)
{
    using(Stream inStream = File.OpenRead(srcFile))
    using (Stream outStream = File.OpenWrite(dstFile))
    {
        inStream.Seek(offset, SeekOrigin.Begin);
        int bufferLength = buffer.Length, bytesRead;
        while (length > bufferLength &&
            (bytesRead = inStream.Read(buffer, 0, bufferLength)) > 0)
        {
            outStream.Write(buffer, 0, bytesRead);
            length -= bytesRead;
        }
        while (length > 0 &&
            (bytesRead = inStream.Read(buffer, 0, length)) > 0)
        {
            outStream.Write(buffer, 0, bytesRead);
            length -= bytesRead;
        }
    }        
}
Run Code Online (Sandbox Code Playgroud)