Как эффективнее обрабатывать большой файл по кускам Windows C++?

Есть файл во много раз больший оперативной памяти. Собираюсь реализовать алгоритм, который загружает часть файла, помещающуюся в оперативную память и там обрабатывает. Как сделать это с максимальной эффективностью по времени на C++ под Windows? Mapping файла на оперативную память? Как определить максимальный (!) размер куска, который поместится в оперативную память?


Ответы (1 шт):

Автор решения: Snouphruh

многие бенчмарки показывают, что максимальную скорость накопитель демонстрирует, если читать блоками по 128 Кб и выше. в этих же тестах максимальный размер блока используется 8 Мб.

исходя из этого, если использовать блок в 128 Кб или больше, то достаточно просто объявить переменную-массив необходимого размера, например 8 Мб, (все примеры кода привожу для использования в Visual C++ и WinAPI), читать из файла кусками в этот массив и производить необходимую обработку:

// объявление массива на 8 Мб
BYTE pBuffer [8 * 1024 * 1024];

// объявление переменной для хэндла файла
HANDLE hFile;

// объявление переменной для получения количества прочитанных байт
DWORD dwHaveRead;

// объявление переменной для получения результата функции чтения файла
BOOL blResult;

// открытие заведомо существующего файла с доступом только на чтение,
// общим доступом тоже только на чтение и с указанием для ОС, что файл
// будет последовательно считываться (это необходимо для лучшего кэширования)
hFile = CreateFile ("...", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);

// проверка, что файл успешно открыт
if (hFile != INVALID_HANDLE_VALUE)
{
  // цикл чтения файла кусками по 8 Мб
  do
  {
    // чтение файла
    blResult = ReadFile (hFile, pBuffer, sizeof (pBuffer), &dwHaveRead, NULL);

    // некий код обработки считанных данных из файла
    ...
  } while (blResult && (dwHaveRead == sizeof (pBuffer)));

  // закрытие файла
  CloseHandle (hFile);
}

но сразу скажу, что так делать не очень хорошо, так как Windows выделяет сразу необходимое количество памяти, для размещения всех переменных в ней, которые будут (или не будут) использоваться в данном приложении на всЁ время, пока это приложение запущено. понятное дело, что сегодня в компьютерах установлено по несколько Гб памяти, на фоне которой эти жалкие 8 Мб погоды не сделают, но лучше для таких больших массивов память выделять динамически, а потом, когда эти массивы уже не нужны, можно освобождать память. тем самым экономнее использовать оперативную память:

// определение своей константы
#define BUFFERSIZE 8 * 1024 * 1024

// объявление переменной для получения указателя на выделенную память
LPVOID pBuffer;

// объявление переменной для хэндла файла
HANDLE hFile;

// объявление переменной для получения количества прочитанных байт
DWORD dwHaveRead;

// объявление переменной для получения результата функции чтения файла
BOOL blResult;

// динамическое выделение памяти нужного объЁма
pBuffer = VirtualAlloc (NULL, BUFFERSIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

// проверка, что память успешно выделена
if (pBuffer != NULL)
{
  // открытие заведомо существующего файла с доступом только на чтение,
  // общим доступом тоже только на чтение и с указанием для ОС, что файл
  // будет последовательно считываться (это необходимо для лучшего кэширования)
  hFile = CreateFile ("...", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);

  // проверка, что файл успешно открыт
  if (hFile != INVALID_HANDLE_VALUE)
  {
    // цикл чтения файла кусками
    do
    {
      // чтение файла
      blResult = ReadFile (hFile, pBuffer, BUFFERSIZE, &dwHaveRead, NULL);

      // некий код обработки считанных данных из файла
      ...
    } while (blResult && (dwHaveRead == BUFFERSIZE)));

    // закрытие файла
    CloseHandle (hFile);
  }

  // освобождение выделенной памяти
  VirtualFree (pBuffer, 0, MEM_RELEASE);
}

→ Ссылка