2010-01-31 2 views
14

디스크에 실제로 거대한 파일 (4GB 이상)이 있다고 생각하면이 파일을 스캔하여 특정 이진 패턴의 시간을 계산하려고합니다.디스크의 대용량 파일을 검사하는 방법은 무엇입니까?

  1. 를 사용하여 메모리 매핑 파일 (CreateFileMap 또는 mapped_file 향상) 가상 메모리에 파일을로드 :

    내 생각이다.

  2. 각 100MB 매핑 메모리에 대해 스캔하고 결과를 계산할 하나의 스레드를 만듭니다.

가능한가요 더 좋은 방법이 있습니까?

업데이트 :
메모리 매핑 된 파일은 11S 내에서 처리 할 수있는 1.6GB 파일을 통해 scaning을 위해 좋은 선택이 될 것입니다.

감사합니다.

+4

(2) 패턴이 100MB 경계까지 확장됩니까? 검색 알고리즘을 직접 작성해야하고 검색 문자열이 비교적 길면 길수록 좋습니다! Boyer-Moore 알고리즘을 고려하십시오. http://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_string_search_algorithm – Kristen

+0

@Kristen : 패턴은 100MB 경계를 넘지 않습니다. 패턴이 '1'비트이기 때문입니다. – Jichao

+0

패턴은 무엇입니까? 실제로는 단일 세트 비트입니까? – GalacticJello

답변

4

멀티 스레딩은 서로 다른 하드 드라이브에있는 각각의 파일을 스캔하려는 경우를 제외하고는 속도가 느려지 게 만듭니다. 그렇지 않으면 당신은 단지 찾을 것입니다.

메모리 매핑 된 파일을 사용하여 간단한 테스트 함수를 작성했습니다. 단일 스레드로 1.4Gb 파일을 스캔하는 데 약 20 초가 걸렸습니다. 두 개의 스레드가 파일의 절반을 차지하고 (1MB는 한 스레드로, 다른 스레드는 홀수로), 80 초 이상 걸립니다.

  • 1 실 : 20,015 밀리 초
  • 2 스레드 : 83,985 밀리 초

맞아, 2 개 스레드 1 개 스레드가 아닌 배 느렸다!

여기는 내가 사용하는 코드입니다.이 코드는 단일 스레드 버전이며, 1 바이트 스캔 패턴을 사용하므로 맵 경계를 넘어선 일치하는 위치를 찾지 못하는 코드는 테스트되지 않았습니다.

HRESULT ScanForPattern(LPCTSTR pszFilename, LPBYTE pbPattern, UINT cbPattern, LONGLONG * pcFound) 
{ 
    HRESULT hr = S_OK; 

    *pcFound = 0; 
    if (! pbPattern || ! cbPattern) 
     return E_INVALIDARG; 

    // Open the file 
    // 
    HANDLE hf = CreateFile(pszFilename, 
          GENERIC_READ, 
          FILE_SHARE_READ, NULL, 
          OPEN_EXISTING, 
          FILE_FLAG_SEQUENTIAL_SCAN, 
          NULL); 

    if (INVALID_HANDLE_VALUE == hf) 
     { 
     hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); 
     // catch an open file that exists but is in use 
     if (ERROR_SHARING_VIOLATION == GetLastError()) 
     hr = HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION); 
     return hr; 
     } 

    // get the file length 
    // 
    ULARGE_INTEGER uli; 
    uli.LowPart = GetFileSize(hf, &uli.HighPart); 
    LONGLONG cbFileSize = uli.QuadPart; 
    if (0 == cbFileSize) 
     { 
     CloseHandle (hf); 
     return S_OK; 
     } 

    const LONGLONG cbStride = 1 * 1024 * 1024; // 1 MB stride. 
    LONGLONG cFound = 0; 
    LPBYTE pbGap = (LPBYTE) malloc(cbPattern * 2); 

    // Create a mapping of the file. 
    // 
    HANDLE hmap = CreateFileMapping(hf, NULL, PAGE_READONLY, 0, 0, NULL); 
    if (NULL != hmap) 
     { 
     for (LONGLONG ix = 0; ix < cbFileSize; ix += cbStride) 
     { 
     uli.QuadPart = ix; 
     UINT cbMap = (UINT) min(cbFileSize - ix, cbStride); 
     LPCBYTE pb = (LPCBYTE) MapViewOfFile(hmap, FILE_MAP_READ, uli.HighPart, uli.LowPart, cbMap); 
     if (! pb) 
      { 
      hr = HRESULT_FROM_WIN32(GetLastError()); 
      break; 
      } 
     // handle pattern scanning over the gap. 
     if (cbPattern > 1 && ix > 0) 
      { 
      CopyMemory(pbGap + cbPattern - 1, &pb[0], cbPattern - 1); 
      for (UINT ii = 1; ii < cbPattern; ++ii) 
       { 
       if (pb[ii] == pbPattern[0] && 0 == memcmp(&pb[ii], pbPattern, cbPattern)) 
        { 
        ++cFound; 
        // advance by cbPattern-1 to avoid detecting overlapping patterns 
        } 
       } 
      } 

     for (UINT ii = 0; ii < cbMap - cbPattern + 1; ++ii) 
      { 
      if (pb[ii] == pbPattern[0] && 
       ((cbPattern == 1) || 0 == memcmp(&pb[ii], pbPattern, cbPattern))) 
       { 
       ++cFound; 
       // advance by cbPattern-1 to avoid detecting overlapping patterns 
       } 
      } 
     if (cbPattern > 1 && cbMap >= cbPattern) 
      { 
      // save end of the view in our gap buffer so we can detect map-straddling patterns 
      CopyMemory(pbGap, &pb[cbMap - cbPattern + 1], cbPattern - 1); 
      } 
     UnmapViewOfFile(pb); 
     } 

     CloseHandle (hmap); 
     } 
    CloseHandle (hf); 

    *pcFound = cFound; 
    return hr; 
} 
+0

질문 : "if (pb [ii] == pbPattern [0] && 0 == memcmp (& pb [ii], pbPattern, cbPattern)) "? memcmp (& pb [ii], pbPattern, cbPattern)가 같지 않으면 첫 번째 바이트를 비교하자마자 false를 반환하지 않습니까? –

5

메모리 매핑을 사용할 수 있지만 그렇게 할 필요는 없습니다. 파일을 순차적으로 작은 청크 (예 : 1MB)로 읽으면 파일이 한꺼번에 메모리에 나타나지 않습니다.

검색 코드가 실제로 하드 디스크보다 느리면 원하는 경우 작업자 스레드에 청크를 넘길 수 있습니다.

+0

나는 디스크에서 읽는 것보다 검색이 더 느릴 수있는 유일한 방법은 진정한 병리학 적 사례 (예 : 1,000,000 개의 A 문자가 들어있는 파일에서 'A'문자 다음에'B'가 오는 999,999 개의 문자 검색)를 사용하는 것일뿐입니다. 순진한 검색 방법 (전형적으로'strstr()'에 대해 구현 된 것과 같다). 임의의 선형 시간 문자열 검색 (예 : Knuth-Morris-Pratt)의 경우 디스크 I/O는 100 배 이상 느려집니다. –

+2

그래, 그 이유는 "만약 당신의 검색 코드가 실제로 더 느리다면 ..."에서 "if"와 "실제로"를 썼다. :) – Thomas

10

20 개의 스레드를 생성하면 각각 100MB의 파일을 처리 할 때마다 HD가 관련없는 여러 장소에서 동시에 읽어야하기 때문에 성능이 저하 될 수 있습니다.

순차 데이터를 읽을 때 HD 성능이 최고조에 달합니다. 거대한 파일이 조각난 것이 아니라고 가정 할 때 가장 좋은 방법은 단 하나의 스레드 만 사용하고 처음부터 끝까지 몇 바이트 (4MB)의 덩어리로 읽는 것입니다.

하지만 내가 아는 것은 무엇입니까? 파일 시스템과 캐시는 복잡합니다. 몇 가지 테스트를 수행하고 가장 잘 작동하는지 확인하십시오.

0

메모리 매핑 된 파일을 사용하면 읽기 전용보기를 사용하는 경우 파일 시스템 캐시 메모리에서 (관리되는) 응용 프로그램 메모리로의 복사를 피할 수 있습니다 (바이트 * 포인터를 사용해야 만 기억). 그리고 많은 스레드를 생성하는 대신 하나의 스레드를 사용하여 순차적으로 파일을 스캔합니다. 예를 들어 100MB 메모리 매핑 된 뷰를 파일에 사용합니다 (전체 파일을 프로세스 주소 공간에 한 번에 매핑하지 않음).

0

더블 버퍼로 비동기 읽기를 사용하면됩니다. 따라서 하나의 버퍼가 파일에서 읽혀지면 첫 번째 버퍼를 스캔하는 동안 다음 버퍼 읽기를 시작합니다. 즉, 병렬로 CPU와 IO를 수행해야합니다. 또 다른 장점은 데이터 경계 주변에 항상 데이터가 있다는 것입니다. 그러나 이중 버퍼링 메모리 매핑 된 파일을 사용할 수 있는지 모르겠습니다.

희망이 도움이됩니다.

감사합니다,

Sebastiaan

1

나도 하나 개의 스레드로 갈 것뿐만 아니라 HD 성능 문제에 대한,하지만 당신은 당신의 파일을 분할 할 때 부작용을 관리하는 문제가있을 수 있기 때문에 :이 무엇 경우 파일을 분할 한 곳에서 패턴이 올바르게 표시됩니까?

2

나는 하나의 스레드가 파일로 (아마도 스트림으로) 배열을 읽고 다른 스레드가 그것을 처리하도록 할 것이다. 나는 디스크 찾기 때문에 한 번에 여러 맵을 쓰지 않을 것이다. 아마 다음에 내 스레드에게 알리기 위해 ManualResetEvent를 가지고 있을까요? 바이트를 처리 할 준비가되었습니다. 프로세스 코드가 더 빠르다고 가정하면 hdd는 2 개의 버퍼를 채우고 하나는 채우고 다른 하나는 처리하고 매번 그 사이를 전환합니다.

0

Tim Bray (및 그의 독자)는 그의 Wide Finder ProjectWide Finder 2에서 깊이 탐구했습니다. Benchmark results은 멀티 스레드 구현이 대규모 Sun 멀티 코어 서버에서 단일 스레드 솔루션 을 능가 할 수 있음을 보여줍니다.. 일반적인 PC 하드웨어에서 멀티 스레딩을 사용하면 많은 이점을 얻을 수 없습니다.

관련 문제