2010-10-19 24 views
18

Me gustaría saber cómo puedo dividir un archivo grande sin utilizar demasiados recursos del sistema. Actualmente estoy usando este código:Cómo dividir archivos grandes de manera eficiente

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 se lleva 52.370 segundos para dividir un archivo de 1,6 GB en archivos de 14MB. No me preocupa cuánto tiempo dura la operación. Me preocupan más los recursos del sistema utilizados, 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 forma en que pueda restringir esta operación de usar demasiados recursos?

Gracias

+0

se puede no ejecutarlo en un hilo separado de menor prioridad ? – w69rdy

+0

@ w69rdy: tenga en cuenta que "el uso de la CPU es bajo": la CPU no es el cuello de botella aquí. –

+0

@Marc Ok fair point – w69rdy

Respuesta

19

Parece extraño ensamblar cada archivo de salida en la memoria; Sospecho que deberías ejecutar un búfer interno (tal vez 20k o algo así) y llamar al Write con 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 pausas cortas dentro del ciclo interno y una pausa más larga (quizás 1s) en el ciclo externo. Esto no afectará mucho su tiempo general, pero puede ayudar a otros procesos a obtener IO.

Ejemplo de un tampón para el interior de bucle:

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 
     } 
    } 
} 
0

Actualmente no mis sistemas de uso del disco duro IO esta operación de máximo al 100%.

Esto es lógico - IO va a ser el factor limitante, y su sistema probbably tiene el mismo IO chungo de la mayoría de las computadoras (un disco lento, no es un RAID 10 de discos de alto rendimiento).

Puede usar un trozo decente sze (1mb hacia arriba) para reducir las lecturas y escrituras pequeñas, pero al final es TODO lo que PUEDE hacer. O consigue un subsistema de disco más rápido.

+0

Ah.No.La mayoría de los hosters ignoran el lado IO. INCURSIÓN tal vez, pero luego discos baratos. Un buen rendimiento es costoso. Obtengo IO estable de aproximadamente 400mb/s - en 10 (!) Velociraptors. Solo los discos cuestan casi 3000 USD;) – TomTom

0

Una opción que tienes es estrangulando la operación. Si, por ejemplo, devuelva el búfer a un tamaño más pequeño (entre 4K y 1MB) y ponga un Thread.Sleep entre las operaciones, usará menos recursos.

0

Este es un problema para su anfitrión, no para usted. Suponiendo que esto es absolutamente lo que necesita hacer, entonces lo hará de la forma más eficiente posible. Depende de ellos administrar los recursos según la carga, la prioridad, el SLA, etc. de la misma manera que su Hypervisor/VM/OS/App Server/lo que sea.

¡Separe los archivos y use las instalaciones que ha pagado!

1

he modificado el código en la pregunta un poco en caso de que quería dividir por trozos mientras se asegura de cada fragmento termina 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++; 
      } 
     } 
    } 
Cuestiones relacionadas