2010-10-19 15 views
18

Mi piacerebbe sapere come posso dividere un file di grandi dimensioni senza utilizzare troppe risorse di sistema. Attualmente sto usando questo codice:Come dividere in modo efficiente file di grandi dimensioni

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

L'operazione richiede 52.370 secondi per dividere un file 1,6 GB in file 14MB. Non sono preoccupato per quanto tempo impiega l'operazione, sono più preoccupato per le risorse di sistema utilizzate in quanto questa app verrà distribuita in un ambiente di hosting condiviso. Attualmente questa operazione è in grado di massimizzare l'utilizzo dell'IO HDD del mio sistema al 100% e rallenta notevolmente il mio sistema. L'utilizzo della CPU è basso; La RAM sale un po ', ma sembra a posto.

Esiste un modo per limitare questa operazione all'utilizzo di troppe risorse?

Grazie

+0

Potrebbe non funzionare su un separato thread con priorità più bassa ? – w69rdy

+0

@ w69rdy - nota "L'utilizzo della CPU è basso" - CPU non è il collo di bottiglia qui. –

+0

@Marc Ok punto fiera – w69rdy

risposta

19

Sembra strano assemblare ciascun file di output in memoria; Sospetto che dovresti eseguire un buffer interno (forse 20k o qualcosa del genere) e chiamare lo Write più spesso.

In definitiva, se hai bisogno di IO, hai bisogno di IO. Se si desidera essere cortesi in un ambiente di hosting condiviso, è possibile aggiungere pause intenzionali - forse brevi pause all'interno del ciclo interno e una pausa più lunga (forse 1 secondo) nel ciclo esterno. Questo non influenzerà molto il tempo complessivo, ma potrebbe aiutare altri processi a ottenere un po 'di IO.

Esempio di un tampone per l'anello 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 
     } 
    } 
} 
0

attualmente i miei sistemi uso di HDD IO questa operazione di massimo al 100%.

Questo è logico - IO sta per essere il vostro fattore limitante, e il sistema ha probbably la stessa merda IO della maggior parte dei computer (un lento disco, non un RAID 10 di dischi ad alte prestazioni).

È possibile utilizzare un sunk chunk decente (1mb verso l'alto) per ridurre le letture e le scritture di piccole dimensioni, ma alla fine è tutto ciò che si può fare. Oppure ottieni un sottosistema di dischi più veloce.

+0

Ah.No.Molti hosters ignorano il lato IO. Forse RAID, ma poi dischi economici. Le buone prestazioni sono costose. Ottengo circa 400 MB/s di IO stabile - su 10 (!) Velociraptor. I dischi da soli costano quasi 3000 USD;) – TomTom

0

Un'opzione avete è throttling l'operazione. Se ad es. riporta il buffer ad una dimensione più piccola (da qualche parte tra 4K e 1MB) e metti un Thread.Sleep tra le operazioni, userai meno risorse.

0

Questo è un problema per il tuo host, non per te. Supponendo che questo sia assolutamente la cosa che devi fare, praticamente lo stai facendo nel modo più efficiente possibile. Spetta a loro gestire le risorse in base a carico, priorità, SLA ecc. Allo stesso modo del tuo Hypervisor/VM/OS/App Server/qualsiasi cosa.

Dividi i file e usa i servizi che hai pagato!

1

Ho modificato il codice in questione un po 'nel caso in cui si volesse raggruppati per pezzi mentre assicurandosi che ogni pezzo si conclude su una linea di chiusura il

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