为什么我看到这两种实现之间的复制时间不同

本文关键字:复制 时间 之间 实现 我看 两种 为什么 | 更新日期: 2023-09-27 17:58:23

我编写了一个应用程序,它实现了如下所示的文件副本。我想知道为什么,当试图从网络驱动器复制到另一个网络驱动器时,复制时间非常长(复制一个300mb的文件需要20-30分钟),代码如下:

 public static void CopyFileToDestination(string source, string dest)
    {
        _log.Debug(string.Format("Copying file {0} to {1}", source, dest));
        DateTime start = DateTime.Now;
        string destinationFolderPath = Path.GetDirectoryName(dest);
        if (!Directory.Exists(destinationFolderPath))
        {
            Directory.CreateDirectory(destinationFolderPath);
        }
        if (File.Exists(dest))
        {
            File.Delete(dest);
        }
        FileInfo sourceFile = new FileInfo(source);
        if (!sourceFile.Exists)
        {
            throw new FileNotFoundException("source = " + source);
        }
        long totalBytesToTransfer = sourceFile.Length;
        if (!CheckForFreeDiskSpace(dest, totalBytesToTransfer))
        {
            throw new ApplicationException(string.Format("Unable to copy file {0}: Not enough disk space on drive {1}.",
                source, dest.Substring(0, 1).ToUpper()));
        }
        long bytesTransferred = 0;
        using (FileStream reader = sourceFile.OpenRead())
        {
            using (FileStream writer = new FileStream(dest, FileMode.OpenOrCreate, FileAccess.Write))
            {
                byte[] buf = new byte[64 * 1024];
                int bytesRead = reader.Read(buf, 0, buf.Length);
                double lastPercentage = 0;
                while (bytesRead > 0)
                {
                    double percentage = ((float)bytesTransferred / (float)totalBytesToTransfer) * 100.0;
                    writer.Write(buf, 0, bytesRead);
                    bytesTransferred += bytesRead;
                    if (Math.Abs(lastPercentage - percentage) > 0.25)
                    {
                        System.Diagnostics.Debug.WriteLine(string.Format("{0} : Copied {1:#,##0} of {2:#,##0} MB ({3:0.0}%)",
                            sourceFile.Name,
                            bytesTransferred / (1024 * 1024),
                            totalBytesToTransfer / (1024 * 1024),
                            percentage));
                        lastPercentage = percentage;
                    }
                    bytesRead = reader.Read(buf, 0, buf.Length);
                }
            }
        }
        System.Diagnostics.Debug.WriteLine(string.Format("{0} : Done copying", sourceFile.Name));
        _log.Debug(string.Format("{0} copied in {1:#,##0} seconds", sourceFile.Name, (DateTime.Now - start).TotalSeconds));
    }

但是,使用一个简单的File.Copy,时间正如预期的那样。

有人有什么见解吗?这可能是因为我们正在以小块的形式复制吗?

为什么我看到这两种实现之间的复制时间不同

更改buf变量的大小不会更改FileStream.ReadFileStream.Write在与文件系统通信时使用的缓冲区的大小。要查看缓冲区大小的任何更改,必须在打开文件时指定缓冲区大小。

我记得,默认的缓冲区大小是4K。我不久前做的性能测试表明,最佳点在64K到256K之间,64K始终是最佳选择。

您应该将File.OpenRead()更改为:

new FileStream(sourceFile.FullName, FileMode.Open, FileAccess.Read, FileShare.None, BufferSize)

如果不希望独占访问,请更改FileShare值,并将BufferSize声明为与所需缓冲区大小相等的常量。我使用64*1024

此外,将打开输出文件的方式更改为:

new FileStream(dest, FileMode.Create, FileAccess.Write, FileShare.None, BufferSize)

请注意,我使用了FileMode.Create而不是FileMode.OpenOrCreate。如果您使用OpenOrCreate,并且源文件比现有的目标文件小,我认为在您完成编写时不会截断该文件。因此,目标文件将包含无关的数据。

也就是说,我不希望这会将您的复制时间从20-30分钟减少到几秒钟。我想,如果每一次低级读取都需要一次网络调用,那就可以了。使用默认的4K缓冲区,您将对文件系统进行16次读取调用,以填充64K缓冲区。因此,通过增加缓冲区大小,可以大大减少代码进行的操作系统调用次数(以及潜在的网络事务次数)。

最后,在删除文件之前,不需要检查文件是否存在。File.Delete会自动忽略删除不存在文件的尝试。

在实际复制之前,在编写器Stream上调用SetLength方法,这应该会减少目标磁盘的操作。

像这样

writer.SetLength(totalBytesToTransfer);

在使用Seek调用此方法后,您可能需要将Stream的psotion设置回起始位置。调用SetLength后检查流的位置,应该仍然为零。

writer.Seek(0, SeekOrigin.Begin); // Not sure on that one

如果仍然太慢,请使用SetFileValidData