Domanda

Considerando un file veramente enorme (forse più di 4 GB) sul disco, voglio eseguire la scansione attraverso il file e calcolare i tempi di una specifica verifica modello binario.

Il mio pensiero è:

  1. Usa file mappato in memoria (CreateFileMap o spinta mapped_file) per caricare il file per la memoria virtuale.

  2. Per ogni 100MB mappato-memoria, creare un thread di scansione e calcolare il risultato.

C'è questo fattibile? Sono un metodo migliore per farlo?

Aggiorna :
file mappato in memoria sarebbe una scelta buona, per scaning attraverso un file di 1,6 GB potrebbe essere gestita all'interno di 11s.

Grazie.

È stato utile?

Soluzione

Il multithreading è solo andare a rendere questo movimento più lento a meno che non si desidera eseguire la scansione di più file con ciascuno su un diverso disco rigido. In caso contrario, si sta solo andando a cercare.

Ho scritto una semplice funzione di test utilizzando file di memoria mappata, con un unico filo di un file 1.4 Gb sono voluti circa 20 secondi per la scansione. Con due thread, ognuno prendendo la metà del file (anche 1MB pezzi a un thread, dispari per l'altro), ci sono voluti più di 80 secondi.

  • 1 filetto: 20015 millisecondi
  • 2 fili: 83985 millisecondi

E 'vero, 2 filetti era Quattro volte più lento di 1 filo!

Ecco il codice che ho usato, questa è la versione a thread singolo, ho usato un modello di scansione 1 byte, in modo che il codice per individuare le partite che mappa i confini straddle è non testati.

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

Altri suggerimenti

La creazione di 20 filetti, ciascuno supponendo di gestire circa 100 MB del file è probabile che le prestazioni solo peggiorare poiché la HD dovrà leggere da diversi luoghi non collegati allo stesso tempo.

prestazioni HD è al suo apice quando legge i dati sequenziali. Quindi, supponendo che il file enorme non è frammentato, la cosa migliore da fare sarebbe probabilmente utilizzare un solo filo e leggere dall'inizio alla fine in blocchi di pochi (diciamo 4) MB.

Ma che ne so io. I file system e le cache sono complesse. Fare qualche prova e vedere cosa funziona meglio.

Anche se è possibile utilizzare la mappatura della memoria, non si deve. Se andate a leggere il file in sequenza in piccoli pezzi, diciamo 1 MB ciascuna, il file non sarà mai presente nella memoria tutto in una volta.

Se il codice di ricerca è in realtà più lento del disco rigido, si può ancora pezzi mano fuori al thread di lavoro, se vi piace.

avrei un thread leggere il file (possibilmente come flusso) in una matrice e un altro processo filo esso. Non ci andrei traccio diversi in una sola volta a causa del disco cerca. Io probabilmente avere un ManualResetEvent di raccontare la mia discussione quando il prossimo? byte sono pronti per essere elaborati. Supponendo il codice di processo è più veloce allora la hdd avrei dovuto 2 buffer, uno per riempire e l'altro per il processo e solo passare tra di loro ogni volta.

mi piacerebbe andare con un solo filo troppo, non solo per i problemi di prestazioni HD, ma perché si potrebbe avere effetti collaterali gestione di crisi, quando a dividere il file:? Che cosa succede se c'è un evento del vostro modello proprio dove si divide il file

Utilizzo di un file di memoria mappata ha l'ulteriore vantaggio di evitare una copia dalla memoria cache del sistema al (gestito) Memoria applicazione se si utilizza una visualizzazione di sola lettura (anche se bisogna usare byte * puntatori poi di accedere alla memoria ). E invece di creare tanti fili utilizzano un thread di scansione in sequenza attraverso il file utilizzando per esempio visualizzazioni mappate memoria 100MB nel file (non mappare l'intero file nello spazio di indirizzi di elaborazione in una volta).

Tim Bray (ei suoi lettori) hanno esplorato questo in profondità nella sua Ampia Finder Progetto e Ampia Finder 2 . benchmark risultati mostrano che le implementazioni multithread possono superare una soluzione single-threaded su una massiccia server Sun multicore . Su solito hardware per PC, multithreading non si guadagna più di tanto, se non del tutto.

vorrei farlo con asincrono legge in un doppio buffer. Così, quando un buffer è stato letto da file, iniziare a leggere il buffer successivo durante la scansione del primo buffer. Questo significa che non CPU e IO in parallelo. Un altro vantaggio è che si avrà sempre di dati intorno confini di dati. Comunque io non so se il doppio buffer è possibile con file di memoria mappata.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top