C #에서 큰 파일에 대한 체크섬을 만드는 가장 빠른 방법은 무엇입니까


128

일부 컴퓨터에서 큰 파일을 동기화해야합니다. 파일 크기는 최대 6GB입니다. 동기화는 몇 주마다 수동으로 수행됩니다. 파일 이름은 언제든지 변경할 수 있으므로 고려할 수 없습니다.

내 계획은 대상 PC와 원본 PC에서 체크섬을 만든 다음 아직 대상에없는 체크섬이있는 모든 파일을 대상에 복사하는 것입니다. 내 첫 번째 시도는 다음과 같습니다.

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);
    }
}

런타임 문제 :
-1,6GB 파일이있는 SHA256-> 20 분
-1,6GB 파일이있는 MD5-> 6.15 분

체크섬을 얻는 더 좋고 빠른 방법이 있습니까?


2
체크섬을 정말로 확인해야합니까? 파일을 어떻게 복사합니까? 당신의 창문에 나는 최신 버전의 Robocopy를 사용할 것입니다 ...
Mesh

6
두 개의 후보 파일 사이에서 파일 크기가 다른 경우 해시를 귀찮게하는 좋은 팁 stackoverflow.com/a/288756/74585
Matthew Lock

답변:


117

여기서 문제는 SHA256Managed한 번에 4096 바이트 를 읽으며 ( 파일 스트림에서 읽은 양을 확인하기 위해 상속 FileStream하고 재정의 Read(byte[], int, int)함) 디스크 IO에 비해 너무 작은 버퍼입니다.

속도 일까지 랩 (2 내 SHA256와 기계, MD5 1 분 정도에 2GB의 파일을 해싱 분) FileStreamBufferedStream설정하고 합리적인 크기의 버퍼 크기 (나는 ~ 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
}

3
좋아-이것은 차이를 만들었습니다-MD5로 1.6GB 파일을 해시하는 것은 내 상자에서 5.2 초가 걸렸습니다 (QuadCode @ 2.6 GHz, 8GB Ram)-기본 구현보다 훨씬 빠릅니다 ...
crono

4
나는 그것을 얻지 못한다. 방금이 제안을 시도했지만 그 차이는 거의 없습니다. 버퍼링 12-14 초, 1024mb 파일, 버퍼링 12-14 초-수백 개의 4k 블록을 읽으면 더 많은 IO가 생성된다는 것을 이해하지만 프레임 워크 또는 프레임 워크 아래의 기본 API가 이미 처리하지 않는지 나 자신에게 묻습니다. ..
Christian Casutt

11
파티에 약간 늦었지만 FileStreams의 경우 현재 FileStream 자체에서 이미 완료되었으므로 더 이상 BufferedStream에 스트림을 래핑 할 필요가 없습니다. 출처
Reyhn

작은 파일 (<10MB이지만 MD5를 얻는 데 영원히 걸리는) 으로이 문제를 겪고있었습니다. .Net 4.5를 사용하더라도 BufferedStream을 사용하여이 방법으로 전환하면 8.6MB 파일의 해시 시간이 약 8.6 초에서 <300ms로 줄었습니다.
Taegost

1024 kB 대신 BufferedStream / w 512 kB를 사용했습니다. 1.8GB 파일은 30 초 만에 해결되었습니다.
휴고 Woesthuis

61

전체 파일을 체크섬하지 말고 100MB 정도마다 체크섬을 생성하십시오. 따라서 각 파일에는 체크섬 모음이 있습니다.

그런 다음 체크섬을 비교할 때 첫 번째 다른 체크섬 후 비교를 중지하고 일찍 나가 전체 파일을 처리하지 않아도됩니다.

동일한 파일의 경우 여전히 풀 타임이 소요됩니다.


2
나는 아이디어가 마음에 들지만 시간이 지나도 변경되지 않은 파일이 많이 있기 때문에 시나리오에서 작동하지 않습니다.
crono

1
파일의 100MB마다 체크섬을 어떻게 확인합니까?
Smith

1
보안상의 이유로 체크섬을 사용할 때는 공격자가 제외 된 바이트 만 변경할 수 있으므로 좋지 않습니다.
b.kiener

2
+1 일대일 비교를 수행 할 때 훌륭한 아이디어입니다. 불행히도 MD5 해시를 색인으로 사용하여 많은 중복 항목 (다 대다 검사) 중에서 고유 파일을 찾습니다.
Nathan Goings

1
@ b.kiener 바이트가 제외되지 않습니다. 당신은 그를 오해했습니다.
Soroush Falahati

47

Anton Gogolev가 언급했듯이 FileStream은 기본적으로 한 번에 4096 바이트를 읽지 만 FileStream 생성자를 사용하여 다른 값을 지정할 수 있습니다.

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

Microsoft의 Brad Abrams는 2004 년에 다음과 같이 썼습니다.

FileStream 주위에 BufferedStream을 래핑하면 이점이 없습니다. 약 4 년 전에 BufferedStream의 버퍼링 로직을 FileStream에 복사하여 기본 성능을 향상 시켰습니다.

출처


22

md5sum.exe 의 Windows 포트를 호출하십시오 . .NET 구현보다 약 2 배 빠릅니다 (적어도 1.2GB 파일을 사용하는 컴퓨터에서는)

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
WOW-pc-tools.net/win32/md5sums에서 md5sums.exe를 사용하면 정말 빠릅니다. 1681457152 바이트, 8672 ms = 184.91 MB / 초-> 1,6GB ~ 9 초 이것은 내 목적에 충분히 빠릅니다.
crono

16

좋아요-모두 감사합니다. 마무리하겠습니다.

  1. "네이티브"exe 를 사용하여 해싱을 수행하는 데 6 분에서 10 초까지 걸리는 시간은 엄청났습니다.
  2. 버퍼를 늘리는 것이 훨씬 빨라졌습니다. 1.6GB 파일은 .Net에서 MD5를 사용하여 5.2 초가 걸렸으므로이 솔루션을 사용하겠습니다. 다시 한 번 감사드립니다.

10

이 코드를 실행하면서 버퍼 크기로 테스트를 수행했습니다.

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½ GB 크기의 파일로 테스트했는데 결과는

  • 10.000 : 369,24 초
  • 100.000 : 362,55 초
  • 1.000.000 : 361,53 초
  • 10.000.000 : 434,15 초
  • 100.000.000 : 435,15 초
  • 1.000.000.000 : 434,31 초
  • 그리고 버퍼링되지 않은 원본 코드를 사용할 때 376,22s.

i5 2500K CPU, 12GB 램 및 OCZ Vertex 4256GB SSD 드라이브를 실행하고 있습니다.

그래서 표준 2TB 하드 드라이브는 어떨까요? 결과는 이렇습니다

  • 10.000 : 368,52 초
  • 100.000 : 364,15 초
  • 1.000.000 : 363,06 초
  • 10.000.000 : 678,96 초
  • 100.000.000 : 617,89 초
  • 1.000.000.000 : 626,86 초
  • 버퍼링되지 않은 버퍼

따라서 버퍼 없음 또는 최대 1 밀의 버퍼를 권장합니다.


나는 그것을 얻지 못한다. 이 테스트는 Anton Gogolev의 승인 된 답변과 어떻게 상충 될 수 있습니까?
buddybubble

데이터의 각 필드에 대한 설명을 추가 할 수 있습니까?
videoguy

2

무언가 잘못하고 있습니다 (아마도 너무 작은 읽기 버퍼). 디스크에 DMA가있을 수있는 부적절한 연령 (2002 년 애슬론 2x1800MP)의 머신에서 아마도 순차 읽기 (6.6M / 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.dat

1m5.299s

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

1m58.832s

이것은 또한 이상합니다 .md5는 sha1보다 지속적으로 느립니다 (여러 번 재생).


예-Anton Gogolev가 제안한 것처럼 버퍼를 늘리려 고합니다. 1,6GB 파일에 9 초가 걸린 "기본"MD5.exe를 통해이 파일을 실행했습니다.
crono

2

나는 파티에 늦었지만 실제로 솔루션을 구현하기 전에 테스트를 수행했다는 것을 알고 있습니다.

내장 MD5 클래스와 md5sum.exe 에 대한 테스트를 수행 했습니다 . 필자의 경우 내장 클래스는 md5sum.exe가 매 실행마다 16-18 초 정도 걸리는 13 초가 걸렸습니다.

    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);
        }
    }

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.