authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-09-25 17:47:35-07:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-09-29 02:46:27+02:00
log070294b36ca0922aaffdc0b7c669b189f5f147c1
tree7a0a6a288fb8b753fe82c6e284f83dc07df80d15
parentc27e685e6316764926513f806c0c4a51c53cf7a8

Threaded.createFileMap: fix handling of `populate` on Windows

This both fixes populate=false, since NtCreateSection needs COMMIT=true when called with a file handle (the higher level API sets this flag for the user), and also signals the intention to prefetch when populate=true, which wasn't happening before. However, PrefetchVirtualMemory (and the ntdll equivalent NtSetInformationVirtualMemory with MEMORY_PREFETCH_INFORMATION) doesn't appear to actually do anything to decrease the number of page/hard faults. We still do the call though since it accurately signals intent, and future version of Windows may be able to do something with that.

4 files changed, 80 insertions(+), 2 deletions(-)

lib/std/Io/File/MemoryMap.zig+1-1
......@@ -54,7 +54,7 @@ pub const CreateOptions = struct {
5454 undefined_contents: bool = false,
5555 /// Prefault the pages. If this option is unsupported, it is silently
5656 /// ignored. Aside from custom Io implementations, this option is only
57 /// supported on Linux.
57 /// supported on Linux and Windows.
5858 populate: bool = true,
5959 /// Asserted to be a multiple of page size which can be obtained via
6060 /// `std.heap.pageSize`.
lib/std/Io/Threaded.zig+35-1
......@@ -19087,7 +19087,13 @@ fn createFileMap(
1908719087 null,
1908819088 &section_size,
1908919089 page,
19090 .{ .COMMIT = populate },
19090 .{
19091 // Required when calling NtCreateSection with a file handle, otherwise
19092 // this call will fail with INVALID_PARAMETER. The higher level
19093 // `CreateFileMapping` function will set this bit for you when it
19094 // calls NtCreateSection.
19095 .COMMIT = true,
19096 },
1909119097 file.handle,
1909219098 )) {
1909319099 .SUCCESS => {},
......@@ -19123,6 +19129,34 @@ fn createFileMap(
1912319129 const alignment: Alignment = .fromByteUnits(page_size);
1912419130 assert(contents_len == alignment.forward(len));
1912519131 }
19132 if (populate) {
19133 const addresses = [_]windows.MEMORY_RANGE_ENTRY{
19134 .{ .VirtualAddress = contents_ptr.?, .NumberOfBytes = len },
19135 };
19136 var info: windows.VIRTUAL_MEMORY.MEMORY_PREFETCH_INFORMATION = .{
19137 .Flags = .{
19138 // This flag requires Windows 11 >= 24H4. On earlier versions, this being set
19139 // will cause NtSetInformationVirtualMemory to fail with INVALID_PARAMETER_5.
19140 .TO_WORKING_SET = false,
19141 },
19142 };
19143 // It appears that this is effectively a no-op, as it doesn't decrease page/hard faults.
19144 // https://github.com/microsoft/Windows-Dev-Performance/issues/108
19145 //
19146 // We still call it, though, to signal the intention of doing something useful.
19147 // It doesn't appear to make anything *worse*, so there's no harm in doing so.
19148 switch (windows.ntdll.NtSetInformationVirtualMemory(
19149 windows.current_process,
19150 .Prefetch,
19151 1,
19152 &addresses,
19153 &info,
19154 @sizeOf(windows.VIRTUAL_MEMORY.MEMORY_PREFETCH_INFORMATION),
19155 )) {
19156 .SUCCESS => {},
19157 else => |status| return windows.unexpectedStatus(status),
19158 }
19159 }
1912619160 return .{
1912719161 .file = file,
1912819162 .offset = offset,
lib/std/os/windows.zig+33
......@@ -3517,6 +3517,39 @@ pub const SEC = packed struct(ULONG) {
35173517
35183518pub const ERESOURCE = opaque {};
35193519
3520pub const VIRTUAL_MEMORY = struct {
3521 pub const INFORMATION_CLASS = enum(c_int) {
3522 Prefetch = 0,
3523 PagePriority = 1,
3524 CfgCallTarget = 2,
3525 PageDirtyState = 3,
3526 ImageHotPatch = 4,
3527 PhysicalContiguity = 5,
3528 VirtualMachinePrepopulate = 6,
3529 RemoveFromWorkingSet = 7,
3530 _,
3531
3532 pub const Max: @typeInfo(@This()).@"enum".tag_type = @typeInfo(@This()).@"enum".field_names.len;
3533 };
3534
3535 pub const MEMORY_PREFETCH_INFORMATION = extern struct {
3536 Flags: VM_PREFETCH,
3537
3538 pub const VM_PREFETCH = packed struct(ULONG) {
3539 /// Introduced in Windows 11 24H4.
3540 /// Attempt to populate specified single or multiple address ranges
3541 /// into the process working set (bring pages into physical memory).
3542 TO_WORKING_SET: bool,
3543 Reserved1: u31 = 0,
3544 };
3545 };
3546};
3547
3548pub const MEMORY_RANGE_ENTRY = extern struct {
3549 VirtualAddress: PVOID,
3550 NumberOfBytes: SIZE_T,
3551};
3552
35203553// ref: shared/ntdef.h
35213554
35223555pub const EVENT_TYPE = enum(c_int) {
lib/std/os/windows/ntdll.zig+11
......@@ -58,6 +58,8 @@ const WORD = windows.WORD;
5858const USER_THREAD_START_ROUTINE = windows.USER_THREAD_START_ROUTINE;
5959const PS = windows.PS;
6060const TEB = windows.TEB;
61const VIRTUAL_MEMORY = windows.VIRTUAL_MEMORY;
62const MEMORY_RANGE_ENTRY = windows.MEMORY_RANGE_ENTRY;
6163
6264// ref: km/ntifs.h
6365
......@@ -631,6 +633,15 @@ pub extern "ntdll" fn NtProtectVirtualMemory(
631633 OldAccessProtection: *PAGE,
632634) callconv(.winapi) NTSTATUS;
633635
636pub extern "ntdll" fn NtSetInformationVirtualMemory(
637 ProcessHandle: HANDLE,
638 VmInformationClass: VIRTUAL_MEMORY.INFORMATION_CLASS,
639 NumberOfExtries: SIZE_T,
640 VirtualAddresses: [*]const MEMORY_RANGE_ENTRY,
641 VmInformation: *anyopaque,
642 VmInformationLength: DWORD,
643) callconv(.winapi) NTSTATUS;
644
634645pub extern "ntdll" fn NtWaitForAlertByThreadId(
635646 Address: ?*const anyopaque,
636647 Timeout: ?*const LARGE_INTEGER,