如何提高或保持复制文件的启动速度
How to increase or keep starting speed of copying a file
我正在使用这些代码复制一个大文件:
const int CopyBufferSize = 64 * 1024;
string src = @"F:\Test\src\Setup.exe";
string dst = @"F:\Test\dst\Setup.exe";
public void CopyFile()
{
Stream input = File.OpenRead(src);
long length = input.Length;
byte[] buffer = new byte[CopyBufferSize];
Stopwatch swTotal = Stopwatch.StartNew();
Invoke((MethodInvoker)delegate
{
progressBar1.Maximum = (int)Math.Abs(length / CopyBufferSize) + 1;
});
using (Stream output = File.OpenWrite(dst))
{
int bytesRead = 1;
// This will finish silently if we couldn't read "length" bytes.
// An alternative would be to throw an exception
while (length > 0 && bytesRead > 0)
{
bytesRead = input.Read(buffer, 0, Math.Min(CopyBufferSize, buffer.Length));
output.Write(buffer, 0, bytesRead);
length -= bytesRead;
Invoke((MethodInvoker)delegate
{
progressBar1.Value++;
label1.Text = (100 * progressBar1.Value / progressBar1.Maximum).ToString() + " %";
label3.Text = ((int)swTotal.Elapsed.TotalSeconds).ToString() + " Seconds";
});
}
Invoke((MethodInvoker)delegate
{
progressBar1.Value = progressBar1.Maximum;
});
}
Invoke((MethodInvoker)delegate
{
swTotal.Stop();
Console.WriteLine("Total time: {0:N4} seconds.", swTotal.Elapsed.TotalSeconds);
label3.Text += ((int)swTotal.Elapsed.TotalSeconds - int.Parse(label3.Text.Replace(" Seconds",""))).ToString() + " Seconds";
});
}
文件大小约为 4 GB。
In the first 7 seconds it can copy up to 400 MB then this hot speed calms down.
发生了什么,如何保持甚至提高这个热速度?
这里还有一个问题:
复制文件后,windows 仍在处理目标文件(大约 10 秒)。
Copy Time: 116 seconds
extra time: 10-15 seconds or even more
如何删除或减少这个额外时间?
会发生什么?主要是缓存。
OS 假装您在 7 秒内复制了 400 MiB,但您没有。您刚刚将 400 MiB 发送到 OS(或文件系统)以供将来写入,这就是缓冲区可以占用的量。如果您尝试写入一个 400 MiB 的文件,并且在它 "done" 时立即拔下插头,您的文件将不会被写入。同样的事情处理 "overtime" - 您的应用程序已将其所有内容发送到缓冲区,但缓冲区尚未写入驱动器本身(它的缓冲区,或者更慢的实际物理盘片)。
这对于经常使用高速缓存的 USB 闪存驱动器尤其明显。这使得使用(通常非常慢的)驱动器变得更加愉快,需要权衡的是您必须等待 OS 在拉出驱动器之前完成所有写入(这就是为什么您得到 "Safe remove" 图标).
所以很明显,您不能真正缩短 总 时间。您所能做的就是尝试让用户界面更好地反映现实,这样用户就不会看到 "first 400 MiB are so fast!" 的东西……但它并不能很好地工作。无论如何,你的读->写速度是~30 MiB/s。 OS 只是隐藏峰值,以便更轻松地处理慢速硬盘驱动器 - 在处理大量小文件时非常有用,在处理大于缓冲区的文件时毫无价值。
当您直接使用 FileStream
构造函数而不是使用 File.OpenWrite
时,您可以对此进行一些控制 - 您可以使用 FileOptions.WriteThrough
来指示 OS避免任何缓存并直接写入磁盘[1],让您更好地了解实际写入速度。请注意,这通常会使 total 时间变大,但它可能会使并发访问变得更糟。你肯定不想用它来处理小文件。
[1] - 哈哈,对。驱动器通常有自己的缓存,有些会忽略 OS' 请求。运气不好。
您可以尝试的一件事是增加缓冲区大小。当写缓存不能再跟上时(如其他答案中所述),这真的很重要。写很多小块通常比写几个大块慢。尝试使用 1 MB、4 MB 或更大的内存而不是 64 kB:
const int CopyBufferSize = 1 * 1024 * 1024; // 1 MB
// or
const int CopyBufferSize = 4 * 1024 * 1024; // 4 MB
我正在使用这些代码复制一个大文件:
const int CopyBufferSize = 64 * 1024;
string src = @"F:\Test\src\Setup.exe";
string dst = @"F:\Test\dst\Setup.exe";
public void CopyFile()
{
Stream input = File.OpenRead(src);
long length = input.Length;
byte[] buffer = new byte[CopyBufferSize];
Stopwatch swTotal = Stopwatch.StartNew();
Invoke((MethodInvoker)delegate
{
progressBar1.Maximum = (int)Math.Abs(length / CopyBufferSize) + 1;
});
using (Stream output = File.OpenWrite(dst))
{
int bytesRead = 1;
// This will finish silently if we couldn't read "length" bytes.
// An alternative would be to throw an exception
while (length > 0 && bytesRead > 0)
{
bytesRead = input.Read(buffer, 0, Math.Min(CopyBufferSize, buffer.Length));
output.Write(buffer, 0, bytesRead);
length -= bytesRead;
Invoke((MethodInvoker)delegate
{
progressBar1.Value++;
label1.Text = (100 * progressBar1.Value / progressBar1.Maximum).ToString() + " %";
label3.Text = ((int)swTotal.Elapsed.TotalSeconds).ToString() + " Seconds";
});
}
Invoke((MethodInvoker)delegate
{
progressBar1.Value = progressBar1.Maximum;
});
}
Invoke((MethodInvoker)delegate
{
swTotal.Stop();
Console.WriteLine("Total time: {0:N4} seconds.", swTotal.Elapsed.TotalSeconds);
label3.Text += ((int)swTotal.Elapsed.TotalSeconds - int.Parse(label3.Text.Replace(" Seconds",""))).ToString() + " Seconds";
});
}
文件大小约为 4 GB。
In the first 7 seconds it can copy up to 400 MB then this hot speed calms down.
发生了什么,如何保持甚至提高这个热速度?
这里还有一个问题: 复制文件后,windows 仍在处理目标文件(大约 10 秒)。
Copy Time: 116 seconds
extra time: 10-15 seconds or even more
如何删除或减少这个额外时间?
会发生什么?主要是缓存。
OS 假装您在 7 秒内复制了 400 MiB,但您没有。您刚刚将 400 MiB 发送到 OS(或文件系统)以供将来写入,这就是缓冲区可以占用的量。如果您尝试写入一个 400 MiB 的文件,并且在它 "done" 时立即拔下插头,您的文件将不会被写入。同样的事情处理 "overtime" - 您的应用程序已将其所有内容发送到缓冲区,但缓冲区尚未写入驱动器本身(它的缓冲区,或者更慢的实际物理盘片)。
这对于经常使用高速缓存的 USB 闪存驱动器尤其明显。这使得使用(通常非常慢的)驱动器变得更加愉快,需要权衡的是您必须等待 OS 在拉出驱动器之前完成所有写入(这就是为什么您得到 "Safe remove" 图标).
所以很明显,您不能真正缩短 总 时间。您所能做的就是尝试让用户界面更好地反映现实,这样用户就不会看到 "first 400 MiB are so fast!" 的东西……但它并不能很好地工作。无论如何,你的读->写速度是~30 MiB/s。 OS 只是隐藏峰值,以便更轻松地处理慢速硬盘驱动器 - 在处理大量小文件时非常有用,在处理大于缓冲区的文件时毫无价值。
当您直接使用 FileStream
构造函数而不是使用 File.OpenWrite
时,您可以对此进行一些控制 - 您可以使用 FileOptions.WriteThrough
来指示 OS避免任何缓存并直接写入磁盘[1],让您更好地了解实际写入速度。请注意,这通常会使 total 时间变大,但它可能会使并发访问变得更糟。你肯定不想用它来处理小文件。
[1] - 哈哈,对。驱动器通常有自己的缓存,有些会忽略 OS' 请求。运气不好。
您可以尝试的一件事是增加缓冲区大小。当写缓存不能再跟上时(如其他答案中所述),这真的很重要。写很多小块通常比写几个大块慢。尝试使用 1 MB、4 MB 或更大的内存而不是 64 kB:
const int CopyBufferSize = 1 * 1024 * 1024; // 1 MB
// or
const int CopyBufferSize = 4 * 1024 * 1024; // 4 MB