为什么我看到这两种实现之间的复制时间不同
本文关键字:复制 时间 之间 实现 我看 两种 为什么 | 更新日期: 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.Read
或FileStream.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