Как просканировать через действительно огромные файлы на диске?

Рассматривая действительно огромный файл (возможно, больше чем 4 ГБ) на диске, я хочу просканировать через этот файл и вычислить, времена определенного двоичного шаблона происходит.

Моя мысль:

  1. Используйте файл с отображенной памятью (CreateFileMap или повысьте mapped_file) загрузить файл в виртуальную память.

  2. Для каждых 100 МБ расширенной памяти создайте один поток, чтобы просканировать и вычислить результат.

Действительно ли это выполнимо? Там какой-либо лучший метод должен сделать так?

Обновление:
Файл с отображенной памятью был бы хорошим выбором, для того, чтобы просканировать через файл на 1.6 ГБ мог быть обработан в 11.

спасибо.

14
задан Jon Seigel 14 March 2010 в 20:25
поделиться

8 ответов

Многопоточность только замедлит этот процесс, если вы не хотите сканировать несколько файлов, каждый из которых находится на другом жестком диске. В противном случае вы просто будете искать.

Я написал простую тестовую функцию, используя файлы с отображением памяти, с одним потоком для сканирования файла размером 1,4 ГБ потребовалось около 20 секунд. С двумя потоками, каждый из которых забирает половину файла (даже фрагменты размером 1 МБ для одного потока, нечетные для другого), это заняло более 80 секунд.

  • 1 поток: 20015 миллисекунд
  • 2 потока: 83985 миллисекунд

Верно, 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;
}
4
ответ дан 1 December 2019 в 12:38
поделиться

Проверьте это - Закругленные углы на UIImage

Изменение слоя представляется наилучшим способом.

UIImageView * roundedView = [[UIImageView alloc] initWithImage: [UIImage imageNamed:@"wood.jpg"]];
// Get the Layer of any view
CALayer * l = [roundedView layer];
[l setMasksToBounds:YES];
[l setCornerRadius:10.0];
-121--831617-

Только немного лучше, но меньше подвержен ошибкам:

public static double FromPercentageString(this string value)
{
    return double.Parse(value.Replace("%","")) / 100;
}
-121--1316219-

Создание 20 потоков, каждый из которых должен обрабатывать около 100 МБ файла, скорее всего, только ухудшит производительность, так как HD придется читать из нескольких несвязанных мест одновременно.

Производительность HD достигает пика при считывании последовательных данных. Таким образом, предполагая, что ваш огромный файл не фрагментирован, лучше всего использовать только один поток и читать от начала до конца в несколько (скажем, 4) МБ.

Но что я знаю. Файловые системы и кэши сложны. Проведите тестирование и посмотрите, что работает лучше всего.

10
ответ дан 1 December 2019 в 12:38
поделиться

Хотя вы можете использовать отображение памяти, это не обязательно. Если вы читаете файл последовательно небольшими кусочками, скажем по 1 Мб каждый, то этот файл никогда не будет присутствовать в памяти сразу.

Если ваш код поиска на самом деле медленнее, чем на жестком диске, вы все равно можете отдать часть файла рабочим потокам, если хотите.

5
ответ дан 1 December 2019 в 12:38
поделиться

Я бы попросил один поток прочитать файл (возможно, как поток) в массив, а другой - обработать его. Я бы не стал сопоставлять несколько за один раз из-за поиска диска. Я бы, вероятно, иметь ManualResetEvent, чтобы сказать моему потоку, когда следующие ? байты готовы к обработке. Предполагая, что ваш код процесса быстрее, чем hdd я бы 2 буфера, один для заполнения, а другой для обработки и просто переключаться между ними каждый раз.

2
ответ дан 1 December 2019 в 12:38
поделиться

Я бы тоже использовал только один поток, не только из-за проблем с производительностью HD, но и потому, что у вас могут возникнуть проблемы с управлением побочными эффектами при разделении файла: что если ваш шаблон встречается прямо там, где вы разделяете файл?

1
ответ дан 1 December 2019 в 12:38
поделиться

Использование файла с отображением в память дает дополнительное преимущество, так как позволяет избежать копирования из кэш-памяти файловой системы в (управляемую) память приложения, если вы используете представление только для чтения (хотя для доступа к памяти вам необходимо использовать байтовые * указатели) ). И вместо создания множества потоков используйте один поток для последовательного сканирования файла, используя, например, представления с отображением памяти 100 МБ в файл (не отображайте сразу весь файл в адресное пространство процесса).

0
ответ дан 1 December 2019 в 12:38
поделиться

Тим Брей (и его читатели) подробно исследовали это в своих Wide Finder Project и Wide Finder 2 . Результаты тестов показывают, что многопоточные реализации могут превосходить однопоточные решения на массивном многоядерном сервере Sun . На обычном оборудовании ПК многопоточность не принесет вам столько пользы, если вообще принесет пользу.

0
ответ дан 1 December 2019 в 12:38
поделиться

Я бы сделал это с асинхронным считыванием в двойной буфер. Поэтому, когда один буфер был прочитан из файла, начинайте читать следующий при сканировании первого буфера. Это означает, что вы делаете процессор и вход в систему параллельно. Еще одним преимуществом является то, что вы всегда будете иметь данные вокруг границ данных. Однако я не знаю, возможно ли двойное буферизация с файлами, отображенными в памяти.

0
ответ дан 1 December 2019 в 12:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: