Подсказка по предварительному выделению файлов в Windows (ReFS,NTFS)
Предположим, у меня есть несколько процессов, пишущих большие файлы (20 ГБ +). Каждый процесс записывает свой собственный файл и предполагает, что процесс записывает x mb за раз, затем выполняет некоторую обработку и снова записывает x mb и т. Д.
В результате этого шаблона записи файлы сильно фрагментируются, поскольку блоки файлов последовательно размещаются на диске.
Конечно, это легко обойти эту проблему с помощью SetEndOfFile
"предварительно выделить" файл при его открытии, а затем установить правильный размер до его закрытия. Но теперь приложение, обращающееся к этим файлам удаленно, которое может анализировать эти файлы в процессе, очевидно, видит нули в конце файла и занимает намного больше времени для анализа файла. У меня нет контроля над этим приложением для чтения, поэтому я не могу оптимизировать его, чтобы учесть нули в конце.
Другим грязным решением было бы чаще запускать дефрагментацию, запускать утилиту contig Systernal или даже реализовывать собственный "дефрагментатор", который обрабатывал мои файлы и объединял их блоки.
Другим более радикальным решением было бы реализовать драйвер минифильтра, который сообщал бы о "поддельном" размере файла.
Но очевидно, что оба решения, перечисленные выше, далеки от оптимальных. Поэтому я хотел бы знать, есть ли способ предоставить подсказку о размере файла для файловой системы, чтобы она "резервировала" последовательное пространство на диске, но все же сообщала приложениям правильный размер файла?
Иначе очевидно, что написание больших кусков за раз, очевидно, помогает с фрагментацией, но все еще не решает проблему.
РЕДАКТИРОВАТЬ:
Поскольку полезность 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 на диске:
Изображение показывает, что NTFS действительно выделил кластеры для моего файла. Однако безымянный атрибут DATA имеет StreamDataSize
указано как 0.
Systernals DiskView также подтверждает, что кластеры были выделены
При нажатии Enter, чтобы продолжить тестирование (и в течение довольно долгого времени, так как файл был создан на медленной флешке), StreamDataSize
поле было обновлено
Так как я написал 1 байт в конце, NTFS теперь действительно пришлось обнулить все, поэтому SetEndOfFile
действительно помогает с проблемой, о которой я беспокоюсь.
Я был бы очень признателен за то, что ответы / комментарии также содержат официальную ссылку для подтверждения заявленных претензий.
Ох, и тестовое приложение выводит это в моем случае:
creating file
file extended, elapsed: 0
writing 'A' at the end
written: 1 bytes, elapsed: 21735
Также для полноты картины приведем пример того, как выглядит атрибут DATA при установке FileAllocationInfo
(обратите внимание, что я создал новый файл для этой картинки)
1 ответ
Файловые системы Windows поддерживают два общедоступных размера файловых данных, о которых сообщается в FileStandardInformation
:
AllocationSize
- размер выделения файла в байтах, который обычно кратен размеру сектора или кластера.EndOfFile
- абсолютный конец позиции файла в файле в виде байтового смещения от начала файла, который должен быть меньше или равен размеру выделения.
Установка конца файла, который превышает текущий размер выделения, неявно расширяет выделение. Установка размера выделения, который меньше текущего конца файла, неявно усекает конец файла.
Начиная с Windows Vista, мы можем вручную увеличить размер выделения без изменения конца файла с помощью SetFileInformationByHandle
: FileAllocationInfo
, Вы можете использовать Sysinternals DiskView, чтобы убедиться, что это выделяет кластеры для файла. Когда файл закрыт, распределение усекается до текущего конца файла.
Если вы не возражаете против использования NT API напрямую, вы также можете позвонить NtSetInformationFile
: FileAllocationInformation
, Или даже установить размер выделения при создании через NtCreateFile
,
К вашему сведению, есть также внутренний ValidDataLength
размер, который должен быть меньше или равен концу файла. По мере роста файла кластеры на диске инициализируются лениво. Чтение за пределами допустимой области возвращает нули. Запись за пределы допустимой области расширяет ее, инициализируя все кластеры вплоть до смещения записи с нулями. Обычно в этом случае мы можем наблюдать снижение производительности при расширении файла со случайной записью. Мы можем установить FileValidDataLengthInformation
обойти это (например, SetFileValidData
), но он предоставляет неинициализированные данные на диске и, таким образом, требует SeManageVolumePrivilege. Приложение, использующее эту функцию, должно позаботиться об открытии файла исключительно и обеспечить его безопасность в случае сбоя приложения или системы.