File.Copy与手动FileStream.Write用于复制文件

jak*_*don 33 c# windows performance

我的问题在于文件复制性能.我们有一个媒体管理系统,需要在文件系统上大量移动文件到不同的位置,包括同一网络上的Windows共享,FTP站点,AmazonS3等.当我们都在一个Windows网络上时,我们可以使用System.IO.File.Copy(源,目标)复制文件.由于我们所拥有的很多次都是输入流(如MemoryStream),我们尝试抽象复制操作以获取输入流和输出流,但我们看到了大量的性能下降.下面是一些用于复制文件以用作讨论点的代码.

public void Copy(System.IO.Stream inStream, string outputFilePath)
{
    int bufferSize = 1024 * 64;

    using (FileStream fileStream = new FileStream(outputFilePath, FileMode.OpenOrCreate, FileAccess.Write))
    {

        int bytesRead = -1;
        byte[] bytes = new byte[bufferSize];

        while ((bytesRead = inStream.Read(bytes, 0, bufferSize)) > 0)
        {
            fileStream.Write(bytes, 0, bytesRead);
            fileStream.Flush();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有谁知道为什么这比File.Copy执行速度慢得多?我能做些什么来提高性能吗?我是否只需要使用特殊逻辑来查看我是否从一个窗口位置复制到另一个窗口位置 - 在这种情况下我将只使用File.Copy而在其他情况下我将使用流?

请告诉我您的想法以及是否需要其他信息.我尝试了不同的缓冲区大小,似乎64k缓冲区大小对于我们的"小"文件来说是最佳的,256k +对于我们的"大"文件来说是一个更好的缓冲区大小 - 但无论哪种情况下它都比File.Copy执行得更糟糕( ).提前致谢!

arb*_*ter 23

File.Copy是围绕CopyFile Win32功能构建的,这个功能需要MS工作人员的大量关注(请记住这个与Vista相关的慢速复制性能线程).

提高方法性能的几条线索:

  1. 像许多人说的那样早先从你的周期中删除Flush方法.你根本不需要它.
  2. 增加缓冲区可能有所帮助,但仅限于文件到文件操作,对于网络共享或ftp服务器,这将会减慢速度.60*1024是网络共享的理想选择,至少在vista之前.在大多数情况下,对于ftp 32k就足够了.
  3. 通过提供缓存策略帮助操作系统(在您的情况下顺序读取和写入),使用FileOream构造函数覆盖与FileOptions参数(SequentalScan).
  4. 你可以通过使用异步模式加速复制(对于网络到文件的情况特别有用),但是不要使用线程,而是使用重叠的io(BeginRead,EndRead,BeginWrite,EndWrite in .net),不要忘记在FileStream构造函数中设置异步选项(请参阅FileOptions)

异步复制模式的示例:

int Readed = 0;
IAsyncResult ReadResult;
IAsyncResult WriteResult;

ReadResult = sourceStream.BeginRead(ActiveBuffer, 0, ActiveBuffer.Length, null, null);
do
{
    Readed = sourceStream.EndRead(ReadResult);

    WriteResult = destStream.BeginWrite(ActiveBuffer, 0, Readed, null, null);
    WriteBuffer = ActiveBuffer;

    if (Readed > 0)
    {
      ReadResult = sourceStream.BeginRead(BackBuffer, 0, BackBuffer.Length, null, null);
      BackBuffer = Interlocked.Exchange(ref ActiveBuffer, BackBuffer);
    }

    destStream.EndWrite(WriteResult);
  }
  while (Readed > 0);
Run Code Online (Sandbox Code Playgroud)


Ed *_* S. 7

除了反射器,我们可以看到File.Copy实际上调用了Win32 API:

if (!Win32Native.CopyFile(fullPathInternal, dst, !overwrite))
Run Code Online (Sandbox Code Playgroud)

哪个解决了

[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
internal static extern bool CopyFile(string src, string dst, bool failIfExists);
Run Code Online (Sandbox Code Playgroud)

这是CopyFile的文档


Rob*_*ine 6

三个变化将显着提高性能:

  1. 增加你的缓冲区大小,尝试 1MB(好吧 - 只是实验)
  2. 打开 fileStream 后,调用 fileStream.SetLength(inStream.Length) 预先在磁盘上分配整个块(仅在 inStream 可查找时才有效)
  3. 删除 fileStream.Flush() - 它是多余的,可能对性能的影响最大,因为它会阻塞直到刷新完成。无论如何都会在处置时刷新流。

在我尝试的实验中,这似乎快了 3-4 倍:

   public static void Copy(System.IO.Stream inStream, string outputFilePath)
    {
        int bufferSize = 1024 * 1024;

        using (FileStream fileStream = new FileStream(outputFilePath, FileMode.OpenOrCreate, FileAccess.Write))
        {
            fileStream.SetLength(inStream.Length);
            int bytesRead = -1;
            byte[] bytes = new byte[bufferSize];

            while ((bytesRead = inStream.Read(bytes, 0, bufferSize)) > 0)
            {
                fileStream.Write(bytes, 0, bytesRead);
            }
       }
    }
Run Code Online (Sandbox Code Playgroud)


Ant*_*nes 6

你永远无法用你自己的代码做一些如此有趣的事情来击败操作系统,即使你在汇编程序中仔细精心制作它也是如此.

如果您需要确保您的操作以最佳性能发生并且您希望混合和匹配各种源,那么您将需要创建一个描述资源位置的类型.然后,您创建一个具有诸如Copy两种类型的函数的API,并检查两者的描述,选择性能最佳的复制机制.例如,确定两个位置都是Windows文件位置后,它会选择File.Copy OR,如果源是Windows文件,但目标是HTTP POST,则它使用WebRequest.