2010-10-19 7 views
18

J'aimerais savoir comment je peux diviser un gros fichier sans utiliser trop de ressources système. J'utilise actuellement ce code:Comment diviser efficacement les gros fichiers

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'opération prend 52.370 secondes pour diviser un fichier 1.6GB en fichiers 14MB. Je ne suis pas préoccupé par la durée de l'opération, je suis plus préoccupé par la ressource système utilisée que cette application sera déployée dans un environnement d'hébergement partagé. Actuellement, cette opération est à 100% de mon utilisation des systèmes HDD IO et ralentit considérablement mon système. L'utilisation du processeur est faible. RAM augmente un peu, mais ça semble bien.

Y a-t-il un moyen de limiter cette opération en utilisant trop de ressources?

Merci

+0

Pourriez-vous pas courir sur une priorité inférieure séparée fil ? – w69rdy

+0

@ w69rdy - remarque "L'utilisation du processeur est faible" - CPU n'est pas le goulot d'étranglement ici. –

+0

@Marc Ok point juste – w69rdy

Répondre

19

Il semble étrange d'assembler chaque fichier de sortie en mémoire; Je suppose que vous devriez exécuter un tampon interne (peut-être 20k ou quelque chose) et appeler Write plus fréquemment.

En fin de compte, si vous avez besoin d'E/S, vous avez besoin d'E/S. Si vous voulez être courtois envers un environnement d'hébergement partagé, vous pouvez ajouter des pauses délibérées - peut-être de courtes pauses dans la boucle interne, et une pause plus longue (peut-être 1s) dans la boucle externe. Cela n'affectera pas beaucoup votre timing global, mais peut aider d'autres processus à obtenir des E/S.

l'exemple d'un tampon pour la boucle intérieure:

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

Actuellement cette opération max mes systèmes de l'utilisation du disque dur IO à 100%.

Ce qui est logique - l'IO va être votre facteur limitant, et que votre système a probbably même merdique IO de la plupart des ordinateurs (un disque lent, pas un RAID 10 de disques haute performance).

Vous pouvez utiliser un fragment décent (1mb vers le haut) pour réduire les petites lectures et écritures, mais à la fin c'est tout ce que vous pouvez faire. Ou obtenez un sous-système de disque plus rapide.

+0

Ah.Most Les hébergeurs ignorent le côté IO. RAID peut-être, mais alors des disques bon marché. Une bonne performance est chère. Je reçois environ 400mb/s stable IO - sur 10 (!) Velociraptors. Les disques à eux seuls coûtent près de 3000 USD;) – TomTom

0

Une option vous étrangle l'opération. Si par exemple vous ramener le tampon à une taille plus petite (entre 4K et 1 Mo) et mettre un Thread.Sleep entre les opérations, vous utiliserez moins de ressources.

0

Ceci est un problème pour votre hébergeur, pas vous. En supposant que c'est absolument ce que vous devez faire, alors vous le faites de la manière la plus efficace possible. C'est à eux de gérer les ressources en fonction de la charge, de la priorité, du SLA, etc. de la même manière que votre Hypervisor/VM/OS/App Server/any. Diviser les fichiers et utiliser les installations que vous avez payées!

1

J'ai modifié le code dans la question un peu dans le cas où vous vouliez diviser par morceaux tout en veillant à chaque morceau se termine sur une fin de ligne:

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