web-dev-qa-db-fra.com

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

Je dois synchroniser des fichiers volumineux sur certaines machines. Les fichiers peuvent avoir une taille allant jusqu'à 6 Go. La synchronisation sera effectuée manuellement toutes les quelques semaines. Je ne peux pas prendre en compte le nom du fichier car ils peuvent 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 se trouvent pas déjà dans la destination, dans la destination. Ma première tentative ressemblait à 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 runtime:
- avec SHA256 avec un fichier de 1,6 Go -> 20 minutes
- avec MD5 avec un fichier de 1,6 Go -> 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)?

124
crono

Le problème ici est que SHA256Managed Lit 4096 octets à la fois (hériter de FileStream et surcharger Read(byte[], int, int) pour voir combien de fois il lit dans le flux de fichiers), ce qui est trop petit. un tampon pour le disque IO.

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

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

Ne contrôlez pas la totalité du fichier, créez des sommes de contrôle tous les 100 Mo environ, de sorte que chaque fichier dispose d'une collection de sommes de contrôle.

Ensuite, lorsque vous comparez des sommes de contrôle, vous pouvez arrêter de comparer après la première somme de contrôle, sortir plus tôt et éviter de traiter le fichier entier.

Cela prendra encore tout le temps pour des fichiers identiques.

60
Binary Worrier

Comme Anton Gogolev l'a noté, FileStream lit 4 096 octets à la fois par défaut, mais vous pouvez spécifier toute autre valeur à l'aide du constructeur FileStream:

new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 16 * 1024 * 1024)

Notez que Brad Abrams de Microsoft a écrit en 2004:

lier un BufferedStream à un FileStream ne présente aucun avantage. Nous avons copié la logique de mise en mémoire tampon de BufferedStream dans FileStream il y a environ 4 ans pour améliorer les performances par défaut.

source

42
Tal Aloni

Appelez le port Windows de md5sum.exe . C'est environ deux fois plus vite que l'implémentation .NET (au moins sur ma machine utilisant 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 ();
}
22
Christian Birkl

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

  1. en utilisant un exe "natif" faire le hachage a pris du temps de 6 minutes à 10 secondes, ce qui est énorme.
  2. L'augmentation de la mémoire tampon était encore plus rapide - le fichier de 1,6 Go prenait 5,2 secondes avec MD5 en .Net, je vais donc utiliser cette solution - merci encore
15
crono

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 de 29½ Go en taille, les résultats étaient

  • 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 lors de l’utilisation du code original, sans tampon.

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

Alors j'ai pensé, qu'en est-il d'un disque dur standard de 2 To. Et les résultats étaient comme ça

  • 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

Donc, je recommanderais soit pas de tampon ou un tampon de max 1 mill.

10
Anders

Vous faites quelque chose de mal (tampon de lecture probablement trop petit). Sur une machine d'âge indécent (Athlon 2x1800MP de 2002) qui a DMA sur disque probablement hors de contrôle (6.6M/s est sacrément lent pour les lectures séquentielles)):

Créez un fichier 1G avec des données "aléatoires":

# dd if=/dev/sdb of=temp.dat bs=1M count=1024    
1073741824 bytes (1.1 GB) copied, 161.698 s, 6.6 MB/s

# time sha1sum -b temp.dat
abb88a0081f5db999d0701de2117d2cb21d192a2 *temp.dat

1m5.299s

# time md5sum -b temp.dat
9995e1c1a704f9c1eb6ca11e7ecb7276 *temp.dat

1m58.832s

C’est aussi étrange, md5 est toujours plus lent que sha1 pour moi (me répète plusieurs fois).

2
Pasi Savolainen

Je sais que je suis en retard pour faire la fête mais que j'ai effectué un test avant de réellement mettre en œuvre la solution.

J'ai effectué un test contre la classe MD5 intégrée et aussi md5sum.exe . Dans mon cas, la classe intégrée a pris 13 secondes, alors que md5sum.exe a également tourné autour de 16 à 18 secondes à chaque exécution.

    DateTime current = DateTime.Now;
    string file = @"C:\text.iso";//It's 2.5 Gb file
    string output;
    using (var md5 = MD5.Create())
    {
        using (var stream = File.OpenRead(file))
        {
            byte[] checksum = md5.ComputeHash(stream);
            output = BitConverter.ToString(checksum).Replace("-", String.Empty).ToLower();
            Console.WriteLine("Total seconds : " + (DateTime.Now - current).TotalSeconds.ToString() + " " + output);
        }
    }
0
Romil Kumar Jain