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

일부 컴퓨터에서 큰 파일을 동기화해야합니다. 파일 크기는 최대 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 분

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



답변

여기서 문제는 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
}


답변

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

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

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


답변

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에 복사하여 기본 성능을 향상 시켰습니다.

출처


답변

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


답변

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

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

답변

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

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 밀의 버퍼를 권장합니다.


답변

무언가 잘못하고 있습니다 (아마도 너무 작은 읽기 버퍼). 디스크에 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보다 지속적으로 느립니다 (여러 번 재생).