Как эффективно распределять большие файлы
Я хотел бы знать, как я могу разделить большой файл, не используя слишком много системных ресурсов.
В настоящее время я использую этот код:
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 немного нарастает, но кажется прекрасным.
Есть ли способ ограничить эту операцию от использования слишком большого количества ресурсов?
Спасибо
Ответы
Ответ 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/что-то делает.
Разделите файлы и воспользуйтесь услугами, за которые вы заплатили!