如何有效地拆分大文件

时间:2010-10-19 10:47:50

标签: c# .net

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

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++;
        }
    }
}

将1.6GB文件拆分为14mb文件需要52.370秒。我不关心操作需要多长时间,我更关心使用的系统资源,因为这个应用程序将部署到共享托管环境。目前,此操作最大化了我的系统HDD IO使用率100%,并大大减慢了我的系统速度。 CPU使用率低; RAM略微上升,但看起来很好。

有没有办法可以限制此操作使用太多资源?

由于

5 个答案:

答案 0 :(得分:27)

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

最终,如果您需要IO,则需要IO。如果你想对共享的托管环境保持礼貌,你可以添加故意的暂停 - 可能是内循环中的短暂暂停,以及外循环中的较长暂停(可能是1s)。这不会对您的总体时间造成太大影响,但可能会帮助其他进程获得一些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
        }
    }
}

答案 1 :(得分:1)

我已经修改了问题中的代码,以防你想要按块分割,同时确保每个块结束一行结束:

    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++;
            }
        }
    }

答案 2 :(得分:0)

  

目前这个操作最大化了我的   系统HDD IO使用率为100%。

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

你可以使用一个体面的chunk sze(向上1mb)来减少小的读写,但最后你可以这样做。或者获得更快的光盘子系统。

答案 3 :(得分:0)

您拥有的选项会限制操作。如果你是将缓冲区恢复到较小的大小(介于4K和1MB之间)并在操作之间放置Thread.Sleep,您将使用更少的资源。

答案 4 :(得分:0)

这对您的主人来说是一个问题,而不是您。假设这绝对是你需要做的事情,那么你几乎可以以最有效的方式做到这一点。他们可以根据负载,优先级,SLA等来管理资源,就像您的Hypervisor / VM / OS / App Server /无论如何一样。

将文件拆开并使用您已支付的设施!