【问题标题】:How to split large files efficiently如何高效分割大文件
【发布时间】:2010-10-19 10:47:50
【问题描述】:

我想知道如何在不使用太多系统资源的情况下拆分大文件。 我目前正在使用此代码:

public static void SplitFile(string inputFile, int chunkSize, string path)
{
    byte[] buffer = new byte[chunkSize];

    using (Stream input = File.OpenRead(inputFile))
    {
        int index = 0;
        while (input.Position < input.Length)
        {
            using (Stream output = File.Create(path + "\\" + index))
            {
                int chunkBytesRead = 0;
                while (chunkBytesRead < chunkSize)
                {
                    int bytesRead = input.Read(buffer, 
                                               chunkBytesRead, 
                                               chunkSize - chunkBytesRead);

                    if (bytesRead == 0)
                    {
                        break;
                    }
                    chunkBytesRead += bytesRead;
                }
                output.Write(buffer, 0, chunkBytesRead);
            }
            index++;
        }
    }
}

该操作需要 52.370 秒才能将 1.6GB 文件拆分为 14mb 文件。我不关心操作需要多长时间,我更关心使用的系统资源,因为此应用程序将部署到共享托管环境。目前,此操作使我的系统 HDD IO 使用率达到 100%,并大大降低了我的系统速度。 CPU使用率低; RAM 增加了一点,但看起来还不错。

有没有办法限制此操作使用过多资源?

谢谢

【问题讨论】:

  • 你不能在单独的低优先级线程上运行它吗?
  • @w69rdy - 注意“CPU 使用率低” - CPU 不是这里的瓶颈。

标签: c# .net


【解决方案1】:

在内存中组装每个输出文件似乎很奇怪;我怀疑你应该运行一个内部缓冲区(可能是 20k 或其他)并更频繁地调用Write

归根结底,如果你需要 IO,你就需要 IO。如果您想对共享主机环境有礼貌,您可以添加故意的暂停 - 可能在内循环中短暂暂停,在外循环中暂停较长时间(可能 1 秒)。这不会对您的整体时间产生太大影响,但可能会帮助其他进程获得一些 IO。

内循环缓冲区示例:

public static void SplitFile(string inputFile, int chunkSize, string path)
{
    const int BUFFER_SIZE = 20 * 1024;
    byte[] buffer = new byte[BUFFER_SIZE];

    using (Stream input = File.OpenRead(inputFile))
    {
        int index = 0;
        while (input.Position < input.Length)
        {
            using (Stream output = File.Create(path + "\\" + index))
            {
                int remaining = chunkSize, bytesRead;
                while (remaining > 0 && (bytesRead = input.Read(buffer, 0,
                        Math.Min(remaining, BUFFER_SIZE))) > 0)
                {
                    output.Write(buffer, 0, bytesRead);
                    remaining -= bytesRead;
                }
            }
            index++;
            Thread.Sleep(500); // experimental; perhaps try it
        }
    }
}

【讨论】:

  • 这看起来不错,谢谢。如何将其反转以统一块?
  • 我找到了this;它似乎运作良好。
【解决方案2】:

我已经稍微修改了问题中的代码,以防您想按块拆分,同时确保每个块都以行结尾结尾:

    private static void SplitFile(string inputFile, int chunkSize, string path)
    {
        byte[] buffer = new byte[chunkSize];
        List<byte> extraBuffer = new List<byte>();

        using (Stream input = File.OpenRead(inputFile))
        {
            int index = 0;
            while (input.Position < input.Length)
            {
                using (Stream output = File.Create(path + "\\" + index + ".csv"))
                {
                    int chunkBytesRead = 0;
                    while (chunkBytesRead < chunkSize)
                    {
                        int bytesRead = input.Read(buffer,
                                                   chunkBytesRead,
                                                   chunkSize - chunkBytesRead);

                        if (bytesRead == 0)
                        {
                            break;
                        }

                        chunkBytesRead += bytesRead;
                    }

                    byte extraByte = buffer[chunkSize - 1];
                    while (extraByte != '\n')
                    {
                        int flag = input.ReadByte();
                        if (flag == -1)
                            break;
                        extraByte = (byte)flag;
                        extraBuffer.Add(extraByte);
                    }

                    output.Write(buffer, 0, chunkBytesRead);
                    if (extraBuffer.Count > 0)
                        output.Write(extraBuffer.ToArray(), 0, extraBuffer.Count);

                    extraBuffer.Clear();
                }
                index++;
            }
        }
    }

【讨论】:

    【解决方案3】:

    目前此操作最大已超出我的范围 系统 HDD IO 使用率为 100%。

    这是合乎逻辑的 - IO 将成为您的限制因素,并且您的系统可能具有与大多数计算机相同的糟糕 IO(一张慢速磁盘,而不是高性能磁盘的 RAID 10)。

    您可以使用合适的块大小(向上 1mb)来减少小型读取和写入,但最终您只能这样做。或者获得更快的磁盘子系统。

    【讨论】:

    • Ah.No. 大多数主机忽略 IO 端。也许是 RAID,但便宜的光盘。好的性能是昂贵的。我在 10 个(!)Velociraptors 上获得了大约 400mb/s 的稳定 IO。光是光盘就花了将近 3000 美元;)
    【解决方案4】:

    您可以选择限制操作。如果你例如将缓冲区恢复到较小的大小(4K 到 1MB 之间)并在操作之间放置一个 Thread.Sleep,您将使用更少的资源。

    【讨论】:

      【解决方案5】:

      这是您的主机的问题,而不是您。假设这绝对是您需要做的事情,那么您几乎是以最有效的方式来做的。由他们根据负载、优先级、SLA 等以与您的 Hypervisor/VM/OS/App Server/其他方式相同的方式管理资源。

      拆分文件并使用您已付费的设施!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-01-02
        • 2023-03-12
        • 2018-12-18
        • 2014-01-21
        • 2013-01-25
        • 1970-01-01
        相关资源
        最近更新 更多