Подтвердить что ты не робот

Как эффективно распределять большие файлы

Я хотел бы знать, как я могу разделить большой файл, не используя слишком много системных ресурсов. В настоящее время я использую этот код:

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,6 ГБ в файлы размером 14 МБ. Меня не волнует, как долго длится операция, меня больше беспокоит системный ресурс, используемый, поскольку это приложение будет развернуто в общедоступной среде хостинга. В настоящее время эта операция максимизирует использование моего жесткого диска IO на 100% и значительно замедляет мою систему. Низкое потребление ЦП; RAM немного нарастает, но кажется прекрасным.

Есть ли способ ограничить эту операцию от использования слишком большого количества ресурсов?

Спасибо

4b9b3361

Ответ 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
        }
    }
}

Ответ 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

В настоящее время эта операция максимизирует мой системного жесткого диска на 100%.

Это логично: IO будет вашим лимитирующим фактором, и ваша система, вероятно, будет иметь один и тот же дерьмовый IO большинства компьютеров (один медленный диск, а не RAID 10 высокопроизводительных дисков).

Вы можете использовать приличный chunk sze (1mb вверх) для уменьшения небольших чтений и записи, но в конце, который вы можете сделать. Или получить более быструю подсистему диска.

Ответ 4

У вас есть опция дросселирования операции. Если вы, например, верните буфер на меньший размер (где-то между 4K и 1MB) и поместите Thread.Sleep между операциями, вы будете использовать меньше ресурсов.

Ответ 5

Это проблема для вашего хоста, а не для вас. Предполагая, что это абсолютно то, что вам нужно сделать, в значительной степени вы делаете это наиболее эффективным способом. Это зависит от них, чтобы управлять ресурсами в соответствии с нагрузкой, приоритетом, SLA и т.д. Таким же образом, как ваш Hypervisor/VM/OS/App Server/что-то делает.

Разделите файлы и воспользуйтесь услугами, за которые вы заплатили!