我的问题在于文件复制性能。我们有一个媒体管理系统,需要在文件系统上大量移动文件到不同的位置,包括同一网络上的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();
}
}
}
有谁知道为什么它的执行速度比File.Copy慢得多?我能做些什么来提高性能吗?我是否只需要使用特殊逻辑来查看我是否从一个窗口位置复制到另一个窗口位置 - 在这种情况下我只使用File.Copy而在其他情况下我将使用流?
请告诉我您的想法以及是否需要其他信息。我尝试了不同的缓冲区大小,似乎64k缓冲区大小对于我们的“小”文件来说是最佳的,256k +对于我们的“大”文件来说是一个更好的缓冲区大小 - 但无论哪种情况下它都比File.Copy执行得更糟糕( )。提前致谢!
答案 0 :(得分:23)
File.Copy是围绕CopyFile Win32功能构建的,这个功能需要MS工作人员的大量关注(请记住这个与Vista相关的慢速复制性能线程)。
提高方法性能的几条线索:
异步复制模式示例:
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);
答案 1 :(得分:7)
除掉反射器后,我们可以看到File.Copy实际上调用了Win32 API:
if (!Win32Native.CopyFile(fullPathInternal, dst, !overwrite))
哪个解析为
[DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
internal static extern bool CopyFile(string src, string dst, bool failIfExists);
答案 2 :(得分:6)
使用自己的代码做一些如此有趣的事情,你永远无法击败操作系统,即使你在汇编程序中仔细精心制作它也是如此。
如果您需要确保您的操作以最佳性能发生并且您希望混合和匹配各种源,那么您将需要创建一个描述资源位置的类型。然后,您创建一个具有Copy
等函数的API,它采用两种类型,并检查两者的描述,选择性能最佳的复制机制。例如,在确定两个位置都是Windows文件位置之后,您将选择File.Copy或如果源是Windows文件,但目标是HTTP POST,则它使用WebRequest。
答案 3 :(得分:4)
三项改变将大大提高绩效:
在我尝试的实验中,这似乎快了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);
}
}
}
答案 4 :(得分:1)
尝试删除Flush调用,并将其移至循环外部。
有时,操作系统最清楚何时刷新IO ..它允许它更好地使用其内部缓冲区。
答案 5 :(得分:1)
答案 6 :(得分:1)
马克·鲁西诺维奇将是这方面的权威。
他在blog上写了一个条目Inside Vista SP1 File Copy Improvements,它通过Vista SP1总结了Windows最先进的技术。
我的半受教育的猜测是File.Copy在最多的情况下将是最强大的。当然,这并不意味着在某些特定的角落情况下,您自己的代码可能会击败它......
答案 7 :(得分:0)
有一点值得注意的是,你正在阅读一大块,写下那块,阅读另一块,等等。
流式传输操作是多线程的理想选择。我的猜测是File.Copy实现了多线程。
尝试在一个线程中读取并在另一个线程中写入。您将需要协调线程,以便写入线程不会开始写入缓冲区,直到读取线程完成填充。您可以通过使用两个缓冲区来解决这个问题,一个缓冲区正在读取另一个缓冲区,另一个缓冲区当前正在使用哪个缓冲区。