c# - txt - Cómo dividir archivos grandes de manera eficiente
partir archivos grandes (5)
Me gustaría saber cómo puedo dividir un archivo grande sin utilizar demasiados recursos del sistema. Actualmente estoy usando este código:
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++;
}
}
}
La operación toma 52.370 segundos para dividir un archivo de 1.6GB en archivos de 14mb. No me preocupa cuánto tiempo lleva la operación, me preocupa más el recurso del sistema utilizado, ya que esta aplicación se implementará en un entorno de alojamiento compartido. Actualmente, esta operación maximiza el uso de HDD IO de mis sistemas al 100% y ralentiza considerablemente mi sistema. El uso de la CPU es bajo; RAM aumenta un poco, pero parece estar bien.
¿Hay alguna manera en que pueda restringir esta operación usando demasiados recursos?
Gracias
Actualmente esta operación maximiza el uso de HDD IO de mis sistemas al 100%.
Esto es lógico: la IO será su factor limitante y es probable que su sistema tenga la misma IO de la mayoría de las computadoras (un disco lento, no un RAID 10 de discos de alto rendimiento).
Puedes usar un trozo decente (1mb hacia arriba) para reducir las lecturas y escrituras pequeñas, pero al final eso es todo lo que PUEDES hacer. O consigue un subsistema de disco más rápido.
Este es un problema para su anfitrión, no para usted. Suponiendo que esto es absolutamente lo que debe hacer, entonces lo está haciendo de la manera más eficiente posible. Depende de ellos administrar los recursos de acuerdo con la carga, la prioridad, el SLA, etc., de la misma manera que su Hypervisor / VM / OS / App Server / lo que sea que haga.
¡Divida los archivos y use las instalaciones que ha pagado!
He modificado un poco el código de la pregunta en caso de que quisiera dividir por partes mientras me aseguraba de que cada parte termine en un final de línea:
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++;
}
}
}
Parece extraño ensamblar cada archivo de salida en la memoria; Sospecho que debería estar ejecutando un búfer interno (quizás 20k o algo así) y llamar a Write
más frecuencia.
En última instancia, si necesita IO, necesita IO. Si desea ser cortés con un entorno de alojamiento compartido, puede agregar pausas deliberadas, tal vez breves pausas dentro del bucle interno y una pausa más larga (tal vez 1s) en el bucle externo. Esto no afectará mucho su tiempo general, pero puede ayudar a otros procesos a obtener algo de IO.
Ejemplo de un búfer para el bucle interno:
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
}
}
}
Una opción que tiene es la limitación de la operación. Si, por ejemplo, devuelve el búfer a un tamaño más pequeño (en algún lugar entre 4K y 1MB) y coloca un Thread.Sleep entre las operaciones, utilizará menos recursos.