Frage

Unter Berücksichtigung einer wirklich großen Datei (vielleicht mehr als 4 GB) auf der Festplatte, möchte ich durch diese Datei scannen und die Zeiten eines bestimmten binären Muster auftritt berechnen.

Mein Gedanke ist:

  1. Mit Memory-Mapped-Datei (CreateFileMap oder Boost-mapped_file) laden die Datei in dem virtuellen Speicher.

  2. Für jede 100MB-Speicher abgebildet, erzeugen ein Thread das Ergebnis abzutasten und zu berechnen.

Ist das möglich? Gibt es eine bessere Methode, dies zu tun?

Aktualisieren :
Memory-Mapped-Datei wäre eine gute Wahl sein, für scaning durch eine 1,6 GB-Datei innerhalb von 11s behandelt werden könnte.

Dank.

War es hilfreich?

Lösung

Multithreading wird nur diese gehen langsamer zu machen, wenn Sie mehrere Dateien mit jeweils auf einer anderen Festplatte scannen möchten. Andernfalls werden Sie nur zu suchen.

Ich schrieb eine einfache Testfunktion im Speicher abgebildeten Dateien verwendet wird, mit einem einzigen Thread eine 1,4 GB-Datei etwa 20 Sekunden dauerte zu scannen. Mit zwei Fäden, die jeweils unter der Hälfte die Datei (auch 1MB chunks ein Gewinde, odd zum anderes), dauerte es mehr als 80 Sekunden.

  • 1 thread: 20015 Millisekunden
  • 2 Themen: 83.985 Millisekunden

Das ist richtig, 2 Themen waren Vier -mal langsamer als 1 Thread!

Hier ist der Code, den ich verwenden, ist dies die einzige Gewindeausführung, habe ich ein 1-Byte-Scan-Muster, so dass der Code Spiele zu lokalisieren, dass Straddle Kartengrenzen ungetestet ist.

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

Andere Tipps

Erstellen 20 Threads, die jeweils Gesetzt etwa 100 MB der Datei zu handhaben ist wahrscheinlich nur noch verschlimmern Leistung, da die HD aus mehreren voneinander unabhängigen Orten zur gleichen Zeit zum Lesen haben.

HD-Leistung ist auf ihrem Höhepunkt, wenn es sequentielle Daten liest. Also vorausgesetzt, Ihre große Datei nicht fragmentiert ist, das Beste, was wäre wohl zu tun nur einen Thread zu verwenden und von Anfang bis Ende in Stücken von wenige gelesen werden (zB 4) MB.

Aber was weiß ich. Dateisysteme und Caches sind komplex. Haben einige Tests und sehen, was am besten funktioniert.

Auch wenn Sie Speicherzuordnung verwenden können, müssen Sie nicht auf. Wenn Sie die Datei der Reihe nach in kleinen Stücken zu lesen, sagen sie 1 MB je, wird die Datei nie auf einmal im Speicher vorhanden sein.

Wenn Ihr Suchcode ist tatsächlich langsamer als die Festplatte, können Sie immer noch Hand Stücke aus zu Worker-Threads, wenn Sie mögen.

würde ich ein Thread die Datei gelesen haben (möglicherweise als ein Strom) in einem Array und haben einen anderen Thread zu verarbeiten. Ich würde nicht auf einmal mehrere Karte wegen Platte sucht. Ich habe wahrscheinlich einen Manual meinen Thread zu sagen, wann die nächsten? Bytes bereit sind, verarbeitet werden. Angenommen, Ihre Prozess-Code ist schneller als die Festplatte i 2 Puffer haben würde, eine zu füllen und das andere zu verarbeiten und einfach zwischen ihnen wechseln jedes Mal.

würde ich auch mit nur einem Thread gehen, nicht nur für HD-Performance-Probleme, sondern weil Sie Probleme Verwaltung Nebenwirkungen haben könnte, wenn Splitting Ihre Datei: was, wenn es ein Auftreten Ihr Muster ist richtig, wo Sie Ihre Datei geteilt

eine Memory-Mapped-Datei verwendet, hat den zusätzlichen Vorteil, eine Kopie aus dem Dateisystem-Cache-Speicher in die (verwaltet) Anwendungsspeicher zu vermeiden, wenn Sie eine schreibgeschützte Ansicht verwenden (auch wenn Sie zu verwenden Byte haben * Zeiger dann dem Zugriff auf den Speicher ). Und statt viele Threads verwenden einen Thread sequentiell Abtastung durch die Datei mit beispielsweise 100 MB Memory Mapped Ansichten in die Datei zu erstellen (ordne nicht die gesamte Datei in den Prozessadressraum auf einmal).

Tim Bray (und seine Leser) erforschen diese in der Tiefe in seinem Breit Finder Projekt und Benchmark-Ergebnisse zeigen, dass multithreaded Implementierungen können eine Single-Threaded-Lösung übertreffen auf ein massiver Sun Multi-Core-Server . Auf üblichen PC-Hardware Multithreading werden Sie nicht so viel gewinnen, wenn über.

Ich würde es tun, mit asynchron in einen Doppelpuffer liest. Also, wenn ein Puffer aus der Datei gelesen wurde, starten Sie den nächsten Puffer zu lesen, während der erste Puffer zu scannen. Das heißt, Sie CPU und IO parallel. Ein weiterer Vorteil ist, dass Sie immer Daten um Daten Grenzen haben. Ich weiß jedoch nicht, ob die doppelte Pufferung mit Memory-Mapped-Dateien möglich ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top