141 votes

Quel est le moyen le plus rapide de créer une somme de contrôle pour des fichiers volumineux en C# ?

Je dois synchroniser des fichiers volumineux sur plusieurs machines. Les fichiers peuvent atteindre une taille de 6 Go. La synchronisation sera effectuée manuellement toutes les quelques semaines. Je ne peux pas prendre en compte le nom du fichier car il peut changer à tout moment.

Mon plan est de créer des sommes de contrôle sur le PC de destination et sur le PC source, puis de copier tous les fichiers avec une somme de contrôle, qui ne sont pas déjà dans la destination, vers la destination. Ma première tentative était quelque chose comme ceci :

using System.IO;
using System.Security.Cryptography;

private static string GetChecksum(string file)
{
    using (FileStream stream = File.OpenRead(file))
    {
        SHA256Managed sha = new SHA256Managed();
        byte[] checksum = sha.ComputeHash(stream);
        return BitConverter.ToString(checksum).Replace("-", String.Empty);
    }
}

Le problème était le temps d'exécution :
- avec SHA256 avec un fichier de 1,6 GB -> 20 minutes
- avec MD5 avec un fichier de 1,6 GB -> 6.15 minutes

Existe-t-il un meilleur moyen - plus rapide - d'obtenir la somme de contrôle (peut-être avec une meilleure fonction de hachage) ?

2 votes

Faut-il vraiment vérifier la somme de contrôle ? Comment copiez-vous les fichiers ? Si vous êtes sous Windows, j'utiliserais la dernière version de Robocopy ...

6 votes

Une astuce intéressante pour ne s'occuper du hachage que si la taille des fichiers est différente entre deux fichiers candidats. stackoverflow.com/a/288756/74585

128voto

Anton Gogolev Points 59794

Le problème ici est que SHA256Managed lit 4096 octets à la fois (hérité de FileStream et de passer outre Read(byte[], int, int) pour voir combien il lit depuis le filestream), ce qui est un tampon trop petit pour l'IO du disque.

Pour accélérer les choses (2 minutes pour le hachage d'un fichier de 2 Go sur ma machine avec SHA256, 1 minute pour MD5) enveloppez FileStream sur BufferedStream et définissez une taille de tampon raisonnable (j'ai essayé avec un tampon de ~1 Mb) :

// Not sure if BufferedStream should be wrapped in using block
using(var stream = new BufferedStream(File.OpenRead(filePath), 1200000))
{
    // The rest remains the same
}

4 votes

OK - cela a fait la différence - hacher le fichier de 1.6GB avec MD5 a pris 5.2 secondes sur ma machine (QuadCode @2.6 GHz, 8GB Ram) - encore plus rapide que l'implémentation native...

4 votes

Je ne comprends pas. Je viens d'essayer cette suggestion mais la différence est minime, voire nulle. fichier de 1024mb sans buffering 12-14 secs, avec buffering également 12-14 secs - je comprends que la lecture de centaines de blocs de 4k produira plus d'IO mais je me demande si le framework ou les API natives sous le framework ne gèrent pas déjà cela

18 votes

J'arrive un peu tard, mais pour les FileStreams, il n'est plus nécessaire d'envelopper le flux dans un BufferedStream, car cela est déjà fait dans le FileStream lui-même. Source :

74voto

Binary Worrier Points 27424

Ne faites pas de somme de contrôle pour l'ensemble du fichier, créez des sommes de contrôle tous les 100 mégaoctets environ, de sorte que chaque fichier possède une collection de sommes de contrôle.

Ainsi, lorsque vous comparez les sommes de contrôle, vous pouvez arrêter la comparaison après la première somme de contrôle différente, ce qui vous permet de vous retirer plus tôt et vous évite de traiter l'ensemble du fichier.

Cela prendra toujours le temps nécessaire pour des fichiers identiques.

2 votes

J'aime l'idée, mais cela ne fonctionnera pas dans mon scénario car je me retrouverai avec beaucoup de fichiers inchangés au fil du temps.

0 votes

Une vision très pessimiste... ;-)

2 votes

Comment faire une somme de contrôle pour chaque 100 Mo d'un fichier ?

23voto

Christian Birkl Points 363

Invoquer le port Windows de md5sum.exe . Il est environ deux fois plus rapide que l'implémentation .NET (du moins sur ma machine avec un fichier de 1,2 Go).

public static string Md5SumByProcess(string file) {
    var p = new Process ();
    p.StartInfo.FileName = "md5sum.exe";
    p.StartInfo.Arguments = file;            
    p.StartInfo.UseShellExecute = false;
    p.StartInfo.RedirectStandardOutput = true;
    p.Start();
    p.WaitForExit();           
    string output = p.StandardOutput.ReadToEnd();
    return output.Split(' ')[0].Substring(1).ToUpper ();
}

3 votes

WOW - l'utilisation de md5sums.exe de pc-tools.net/win32/md5sums le rend vraiment rapide. 1681457152 octets, 8672 ms = 184.91 MB/sec -> 1,6GB ~ 9 secondes Ce sera assez rapide pour mon objectif.

17voto

crono Points 1626

Ok - merci à vous tous - laissez-moi conclure :

  1. Utilisation d'un exe "natif". pour effectuer le hachage a pris du temps de 6 minutes à 10 secondes, ce qui est énorme.
  2. Augmentation du tampon était encore plus rapide - un fichier de 1,6 Go a pris 5,2 secondes en utilisant MD5 dans .Net, je vais donc opter pour cette solution - merci encore.

10voto

Anders Points 141

J'ai fait des tests avec la taille du tampon, en exécutant ce code

using (var stream = new BufferedStream(File.OpenRead(file), bufferSize))
{
    SHA256Managed sha = new SHA256Managed();
    byte[] checksum = sha.ComputeHash(stream);
    return BitConverter.ToString(checksum).Replace("-", String.Empty).ToLower();
}

Et j'ai testé avec un fichier d'une taille de 29½ Go, les résultats sont les suivants

  • 10.000 : 369,24s
  • 100.000 : 362,55s
  • 1.000.000 : 361,53s
  • 10.000.000 : 434,15s
  • 100.000.000 : 435,15s
  • 1.000.000.000 : 434,31s
  • Et 376,22s en utilisant le code original, sans tampon.

J'utilise un processeur i5 2500K, 12 Go de RAM et un disque SSD OCZ Vertex 4 256 Go.

Alors j'ai pensé, pourquoi pas un disque dur standard de 2TB. Et les résultats étaient les suivants

  • 10.000 : 368,52s
  • 100.000 : 364,15s
  • 1.000.000 : 363,06s
  • 10.000.000 : 678,96s
  • 100.000.000 : 617,89s
  • 1.000.000.000 : 626,86s
  • Et pour aucun tampon 368,24

Je recommanderais donc soit de ne pas avoir de tampon, soit un tampon de 1 million maximum.

0 votes

Je ne comprends pas. Comment ce test peut-il contredire la réponse acceptée d'Anton Gogolev ?

0 votes

Pouvez-vous ajouter une description de chaque champ dans vos données ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X