C# 为什么我看到这两种实现在拷贝时间上存在差异?
我已经编写了一个应用程序,它实现了一个文件副本,如下所示。我想知道为什么在尝试从一个网络驱动器复制到另一个网络驱动器时,复制时间非常长(复制300mb文件需要20-30分钟),代码如下:C# 为什么我看到这两种实现在拷贝时间上存在差异?,c#,.net,io,copy,C#,.net,Io,Copy,我已经编写了一个应用程序,它实现了一个文件副本,如下所示。我想知道为什么在尝试从一个网络驱动器复制到另一个网络驱动器时,复制时间非常长(复制300mb文件需要20-30分钟),代码如下: public static void CopyFileToDestination(string source, string dest) { _log.Debug(string.Format("Copying file {0} to {1}", source, dest));
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,时间与预期一致
有人有什么见解吗?这可能是因为我们正在以小块方式进行复制吗?在实际复制之前调用writer流上的方法,这将减少目标磁盘的操作
像这样
writer.SetLength(totalBytesToTransfer);
在使用Seek调用此方法后,可能需要将流的PSION设置回起始位置。在调用SetLength后检查流的位置,应该仍然为零
writer.Seek(0, SeekOrigin.Begin); // Not sure on that one
如果速度仍然太慢,请在实际复制之前在写入程序流上使用调用该方法,这将减少目标磁盘的操作
像这样
writer.SetLength(totalBytesToTransfer);
在使用Seek调用此方法后,可能需要将流的PSION设置回起始位置。在调用SetLength后检查流的位置,应该仍然为零
writer.Seek(0, SeekOrigin.Begin); // Not sure on that one
如果速度仍然太慢,请在实际复制之前在写入程序流上使用调用该方法,这将减少目标磁盘的操作
像这样
writer.SetLength(totalBytesToTransfer);
在使用Seek调用此方法后,可能需要将流的PSION设置回起始位置。在调用SetLength后检查流的位置,应该仍然为零
writer.Seek(0, SeekOrigin.Begin); // Not sure on that one
如果速度仍然太慢,请在实际复制之前在写入程序流上使用调用该方法,这将减少目标磁盘的操作
像这样
writer.SetLength(totalBytesToTransfer);
在使用Seek调用此方法后,可能需要将流的PSION设置回起始位置。在调用SetLength后检查流的位置,应该仍然为零
writer.Seek(0, SeekOrigin.Begin); // Not sure on that one
如果仍然太慢,请使用更改
buf
变量的大小不会更改与文件系统通信时使用的FileStream.Read
或FileStream.Write
缓冲区的大小。要查看缓冲区大小的任何更改,必须在打开文件时指定缓冲区大小
我记得,默认的缓冲区大小是4K。我不久前做的性能测试表明,最佳点在64K和256K之间,64K始终是最佳选择
您应该将文件.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
自动忽略删除不存在文件的尝试。更改buf
变量的大小不会更改FileStream.Read
或FileStream.Write
在与文件系统通信时使用的缓冲区大小。要查看缓冲区大小的任何更改,必须在打开文件时指定缓冲区大小
我记得,默认的缓冲区大小是4K。我不久前做的性能测试表明,最佳点在64K和256K之间,64K始终是最佳选择
您应该将文件.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
自动忽略删除不存在文件的尝试。更改buf
变量的大小不会更改FileStream.Read
或FileStream.Write
在与文件系统通信时使用的缓冲区大小。要查看缓冲区大小的任何更改,必须在打开文件时指定缓冲区大小
我记得,默认的缓冲区大小是4K。我不久前做的性能测试表明,最佳点在64K和256K之间,64K更容易出错