Подсказка по предварительному выделению файлов в Windows (ReFS, NTFS) - PullRequest
0 голосов
/ 16 ноября 2018

Предположим, у меня есть несколько процессов, пишущих большие файлы (20 ГБ +). Каждый процесс записывает свой собственный файл и предполагает, что процесс записывает x mb за раз, затем выполняет некоторую обработку и снова записывает x mb и т. Д.

В результате этого шаблона записи файлы будут сильно фрагментированы, поскольку блоки файлов последовательно размещаются на диске.

Конечно, легко обойти эту проблему, используя SetEndOfFile, чтобы «предварительно выделить» файл при его открытии, а затем установить правильный размер перед его закрытием. Но теперь приложение, обращающееся к этим файлам удаленно, которое может анализировать эти файлы в процессе, очевидно, видит нули в конце файла и занимает намного больше времени для анализа файла. У меня нет контроля над этим приложением для чтения, поэтому я не могу оптимизировать его для учета нулей в конце.

Еще одним грязным решением было бы чаще запускать дефрагментацию, запускать утилиту contig Systernal или даже реализовывать собственный «дефрагментатор», который обрабатывал мои файлы и объединял их блоки.

Другим более радикальным решением было бы внедрение драйвера минифильтра, который сообщал бы о «поддельном» размере файла.

Но очевидно, что оба решения, перечисленные выше, далеки от оптимальных. Поэтому я хотел бы знать, есть ли способ предоставить подсказку о размере файла для файловой системы, чтобы она «резервировала» последовательное пространство на диске, но все же сообщала приложениям правильный размер файла?

В противном случае, очевидно, что запись больших кусков за раз, очевидно, помогает с фрагментацией, но все еще не решает проблему.

EDIT:

Поскольку полезность SetEndOfFile в моем случае, кажется, оспаривается, я сделал небольшой тест:

LARGE_INTEGER size;
LARGE_INTEGER a;
char buf='A';
DWORD written=0;

DWORD tstart;

std::cout << "creating file\n";
tstart = GetTickCount();
HANDLE f = CreateFileA("e:\\test.dat", GENERIC_ALL, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL);
size.QuadPart = 100000000LL;
SetFilePointerEx(f, size, &a, FILE_BEGIN);
SetEndOfFile(f);
printf("file extended, elapsed: %d\n",GetTickCount()-tstart);
getchar();
printf("writing 'A' at the end\n");
tstart = GetTickCount();
SetFilePointer(f, -1, NULL, FILE_END);
WriteFile(f, &buf,1,&written,NULL);
printf("written: %d bytes, elapsed: %d\n",written,GetTickCount()-tstart);

Когда приложение выполняется и ожидает нажатия клавиши после SetEndOfFile, я проверил структуры NTFS на диске: before

На рисунке показано, что NTFS действительно выделила кластеры для моего файла. Однако для безымянного атрибута DATA StreamDataSize указано как 0.

Systernals DiskView также подтверждает, что кластеры были выделены DickView

При нажатии клавиши Enter для продолжения теста (и ожидания в течение некоторого времени с момента создания файла на медленном USB-накопителе) поле StreamDataSize было обновлено enter image description here

Поскольку я написал 1 байт в конце, NTFS теперь действительно нужно было обнулить все, поэтому SetEndOfFile действительно помогает с проблемой, о которой я "беспокоюсь".

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

Да, и тестовое приложение выводит это в моем случае:

creating file
file extended, elapsed: 0

writing 'A' at the end
written: 1 bytes, elapsed: 21735

Также для полноты картины приведен пример того, как выглядит атрибут DATA при установке FileAllocationInfo (обратите внимание, что я создал новый файл для этой картинки) enter image description here

1 Ответ

0 голосов
/ 16 ноября 2018

Файловые системы Windows поддерживают два открытых размера для файловых данных, которые указываются в FileStandardInformation:

  • AllocationSize - размер выделения файла в байтах, которыйобычно кратен размеру сектора или кластера.
  • EndOfFile - абсолютный конец позиции файла в файле в виде байтового смещения от начала файла, который должен быть меньше или равен размеру выделения.

Установка конца файла, который превышает текущий размер выделения, неявно расширяет выделение.Установка размера выделения, который меньше текущего конца файла, неявно усекает конец файла.

Начиная с Windows Vista, мы можем вручную увеличить размер выделения без изменения конца файла с помощью SetFileInformationByHandle: FileAllocationInfo.Вы можете использовать Sysinternals DiskView, чтобы убедиться, что это выделяет кластеры для файла.Когда файл закрывается, распределение усекается до текущего конца файла.

Если вы не возражаете против непосредственного использования NT API, вы также можете вызвать NtSetInformationFile: FileAllocationInformation.Или даже установите размер выделения при создании через NtCreateFile.


FYI, также есть внутренний размер ValidDataLength, который должен быть меньше или равен концуфайл.По мере роста файла кластеры на диске инициализируются лениво.Чтение за пределами допустимой области возвращает нули.Запись за пределы допустимой области расширяет ее, инициализируя все кластеры вплоть до смещения записи с нулями.Обычно в этом случае мы можем наблюдать снижение производительности при расширении файла со случайной записью.Мы можем установить FileValidDataLengthInformation, чтобы обойти это (например, SetFileValidData), но он предоставляет неинициализированные данные на диске и поэтому требует SeManageVolumePrivilege.Приложение, использующее эту функцию, должно открывать файл исключительно и обеспечивать его безопасность в случае сбоя приложения или системы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...