【问题标题】:How to increase or keep starting speed of copying a file如何提高或保持复制文件的启动速度
【发布时间】:2016-08-10 01:52:35
【问题描述】:

我正在使用这些代码来复制一个大文件:

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。

在最初的 7 秒内,它最多可以复制 400 MB,然后这个火热的速度就会平静下来。

会发生什么以及如何保持这个热速度甚至提高它?

还有一个问题: 复制文件后,windows 仍在处理目标文件(大约 10 秒)。

复制时间:116 秒

加时:10-15 秒甚至更多

如何消除或减少这个额外的时间?

【问题讨论】:

  • 为什么不试试 XCopy
  • 你为什么还要自己做呢?首先通过 CLR 运行每个字节会损失大部分性能。使用File.Copy 会显着加快速度。
  • @Toxantron File.Copy 需要 146 秒,但这种方法需要 116 秒。
  • 老实说这毫无意义。 File.Copy 是基于 Win32 API 构建的,其性能应该比您自己编写的同步副本至少高出 2 倍。例如,与此线程进行比较。 stackoverflow.com/questions/1246899/…

标签: c# file-copying


【解决方案1】:

会发生什么?主要是缓存。

操作系统假装您在 7 秒内复制了 400 MiB,但您没有。您刚刚将 400 MiB 发送到操作系统(或文件系统)以供将来写入,这与缓冲区可以占用的一样多。如果您尝试写入一个 400 MiB 的文件,并且在“完成”后立即拔掉插头,您的文件将不会被写入。同样的事情处理“超时” - 您的应用程序已将其所有内容发送到缓冲区,但缓冲区尚未写入驱动器本身(它的缓冲区,甚至更慢的实际物理盘片)。

这在 USB 闪存驱动器中尤其明显,它往往会大量使用缓存。这使得使用(通常非常慢的)驱动器更加愉快,但您必须等待操作系统完成写入所有内容才能将驱动器拉出(这就是您获得“安全删除”图标的原因)。

所以很明显,您不能真正缩短 total 时间。您所能做的就是尝试让用户界面更好地反映现实,让用户不会看到“前 400 MiB 这么快!”事情......但它并没有真正运作良好。无论如何,您的读->写速度是 ~30 MiB/s。操作系统只是隐藏了峰值,以便更轻松地处理慢速硬盘驱动器 - 在处理大量小文件时非常有用,在处理大于缓冲区的文件时毫无价值。

当您直接使用 FileStream 构造函数而不是使用 File.OpenWrite 时,您对此有一些控制 - 您可以使用 FileOptions.WriteThrough 指示操作系统避免任何缓存并直接写入磁盘[1] ,让您更好地了解真实的写入速度。但是请注意,这通常会使 total 时间变长,并且可能会使并发访问变得更糟。您绝对不想将它用于小文件。

[1] - 哈哈,对。驱动器通常有自己的缓存,有些忽略操作系统的请求。运气不好。

【讨论】:

  • 这是一个旧线程,不确定我是否可以在此处发布,但是速度会慢到停止吗?我现在正在复制大量数据,速度下降了很多。恐怕如果降到0,它会停止而不会完全复制所有数据。如何快速复制大量数据?我现在该怎么办?
  • @StephanieChen-Xu 如果复制完全停止,则可能存在一些问题。这通常发生在连接中断时的网络传输中 - 它可能会恢复。这也可能意味着其中一个驱动器已损坏。复制工具显示的速度是平均的,因此很遗憾传输完全停止了一段时间,但您暂时无法判断。
【解决方案2】:

您可以尝试的一件事是增加缓冲区大小。当写缓存无法再跟上时,这真的很重要(如其他答案中所述)。写很多小块通常比写几个大块要慢。尝试 1 MB、4 MB 甚至更大,而不是 64 kB:

const int CopyBufferSize = 1 * 1024 * 1024;   // 1 MB
// or
const int CopyBufferSize = 4 * 1024 * 1024;   // 4 MB

【讨论】:

  • 增加 Copy-Buffer-Size 会惊人地增加复制时间。减小缓冲区大小也会增加复制时间。我不知道为什么,但它发生了。 64 KB 是我的最佳缓冲区大小。
  • 这种情况下我要问:F:是网络驱动器吗?如果是这样,可能没有太多帮助。瓶颈很可能是网络(例如拥塞的 WiFi 和/或建筑物的偏远角落)或缓慢或繁忙的服务器(旧 CPU、旧 HD?)。如果 F: 是一个不太好的 USB 驱动器,可能会出现类似的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-01-03
  • 1970-01-01
  • 1970-01-01
  • 2016-11-23
  • 2014-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多