Скорее всего не учтены особенности MonoGame, там же один Update() — один кадр, а за один кадр не всё можно успеть так-то. Можно было бы делать вычисления в отдельном потоке, а MonoGame оставить только UI да отрисовку всяких особей.
Ну вообще так вполне можно написать. Просто собрать отдельный испольняемый файл, position-independent, в каком-нибудь flat binary формате, подключить его как ресурс и будет счастье. А так получается да, выстрел в ногу
Вот что мне подсказали знающие люди:
Мне известно несколько проектов, которые заменили FindFirstFileExW(, FindExInfoBasic,, FindExSearchNameMatch,, FIND_FIRST_EX_LARGE_FETCH) на вызов NtQueryDirectoryFile() напрямую, и получили прирост производительности:
So: we detected a heap corruption during freeing a block of heap memory — inside FindClose API call.
… 8-byte header + 0x1000 requested bytes + 16-byte footer
…
Library ntdll.dll checks footer bytes before freeing a heap block — values other than 0xAB mean that a buffer overflow has occurred
…
After few calls to NtQueryDirectoryFile I noticed that the footer of our 0x1000-byte block of memory has been overwritten with four zeroes
Now some details:
As I checked in the code of mrxvpc.sys, the bug is specific to ntdll.dll!NtQueryDirectoryFile function, no other functions are affected. NtQueryDirectoryFile calls a filesystem driver, in our case — for shared drive letters — the buggy mrxvpc.sys, which may overflow data buffer supplied by NtQueryDirectoryFile.
Следовательно, необходимо аккуратно выбирать местоположение буфера в памяти:
RestartScan [in]
Set to TRUE if the scan is to start at the first entry in the directory. Set to FALSE if resuming the scan from a previous call. When the NtQueryDirectoryFile routine is called for a particular handle, the RestartScan parameter is treated as if it were set to TRUE, regardless of its value. On subsequent NtQueryDirectoryFile calls, the value of the RestartScan parameter is honored.
WRK v1.2:
RestartScan — Supplies a BOOLEAN value that, if TRUE, indicates that the
scan should be restarted from the beginning. This parameter must be
set to TRUE by the caller the first time the service is invoked.
if (RestartScan) {
irpSp->Flags = SL_RESTART_SCAN;
}
т.е. флаг SL_RESTART_SCAN устанавливается только при явном задании.
Возможно, где-то дальше FileHandle проверяется на «новизну» (первое использование в «service»), и устанавливается SL_RESTART_SCAN, т.е. в WRK v1.2 дано некорректное описание. Но возможно, и что в какой-то момент поведение функции NtQueryDirectoryFile() изменилось (в Microsoft Docs описано новое поведение). К сожалению, основываясь только на информации из документации, не получится определить версию системы, в которой изменилось поведение:
The Windows Research Kernel v1.2 contains the sources for the core of
the Windows (NTOS) kernel and a build environment for a kernel that will run on
x86 (Windows Server 2003 Service Pack 1) and
amd64 (Windows XP x64 Professional)
Можно посмотреть, как используют NtQueryDirectoryFile() в сторонних проектах:
То есть, возможно, они не смогут корректно работать ни под ReactOS, ни под WRK v1.2, ни …
Возвращаясь к 18%, 30-40% приросту производительности — надеюсь, что при замерах производительности они проверили, что обработалось столько же файлов/директорий, сколько было при использовании FindFirstFileExW() …
…, но есть вариант прочитать все имена файлов сразу.
FindNextFile() тоже читает (во внутренний буфер) сразу несколько «файловых записей» — столько, сколько поместится в буфер:
И ещё один часто задаваемый вопрос (обычно его задают те, кто пишет драйверы, т.е. те, кто часто работают с Zw* и Nt* функциями): Почему FindFirstFileEx() запрашивает только одну «файловую запись» (NtQueryDirectoryFile() вызывается с (,,,,,,,,ReturnSingleEntry=TRUE,,))? То есть вместо того, чтобы прочитать всё содержимое небольшой директории в первом же вызове FindFirstFileEx() (за минимум 2 переключения контекста: user_space→kernel_space→user_space) приходится вызывать FindFirstFileEx();FindNextFile(); (за минимум 4 переключения контекста: user_space→kernel_space→user_space→kernel_space→user_space)⁉︎
Оказывается, что в прикладном ПО FindFirstFileEx() часто используется, чтобы определить, пуста ли директория. Поэтому для уменьшения задержки (если в директории есть файлы, то из MFT считывается только одна запись (соответствующая первому файлу), вместо чтения множества записей, раскиданных по разным местам внутри MFT) добавили эту оптимизацию. Также бонусом это уменьшило количество используемой памяти при рекурсивном обходе директорий, в случае, если в директории находится только одна поддиректория, например: <несколько директорий>\<одна директория>\<несколько директорий>\<одна директория>\….
А необходимость определить «пуста ли директория» чаще всего возникает при отображении TreeView элемента — нужно определить, показывать ли значок ⊞ рядом с папкой или нет.
Начиная с Windows 8¹ для user-space в Win32 появилась универсальная функция GetFileInformationByHandleEx() (директория — это тоже «файл»; некоторые возвращаемые ею структуры аналогичны структурам NtQueryDirectoryFile()) — см. раздел «Remarks»: любой FileInformationClass (File*Info), который имеет пару в виде класса File*RestartInfo, перечисляет содержимое дирректории.
Краткое описание всех FileInformationClass, включая поведение функции при их использовании, находится здесь.
Кстати, в описании структуры FILE_FULL_DIR_INFO описано отличие класса FileFullDirectoryInfo от FileIdBothDirectoryInfoс точки зрения задержек доступа к данным:
The FILE_FULL_DIR_INFO structure is a subset of the information in the FILE_ID_BOTH_DIR_INFO structure. If the additional information is not needed then the operation will be faster as it comes from the directory entry;
FILE_ID_BOTH_DIR_INFO contains information from both the directory entry and the Master File Table (MFT).
¹ — сама функция доступна начиная с Windows Vista (…, Windows Server 2003 и Windows XP), но некоторые классы (в частности FileFullDirectory*) доступны только начиная с Windows 8:
Windows Server 2008 R2, Windows 7, Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value is not supported before Windows 8 and Windows Server 2012
На девкитах не запускаются retail версии игр, насколько я знаю, там набор ключей другой. И не очень понятно почему NVIDIA будет тестировать нинтендовские приставки… Их дело сделать SoC, а помимо него там ещё много всего…
А как же шикарный синтаксис указателей на функции?
Помянем.
А вообще с вариантами выкручивания помогает вот эта вещь
Не знаю насколько это нетривиальная реализация, но позволяет использовать каждый элемент маски отдельно =)
Нужен был простой советский...Мне известно несколько проектов, которые заменили
FindFirstFileExW(, FindExInfoBasic,, FindExSearchNameMatch,, FIND_FIRST_EX_LARGE_FETCH)
на вызовNtQueryDirectoryFile()
напрямую, и получили прирост производительности:Но при таком низкоуровневом программировании (снятие слоёв абстракций) появляются дополнительные нюансы. Вот парочка из них:
NtQueryDirectoryFile()
буфер — Crash on VirtualPC (Virtual Machine Folder Sharing Driver):Следовательно, необходимо аккуратно выбирать местоположение буфера в памяти:
VirtualAlloc()
(гранулярность 64 KiB) — позаботиться о корректной обработке переполнения буфера;официальное описание функции NtQueryDirectoryFile()
(MSDNMicrosoft Docs), и сравнить его с описанием и кодом этой же функции в Windows Research Kernel (WRK v1.2), то внимание привлечет различие в описании параметраRestartScan
:NtQueryDirectoryFile()
→BuildQueryDirectoryIrp()
:т.е. флаг
SL_RESTART_SCAN
устанавливается только при явном задании.Возможно, где-то дальше
FileHandle
проверяется на «новизну» (первое использование в «service»), и устанавливаетсяSL_RESTART_SCAN
, т.е. в WRK v1.2 дано некорректное описание. Но возможно, и что в какой-то момент поведение функцииNtQueryDirectoryFile()
изменилось (в Microsoft Docs описано новое поведение). К сожалению, основываясь только на информации из документации, не получится определить версию системы, в которой изменилось поведение:Можно посмотреть, как используют
NtQueryDirectoryFile()
в сторонних проектах:NtQueryDirectoryFile(…,TRUE)
(вFindFirstFileExW()
) следует описанию из WRK v1.2;NtQueryDirectoryFile(…,FALSE)
:То есть, возможно, они не смогут корректно работать ни под ReactOS, ни под WRK v1.2, ни …
Возвращаясь к 18%, 30-40% приросту производительности — надеюсь, что при замерах производительности они проверили, что обработалось столько же файлов/директорий, сколько было при использовании
FindFirstFileExW()
…FindNextFile()
тоже читает (во внутренний буфер) сразу несколько «файловых записей» — столько, сколько поместится в буфер:если
FindFirstFileEx()
был вызван сFIND_FIRST_EX_LARGE_FETCH
— 64 KiB;FindDataHandle = RtlAllocateHeap(,,…+sizeof(FIND_FILE_DATA))
←typedef struct _FIND_FILE_DATA{…; BYTE Buffer[FIND_DATA_SIZE];}
←#define FIND_DATA_SIZE 0x4000
).И ещё один часто задаваемый вопрос (обычно его задают те, кто пишет драйверы, т.е. те, кто часто работают с
Zw*
иNt*
функциями):Почему
FindFirstFileEx()
запрашивает только одну «файловую запись» (NtQueryDirectoryFile()
вызывается с(,,,,,,,,ReturnSingleEntry=TRUE,,)
)? То есть вместо того, чтобы прочитать всё содержимое небольшой директории в первом же вызовеFindFirstFileEx()
(за минимум 2 переключения контекста: user_space→kernel_space→user_space) приходится вызыватьFindFirstFileEx();FindNextFile();
(за минимум 4 переключения контекста: user_space→kernel_space→user_space→kernel_space→user_space)⁉︎Оказывается, что в прикладном ПО
FindFirstFileEx()
часто используется, чтобы определить, пуста ли директория. Поэтому для уменьшения задержки (если в директории есть файлы, то из MFT считывается только одна запись (соответствующая первому файлу), вместо чтения множества записей, раскиданных по разным местам внутри MFT) добавили эту оптимизацию. Также бонусом это уменьшило количество используемой памяти при рекурсивном обходе директорий, в случае, если в директории находится только одна поддиректория, например:<несколько директорий>\<одна директория>\<несколько директорий>\<одна директория>\…
.А необходимость определить «пуста ли директория» чаще всего возникает при отображении TreeView элемента — нужно определить, показывать ли значок ⊞ рядом с папкой или нет.
Начиная с Windows 8¹
для user-spaceв Win32 появилась универсальная функцияGetFileInformationByHandleEx()
(директория — это тоже «файл»; некоторые возвращаемые ею структуры аналогичны структурамNtQueryDirectoryFile()
) — см. раздел «Remarks»: любойFileInformationClass
(File*Info
), который имеет пару в виде классаFile*RestartInfo
, перечисляет содержимое дирректории.Краткое описание всех
FileInformationClass
, включая поведение функции при их использовании, находится здесь.Кстати, в описании структуры
FILE_FULL_DIR_INFO
описано отличие классаFileFullDirectoryInfo
отFileIdBothDirectoryInfo
с точки зрения задержек доступа к данным:¹ — сама функция доступна начиная с Windows Vista (…, Windows Server 2003 и Windows XP), но некоторые классы (в частности
FileFullDirectory*
) доступны только начиная с Windows 8:UPD: А, ну в статье про это есть.