Каков самый быстрый способ создать контрольную сумму для больших файлов в C#
Я должен синхронизировать большие файлы на некоторых машинах. Файлы могут быть размером до 6 ГБ. Синхронизация будет выполняться вручную каждые несколько недель. Я не могу принять имя файла во внимание, потому что они могут измениться в любое время.
мой план состоит в том, чтобы создать контрольные суммы на конечном ПК и на исходном ПК, а затем скопировать все файлы с контрольной суммой, которые еще не находятся в пункте назначения, в пункт назначения.
Моя первая попытка была примерно такой:
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);
}
}
Проблема было время выполнения:
- с SHA256 с файлом 1,6 Гб - > 20 минут
- с MD5 с файлом 1,6 ГБ -> 6.15 минут
есть ли более быстрый способ получить контрольную сумму (возможно, с лучшей хэш-функцией)?
7 ответов:
проблема здесь в том, что
SHA256Managedодновременно считывает 4096 байт (наследуется отFileStreamи заменитьRead(byte[], int, int)чтобы увидеть, сколько он читает из filestream), который слишком мал буфер для ввода-вывода диска.чтобы ускорить процесс (2 минуты для хэширования файла 2 Гб на моей машине с SHA256, 1 минута для MD5) wrap
FileStreamнаBufferedStreamи установите размер буфера разумного размера (я пробовал с буфером ~1 Мб):// Not sure if BufferedStream should be wrapped in using block using(var stream = new BufferedStream(File.OpenRead(filePath), 1200000)) { // The rest remains the same }
Не проверяйте весь файл, создавайте контрольные суммы каждые 100 Мб или около того, поэтому каждый файл имеет коллекцию контрольных сумм.
затем при сравнении контрольных сумм вы можете прекратить сравнение после первой другой контрольной суммы, выйти рано и сохранить вас от обработки всего файла.
Это все равно займет все время для идентичных файлов.
Как отметил Антон Гоголев, FileStream по умолчанию считывает 4096 байт за раз, Но вы можете указать любое другое значение с помощью конструктора FileStream:
new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 16 * 1024 * 1024)обратите внимание, что Брэд Абрамс из Microsoft написал в 2004 году:
существует нулевая выгода от обертывания BufferedStream вокруг a файловый поток. Мы скопировали логику буферизации BufferedStream в FileStream около 4 лет назад, чтобы поощрять лучший дефолт производительность
вызовите порт windows md5sum.exe. Это примерно в два раза быстрее, чем реализация .NET (по крайней мере, на моей машине с использованием файла 1,2 ГБ)
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 (); }
Хорошо-спасибо всем вам-позвольте мне закончить это:
- используя "родной" exe сделать хеширование заняло время от 6 минут до 10 секунд, которые огромны.
- увеличение буфера было еще быстрее-файл 1.6 GB занял 5.2 секунды, используя MD5 в .Net, поэтому я пойду с этим решением-еще раз спасибо
Я сделал тест с размером буфера, запустив этот код
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(); }и я тестировал с файлом размером 29½ ГБ, результаты были
- 10.000: 369,24 s
- 100.000: 362,55 s
- 1.000.000: 361,53 s
- 10.000.000: 434,15 s
- 100.000.000: 435,15 s
- 1.000.000.000: 434,31 s
- и 376,22 с при использовании оригинала, ни один буферизованный код.
Я использую процессор i5 2500K, 12 ГБ оперативной памяти и 256 ГБ SSD-накопителя OCZ Vertex 4.
поэтому я подумал, что насчет стандартного жесткого диска 2 ТБ. И результаты были такими
- 10.000: 368,52 s
- 100.000: 364,15 s
- 1.000.000: 363,06 s
- 10.000.000: 678,96 s
- 100.000.000: 617,89 s
- 1.000.000.000: 626,86 s
- и ни для кого не буферизовал 368,24
поэтому я бы рекомендовал либо нет буфера или буфер максимум 1 мельница.
Вы делаете что-то неправильно (слишком маленький буфер чтения). На машине неприличного возраста (Athlon 2x1800MP с 2002 года), которая имеет DMA на диске, вероятно, не в порядке (6.6 M/s чертовски медленно при последовательном чтении):
создать 1G файл со "случайными" данными:
# 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.dat1m5.299s
# time md5sum -b temp.dat 9995e1c1a704f9c1eb6ca11e7ecb7276 *temp.dat1m58.832s
это тоже странно, md5 последовательно медленнее, чем sha1 для меня (reran несколько раз).
Comments