Muszę synchronizować duże pliki na niektórych komputerach. Pliki mogą mieć rozmiar do 6 GB. Synchronizacja będzie wykonywana ręcznie co kilka tygodni. Nie mogę brać pod uwagę nazw plików, ponieważ mogą się one zmienić w dowolnym momencie.
Mój plan polega na utworzeniu sum kontrolnych na komputerze docelowym i na komputerze źródłowym, a następnie skopiowanie wszystkich plików z sumą kontrolną, które nie znajdują się jeszcze w miejscu docelowym, do miejsca docelowego. Moja pierwsza próba wyglądała mniej więcej tak:
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);
}
}
Problem
dotyczył czasu wykonania: - z SHA256 z plikiem 1,6 GB -> 20 minut
- z MD5 z plikiem 1,6 GB -> 6,15 minuty
Czy istnieje lepszy - szybszy - sposób uzyskania sumy kontrolnej (może z lepszą funkcją mieszającą)?
c#
.net
large-files
checksum
crono
źródło
źródło
Odpowiedzi:
Problem polega na tym, że
SHA256Managed
odczytuje jednocześnie 4096 bajtów (dziedziczenie zFileStream
i nadpisywanie,Read(byte[], int, int)
aby zobaczyć, ile odczytuje ze strumienia pliku), co jest zbyt małym buforem dla operacji we / wy dysku.Do prędkości rzeczy w górę (2 minuty do mieszania pliku 2 GB na moim komputerze z SHA256, 1 minuta do MD5) opasania
FileStream
wBufferedStream
i ustawić rozsądnie wielkości rozmiar bufora (próbowałem buforem ~ 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 }
źródło
Nie sumuj całego pliku, twórz sumy kontrolne co około 100 MB, aby każdy plik miał kolekcję sum kontrolnych.
Następnie porównując sumy kontrolne, możesz przestać porównywać po pierwszej innej sumie kontrolnej, wyjść wcześnie i zaoszczędzić na przetwarzaniu całego pliku.
W przypadku identycznych plików zajmie to cały czas.
źródło
Jak zauważył Anton Gogolev , FileStream odczytuje domyślnie 4096 bajtów naraz, ale możesz określić dowolną inną wartość za pomocą konstruktora FileStream:
new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 16 * 1024 * 1024)
Zauważ, że Brad Abrams z firmy Microsoft napisał w 2004 roku:
źródło
źródło
Wywołaj port systemu Windows programu md5sum.exe . To około dwa razy szybciej niż implementacja .NET (przynajmniej na moim komputerze z plikiem 1,2 GB)
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 (); }
źródło
Ok - dziękuję wszystkim - podsumuję:
źródło
Zrobiłem testy z rozmiarem bufora, uruchamiając ten kod
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(); }
Testowałem z plikiem o rozmiarze 29½ GB i wyniki były takie
Używam procesora i5 2500K, 12 GB pamięci RAM i dysku SSD OCZ Vertex 4 256 GB.
Pomyślałem więc, co ze standardowym dyskiem twardym o pojemności 2 TB. A wyniki były takie
Dlatego zalecałbym albo brak bufora, albo bufor o maksymalnej wartości 1 miliona.
źródło
Wiem, że spóźniłem się na imprezę, ale przed wdrożeniem rozwiązania przeprowadziłem test.
Przeprowadziłem test z wbudowaną klasą MD5, a także md5sum.exe . W moim przypadku wbudowana klasa zajęła 13 sekund, gdzie md5sum.exe zbyt około 16-18 sekund w każdym uruchomieniu.
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); } }
źródło
Możesz zajrzeć do XxHash.Net ( https://github.com/wilhelmliao/xxHash.NET )
Algorytm xxHash wydaje się być szybszy niż wszystkie inne.
Niektóre testy porównawcze na stronie xxHash: https://github.com/Cyan4973/xxHash
PS: Jeszcze go nie używałem.
źródło
Robisz coś źle (prawdopodobnie za mały bufor odczytu). Na maszynie w niedoszłym wieku (Athlon 2x1800MP z 2002 roku), która ma DMA na dysku, prawdopodobnie nie działa (6,6 M / s jest cholernie powolny podczas odczytu sekwencyjnego):
Utwórz plik 1G z „losowymi” danymi:
# 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
1m 5,299s
# time md5sum -b temp.dat 9995e1c1a704f9c1eb6ca11e7ecb7276 *temp.dat
1m58.832s
To też jest dziwne, md5 jest dla mnie konsekwentnie wolniejsze niż sha1 (kilkakrotnie powtórzone).
źródło