| author | |
| committer | |
| log | 070294b36ca0922aaffdc0b7c669b189f5f147c1 |
| tree | 7a0a6a288fb8b753fe82c6e284f83dc07df80d15 |
| parent | c27e685e6316764926513f806c0c4a51c53cf7a8 |
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 { | ... | @@ -54,7 +54,7 @@ pub const CreateOptions = struct { |
| 54 | undefined_contents: bool = false, | 54 | undefined_contents: bool = false, |
| 55 | /// Prefault the pages. If this option is unsupported, it is silently | 55 | /// Prefault the pages. If this option is unsupported, it is silently |
| 56 | /// ignored. Aside from custom Io implementations, this option is only | 56 | /// ignored. Aside from custom Io implementations, this option is only |
| 57 | /// supported on Linux. | 57 | /// supported on Linux and Windows. |
| 58 | populate: bool = true, | 58 | populate: bool = true, |
| 59 | /// Asserted to be a multiple of page size which can be obtained via | 59 | /// Asserted to be a multiple of page size which can be obtained via |
| 60 | /// `std.heap.pageSize`. | 60 | /// `std.heap.pageSize`. |
lib/std/Io/Threaded.zig+35-1| ... | @@ -19087,7 +19087,13 @@ fn createFileMap( | ... | @@ -19087,7 +19087,13 @@ fn createFileMap( |
| 19087 | null, | 19087 | null, |
| 19088 | &section_size, | 19088 | &section_size, |
| 19089 | page, | 19089 | 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 | }, | ||
| 19091 | file.handle, | 19097 | file.handle, |
| 19092 | )) { | 19098 | )) { |
| 19093 | .SUCCESS => {}, | 19099 | .SUCCESS => {}, |
| ... | @@ -19123,6 +19129,34 @@ fn createFileMap( | ... | @@ -19123,6 +19129,34 @@ fn createFileMap( |
| 19123 | const alignment: Alignment = .fromByteUnits(page_size); | 19129 | const alignment: Alignment = .fromByteUnits(page_size); |
| 19124 | assert(contents_len == alignment.forward(len)); | 19130 | assert(contents_len == alignment.forward(len)); |
| 19125 | } | 19131 | } |
| 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 | } | ||
| 19126 | return .{ | 19160 | return .{ |
| 19127 | .file = file, | 19161 | .file = file, |
| 19128 | .offset = offset, | 19162 | .offset = offset, |
lib/std/os/windows.zig+33| ... | @@ -3517,6 +3517,39 @@ pub const SEC = packed struct(ULONG) { | ... | @@ -3517,6 +3517,39 @@ pub const SEC = packed struct(ULONG) { |
| 3517 | 3517 | ||
| 3518 | pub const ERESOURCE = opaque {}; | 3518 | pub const ERESOURCE = opaque {}; |
| 3519 | 3519 | ||
| 3520 | pub 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 | |||
| 3548 | pub const MEMORY_RANGE_ENTRY = extern struct { | ||
| 3549 | VirtualAddress: PVOID, | ||
| 3550 | NumberOfBytes: SIZE_T, | ||
| 3551 | }; | ||
| 3552 | |||
| 3520 | // ref: shared/ntdef.h | 3553 | // ref: shared/ntdef.h |
| 3521 | 3554 | ||
| 3522 | pub const EVENT_TYPE = enum(c_int) { | 3555 | pub const EVENT_TYPE = enum(c_int) { |
lib/std/os/windows/ntdll.zig+11| ... | @@ -58,6 +58,8 @@ const WORD = windows.WORD; | ... | @@ -58,6 +58,8 @@ const WORD = windows.WORD; |
| 58 | const USER_THREAD_START_ROUTINE = windows.USER_THREAD_START_ROUTINE; | 58 | const USER_THREAD_START_ROUTINE = windows.USER_THREAD_START_ROUTINE; |
| 59 | const PS = windows.PS; | 59 | const PS = windows.PS; |
| 60 | const TEB = windows.TEB; | 60 | const TEB = windows.TEB; |
| 61 | const VIRTUAL_MEMORY = windows.VIRTUAL_MEMORY; | ||
| 62 | const MEMORY_RANGE_ENTRY = windows.MEMORY_RANGE_ENTRY; | ||
| 61 | 63 | ||
| 62 | // ref: km/ntifs.h | 64 | // ref: km/ntifs.h |
| 63 | 65 | ||
| ... | @@ -631,6 +633,15 @@ pub extern "ntdll" fn NtProtectVirtualMemory( | ... | @@ -631,6 +633,15 @@ pub extern "ntdll" fn NtProtectVirtualMemory( |
| 631 | OldAccessProtection: *PAGE, | 633 | OldAccessProtection: *PAGE, |
| 632 | ) callconv(.winapi) NTSTATUS; | 634 | ) callconv(.winapi) NTSTATUS; |
| 633 | 635 | ||
| 636 | pub 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 | |||
| 634 | pub extern "ntdll" fn NtWaitForAlertByThreadId( | 645 | pub extern "ntdll" fn NtWaitForAlertByThreadId( |
| 635 | Address: ?*const anyopaque, | 646 | Address: ?*const anyopaque, |
| 636 | Timeout: ?*const LARGE_INTEGER, | 647 | Timeout: ?*const LARGE_INTEGER, |