尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

mimalloc Windows 平台原语(primitives)与 ETW 分配事件追踪实战指南

mimalloc Windows 平台原语(primitives)与 ETW 分配事件追踪实战指南 mimalloc Windows 平台原语primitives与 ETW 分配事件追踪实战指南【免费下载链接】mimallocmimalloc is a compact general purpose allocator with excellent performance.项目地址: https://gitcode.com/GitHub_Trending/mi/mimallocmimalloc 通过src/prim/windows/下的平台原语层封装 Windows 系统分配器能力VirtualAlloc 家族、NUMA、大页等并提供基于 ETWEvent Tracing for Windows的高性能分配/释放事件追踪能力。本文以src/prim/windows/readme.md为核心骨架结合 prim.c、etw.man、etw-mimalloc.wprp 与 track.h 等源码讲解如何用 Windows Performance RecorderWPR采集 mimalloc 的分配轨迹并用 WPA 分析同时深入剖析 Windows 平台原语的底层实现。一、Windows 平台原语Primitives在 mimalloc 中的角色mimalloc 将“从操作系统获取内存”的最小能力集合抽象为可移植性原语层Portability Primitives。该层统一定义于 prim.h而具体实现由 src/prim/prim.c 按宿主平台选择unix/prim.cLinux 等 Unix 系实现wasi/prim.cWebAssembly WASI 实现windows/prim.cWindows 平台实现macOS 的osx/prim.c会 defer 到unix/prim.c。如 src/prim/readme.md 所述该层仍然work in progress源码中个别位置仍保留 OS 相关的 ifdef。对于 Windows 构建src/prim/windows/prim.c承载了所有与操作系统交互的核心职责文件作用prim.cWindows 系统内存分配、提交/释放、NUMA、大页、进程信息等原语实现etw.manmimalloc ETW 事件清单manifest定义分配/释放事件etw.h由etw.man经 MCMessage Compiler工具生成的事件写入头文件etw-mimalloc.wprpWindows Performance RecorderWPR采集 profile二、深入 prim.cWindows 系统内存原语实现剖析windows/prim.c的核心设计是动态绑定 Windows APIGetProcAddress以便在旧系统甚至 Xbox、Store 应用等受限环境上也能运行同时保留对新 API 的能力探测。2.1 动态 API 绑定策略prim.c 开头定义了一系列函数指针与自定义结构逐一声明需要动态解析的 APIVirtualAlloc2/VirtualAlloc2FromApp仅在 Windows 10 / Server 2016 之后可用用于对齐分配因此通过GetProcAddress从kernelbase.dll动态查找优先取VirtualAlloc2FromApp对 Windows Store 应用可用。NtAllocateVirtualMemoryEx从ntdll.dll动态查找用于1GiB 巨页huge OS page分配。NUMA 系列 APIGetCurrentProcessorNumberEx、GetNumaProcessorNodeEx、GetNumaNodeProcessorMaskEx等从kernel32.dll动态查找且区分 Win7 扩展 API 与 Vista/更早的受限 API旧 API 仅支持 64 个处理器。GetLargePageMinimum探测系统支持的大页尺寸通常 2MiB。GetPhysicallyInstalledSystemMemory获取物理内存总量XP 上不存在故动态检查。代码注释明确给出了设计动机We use VirtualAlloc2 for aligned allocation, but it is only supported on Windows 10 and Windows Server 2016. So, we need to look it up dynamically to run on older systems.。此外为保证 32 位兼容函数指针统一使用__stdcall调用约定MI_MEM_EXTENDED_PARAMETER等结构也被最小化地自行定义以避免依赖过新的 SDK 头文件。2.2 系统内存初始化_mi_prim_mem_init_mi_prim_mem_init负责在启动时收集系统配置并写入mi_os_mem_config_t通过GetSystemInfo获取页面大小dwPageSize与分配粒度dwAllocationGranularity根据最大应用地址计算虚拟地址位数解析上述动态 API若mi_option_allow_large_os_pages或mi_option_reserve_huge_os_pages被启用则调用win_enable_large_os_pages尝试开启大页支持。大页权限是关键前置条件源码中win_enable_large_os_pages_onceprim.c通过OpenProcessTokenAdjustTokenPrivileges尝试为当前进程启用SeLockMemoryPrivilege即组策略中的Lock pages in memory权限。若权限不足会打印cannot enable large OS page support警告并返回失败。2.3 虚拟内存分配、对齐与大页回退分配入口为_mi_prim_alloc内部由win_virtual_alloc组织三级策略prim.c大页优先若允许/要求大页2MiB先以MEM_LARGE_PAGES | MEM_COMMIT | MEM_RESERVE尝试一旦失败会记录失败次数large_page_try_ok之后连续 10 次分配不再尝试大页避免反复触发昂贵的VirtualAlloc调用源码注释if a large page allocation fails, it seems the calls to VirtualAlloc get very expensive。对齐分配win_virtual_alloc_prim_onceprim.c在 64 位系统上先用_mi_os_get_aligned_hint尝试在 2TiB 之后的地址区域做 4MiB 对齐的 hint 分配失败后若对齐是页大小的整数倍且pVirtualAlloc2可用则改用VirtualAlloc2的MiMemExtendedParameterAddressRequirements参数精确对齐。兜底普通VirtualAlloc(addr, size, flags, PAGE_READWRITE)。另外win_virtual_alloc_primprim.c实现了OOM 重试当分配普通已提交内存时若遇到ERROR_COMMITMENT_MINIMUM、ERROR_COMMITMENT_LIMIT、ERROR_PAGEFILE_QUOTA、ERROR_NOT_ENOUGH_MEMORY这类内存不足错误且mi_option_retry_on_oom开启则按tries*40毫秒的递增间隔重试最多 10 次约 2.2 秒等待系统释放内存。2.4 1GiB 巨页分配_mi_prim_alloc_huge_os_pages支持1GiB 巨页对应reserve_huge_os_pages选项现代 Windows 上优先调用NtAllocateVirtualMemoryEx通过MI_MEM_EXTENDED_PARAMETER_NONPAGED_HUGE属性标记非分页巨页并可通过MiMemExtendedParameterNumaNode指定 NUMA 节点若失败例如硬件/系统不支持 1GiB 页会打印unable to allocate using huge (1GiB) pages, trying large (2MiB) pages instead并自动回退到 2MiB 大页为免反复尝试失败后通过原子标志mi_huge_pages_available永久停用 1GiB 尝试回退路径依次为VirtualAlloc2的 NUMA 感知分配numa_node 0时→ 普通VirtualAlloc(…, MEM_LARGE_PAGES, …)。2.5 Commit / Decommit / Reset / Protect_mi_prim_commitprim.c以VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE)提交物理页_mi_prim_decommitprim.c以VirtualFree(addr, size, MEM_DECOMMIT)解除提交且出于安全考虑无论成败都假定需要重新提交*needs_recommit true_mi_prim_resetprim.c以VirtualAlloc(…, MEM_RESET, …)丢弃页内容将物理页归还给系统_mi_prim_protectprim.c通过VirtualProtect在PAGE_NOACCESS与PAGE_READWRITE之间切换保护属性用于守卫页guard pages。2.6 其他平台原语NUMA 感知_mi_prim_numa_node与_mi_prim_numa_node_countprim.c优先使用 Win7 扩展 API支持 64 以上处理器否则回退到受限于 64 处理器的旧 API节点计数会从最高节点号向下查找实际分配了处理器的节点。高精度时钟_mi_prim_clock_now基于QueryPerformanceCounter/QueryPerformanceFrequencyprim.c。进程信息_mi_prim_process_info通过GetProcessTimes与按需加载的psapi.dll的GetProcessMemoryInfo获取用户/内核时间、当前/峰值 RSS、提交量与缺页计数prim.c。随机数_mi_prim_random_buf优先使用bcrypt.dll的BCryptGenRandomBCRYPT_USE_SYSTEM_PREFERRED_RNG若定义MI_USE_RTLGENRANDOM则改用RtlGenRandom。源码注释提示动态重定向场景下 C 编译可能触发异常、VS 调试器下可能死锁而 Server 2019 上又需要BCryptGenRandom——这是仍在演进中的兼容性权衡。stderr 输出_mi_prim_out_stderr避免依赖 C 运行时可能在主线程退出后失效主线程关闭后直接使用WriteConsoleA/WriteFile写控制台或重定向句柄。环境变量_mi_prim_getenv用GetEnvironmentVariableA替代getenv保证在预加载_mi_preloading()阶段 C 运行时尚未初始化时仍可用。2.7 进程与线程生命周期挂钩prim.c 通过多种机制在进程/线程启动与退出时自动初始化和回收 mimalloc入口统一为mi_win_main对应_mi_auto_process_init/_mi_auto_process_done/_mi_thread_done默认方案MI_WIN_INIT_USE_CRT_TLS结合 CRT 初始化回调.CRT$XIB段与 TLS 回调.CRT$XLB/.CRT$XLY段保证 attach 事件先于构造函数、detach 事件晚于析构函数静态链接库场景下通过特殊数据段注入回调。MI_WIN_INIT_USE_RAW_DLLMAINDLL 场景下通过 CRT_pRawDllMain挂钩EXE 场景下依赖 TLS 回调。MI_WIN_INIT_USE_TLS_DLLMAIN直接实现DllMainIntel ICX 编译器所需见 issue #1268。MI_WIN_INIT_USE_FLS已废弃静态链接时基于 Fiber Local StorageFlsAlloc/FlsSetValue在 Fiber 退出时回收线程堆mingw 旧版本需要。此外共享库重定向模式下通过_mi_redirect_entry与mi_allocator_init/mi_allocator_done与重定向模块malloc 重定向到 mi_malloc通信prim.c。三、ETW 事件追踪从 manifest 到分配轨迹ETW 是 Windows 自带的高性能事件追踪机制内核与用户态均可写入开销极低非常适合持续记录所有分配/释放并离线分析。mimalloc 将其分配事件发布为 ETW 事件配合 WPR 采集、WPA 分析即可还原程序的完整内存行为。3.1 事件定义etw.man manifestetw.man 是 ETW 事件的**清单manifest**文件UTF-16 编码注释提示需在 Visual Studio 命令提示符下执行mc .\etw.man编译。其核心内容包括Providermicrosoft-windows-mimallocGUID 为{138f4dbb-ee04-4899-aa0a-572ad4475779}两个事件readme 中明确指出100 is an allocation, 101 is for a freeETW_MI_ALLOCvalue100levelInformational分配事件ETW_MI_FREEvalue101levelInformational释放事件事件模板两个事件均携带两个win:UInt64字段——Address地址与Size字节数。通过mc命令该 manifest 会被编译为 etw.h生成的 C 头文件其中包含EventRegistermicrosoft_windows_mimalloc、EventWriteETW_MI_ALLOC、EventWriteETW_MI_FREE等宏/函数。3.2 与 track.h 的集成mimalloc 的追踪抽象层 track.h 支持 Valgrind、ASan、ETW 三种后端。当定义MI_TRACK_ETW时track.h#include ../src/prim/windows/etw.h #define mi_track_init() EventRegistermicrosoft_windows_mimalloc() #define mi_track_done() EventUnregistermicrosoft_windows_mimalloc() #define mi_track_malloc_size(p,reqsize,size,zero) EventWriteETW_MI_ALLOC((UINT64)(p), size) #define mi_track_free_size(p,size) EventWriteETW_MI_FREE((UINT64)(p), size)也就是说mimalloc 内部的每次分配/释放都会经由mi_track_malloc_size/mi_track_free_size宏向 ETW 写入地址与大小。值得注意的是size语义根据 track.h 头注释若开启MI_PADDING则size reqsize字节精确否则size是可用块大小可能大于请求大小分析时需注意区分。3.3 构建启用-DMI_TRACK_ETWONETW 支持通过 CMake 选项开启见 CMakeLists.txt。项目主 readme.md 的 ETW 章节给出了明确指引cmake -DMI_TRACK_ETWON ..CMake 侧有两个硬性约束CMakeLists.txt仅支持 Windows非 Windows 平台会自动关闭并告警Can only enable ETW support on Windows (MI_TRACK_ETWOFF)与 Valgrind / ASan 互斥若同时开启了MI_TRACK_VALGRIND或MI_TRACK_ASANETW 会被强制关闭并告警因为三个后端共享同一套mi_track_*宏。启用后mimalloc 每次 malloc/free 都会写入 ETW 事件带来一定运行时开销CMake 注释标注 adds a small overhead适合性能分析场景。3.4 使用 WPR 采集分配轨迹核心操作步骤etw-mimalloc.wprp是现成的Windows Performance Recorder 采集 profile。按照 readme.md 与 src/prim/windows/readme.md 的说明完整流程如下以管理员身份打开命令提示符WPR 采集需要管理员权限 wpr -start src\prim\windows\etw-mimalloc.wprp -filemode-start指定采集 profile-filemode表示将轨迹写入文件而非内存缓冲区。运行被测程序 my mimalloc program停止采集并保存 ETL 文件 wpr -stop test.etlreadme 中亦可使用wpr -stop my_mimalloc_program.etl按程序名命名。分析在Windows Performance AnalyzerWPA中打开生成的test.etl即可查看按时间线组织的分配/释放事件流也可以借助 TraceControl 等专门用于分析 mimalloc 轨迹的工具做进一步聚合统计。3.5 解读 etw-mimalloc.wprp profileetw-mimalloc.wprp 定义了完整的 WPR 采集配置值得逐项了解SystemCollector系统事件收集器缓冲区 1024KB × 100 个并启用Loader关键字记录模块加载便于符号解析EventCollectorMimalloc Collectormimalloc 专用事件收集器EventProviderName138f4dbb-ee04-4899-aa0a-572ad4475779即 manifest 中的 provider GUID并设置NonPagedMemorytrue非分页内存防止事件丢失与Stacktrue记录调用栈这是分析谁分配了这块内存的关键EventFilters限定只采集100分配与101释放两个事件 IDProfileCustomHeap.Verbose.FileLoggingModeFile写文件、DetailLevelVerbose详细级别、ProblemCategoryResource Analysis资源分析并启用 ImageId、BuildInfo、EventMetadata 等合并后置处理事件方便 WPA 正确还原模块与符号信息。也就是说该 profile 已经将 provider、事件筛选、调用栈与符号解析所需的一切配置妥当用户无需手工拼装参数。四、使用注意事项与限制平台限定Windows 原语与 ETW 事件追踪均只在 Windows 构建下生效prim.c选择逻辑见 src/prim/readme.mdETW 的 CMake 强制约束见 CMakeLists.txt。追踪后端互斥ETW、Valgrind、ASan 三选一不能同时开启。大页需要权限使用allow_large_os_pages/reserve_huge_os_pages前需在组策略中为进程授予 Lock pages in memory 权限否则运行时打印cannot enable large OS page support并静默回退。1GiB 巨页自动回退NtAllocateVirtualMemoryEx失败后 mimalloc 会打印告警并回退到 2MiB 大页且当次进程生命周期内不再尝试 1GiB 路径日志中的status 0x…可帮助定位失败原因。事件字段语义ETW 事件中的Size在未开启MI_PADDING时是可用块大小而非严格请求大小详见 track.h 头注释Stacktrue的调用栈采集对定位分配来源至关重要但也会带来额外记录开销。通过本文读者应能(1) 理解 mimalloc 在 Windows 上如何通过动态 API 绑定实现虚拟内存分配、大页/巨页与 NUMA 感知(2) 从etw.man→etw.h→track.h这条链路理解 ETW 事件如何被发出(3) 使用wpr -start/stop WPA 完整采集并分析 mimalloc 的分配/释放轨迹为内存优化与泄漏排查提供数据支撑。【免费下载链接】mimallocmimalloc is a compact general purpose allocator with excellent performance.项目地址: https://gitcode.com/GitHub_Trending/mi/mimalloc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表