MassBattle Docsv1.19.20

MassAPIAffinity.h

自由函数 ×5 控制台命令 ×5

Source/MassAPI/Public/MassAPIAffinity.h

概述 Overview

线程亲和性(Thread Affinity)控制工具集——外部启动脚本的引擎内替代。提供两层杠杆:只钉任务图 worker(MassApiPinTaskWorkers / MassApiResetTaskWorkerAffinity,逐线程 OpenThread+SetThreadAffinityMask)与整进程亲和(MassApiSetProcessAffinity 等,OS 级、约束所有线程含游戏/渲染)。设计依据(源码注释记载的 5.6 事实):重写后的 LowLevelTasks 调度器没有运行期 worker 数量旋钮(TaskGraph.NumWorkerThreadsToIgnore 是死旋钮,其 StallForTuning 为空实现),基础 worker 数启动时定死;worker 线程都是注册在 FThreadManager 的 FRunnableThread,因此逐线程亲和是唯一免改引擎的有效杠杆——只钉 ParallelForEachEntityChunk / ParallelFor 的执行地(任务图 worker),游戏/渲染/RHI 线程仍占全机。全部实现仅 Windows;每个函数都在同名 .cpp 中由 FAutoConsoleCommand 注册对应的 massapi.* 控制台命令。

A thread-affinity control toolkit — the in-engine replacement for external launcher scripts. Two levers are provided: pin task-graph workers only (MassApiPinTaskWorkers / MassApiResetTaskWorkerAffinity, per-thread OpenThread+SetThreadAffinityMask) and whole-process affinity (MassApiSetProcessAffinity etc., OS-level, constrains every thread including game/render). The design rests on 5.6 facts recorded in the source comment: the rewritten LowLevelTasks scheduler exposes NO runtime worker-count knob (TaskGraph.NumWorkerThreadsToIgnore is a dead stub — its StallForTuning is empty) and the base worker count is fixed at startup; worker threads ARE FRunnableThreads registered in FThreadManager, so per-thread affinity is the one effective lever that needs no engine change — pin ONLY where ParallelForEachEntityChunk / ParallelFor run (task-graph workers), while game/render/RHI keep the whole machine. Everything is Windows-only; each function gets a matching massapi.* console command registered by FAutoConsoleCommand in the same-named .cpp.

公开函数 Public Functions

MassApiPinTaskWorkers MASSAPI_API int32 (uint64 AffinityMask)

把所有调度器 worker 线程钉进掩码。实现:先与当前进程掩码求交,交集为空则告警并返回 0;再遍历 FThreadManager::ForEachThread,仅对名字匹配任务 worker 命名(" Worker #" 或 " Worker (Standby" 后缀,覆盖 Foreground/Background/TaskThread 等全部前缀变体,同时排除 GameThread / RenderThread / RHI / 自建池线程)的线程执行 OpenThread → SetThreadAffinityMask → CloseHandle。每个成功钉住的线程记录其原始掩码(仅首次钉住记录真实原值)供 MassApiResetTaskWorkerAffinity 精确还原;成功与失败均有 LogMass Warning 日志。

Pins every scheduler-worker thread into the mask. Implementation: intersects with the current process mask first (an empty intersection warns and returns 0), then walks FThreadManager::ForEachThread and, only for threads whose names match the task-worker naming (suffix " Worker #" or " Worker (Standby" — covers every prefixed variant Foreground/Background/TaskThread… while excluding GameThread / RenderThread / RHI / our own pool threads), performs OpenThread → SetThreadAffinityMask → CloseHandle. Each successfully pinned thread's original mask is remembered (only the first pin records the true original) for exact restore by MassApiResetTaskWorkerAffinity; both success and failure log a LogMass Warning.

Input
参数 Param类型 Type说明 Description
AffinityMaskuint64目标亲和掩码(每 bit 一个逻辑核)。内部先与进程掩码求交。Target affinity mask (one bit per logical core). Intersected with the process mask internally.
Output

返回成功钉住的线程数;交集为空或非 Windows 平台返回 0。副作用:被钉线程立即只在掩码核上调度;静态表 GPinnedWorkerMasks(ThreadId → 原始掩码)写入/更新。非 Windows 平台打日志「仅支持 Windows」。

Returns the number of threads pinned; 0 when the intersection is empty or off Windows. Side effects: pinned threads immediately schedule only on the mask's cores; the static map GPinnedWorkerMasks (ThreadId → original mask) is written/updated. Non-Windows logs "Windows-only".

MassApiResetTaskWorkerAffinity MASSAPI_API int32 ()

把先前由 MassApiPinTaskWorkers 钉住的 worker 线程逐一恢复到各自原始亲和掩码(取自 GPinnedWorkerMasks 记录),随后清空该表并打日志。未被钉过的线程不受影响。

Restores every worker thread previously pinned by MassApiPinTaskWorkers back to its original affinity mask (from the GPinnedWorkerMasks record), then resets the map and logs. Threads never pinned are untouched.

Input

无参数。

No parameters.

Output

返回成功还原的线程数。副作用:清空 GPinnedWorkerMasks——还原后再次钉住会重新记录原值。非 Windows 返回 0。

Returns the number of threads restored. Side effect: GPinnedWorkerMasks is reset — after a restore, a later pin re-records originals. Returns 0 off Windows.

MassApiSetProcessAffinity MASSAPI_API bool (uint64 AffinityMask)

整进程亲和(OS 级:约束所有线程,含游戏/渲染/RHI)。实现:与当前进程掩码求交,交集为空返回 false;随后对当前进程调用 Win32 SetProcessAffinityMask,成败均打日志。

Whole-process affinity (OS-level: constrains EVERY thread including game/render/RHI). Implementation: intersects with the current process mask (empty intersection returns false), then calls the Win32 SetProcessAffinityMask on the current process; success and failure both log.

Input
参数 Param类型 Type说明 Description
AffinityMaskuint64目标进程掩码;内部与当前进程掩码求交。Target process mask; intersected with the current process mask internally.
Output

成功返回 true;交集为空、OS 调用失败或非 Windows 返回 false。副作用:进程内所有线程的可用核集合立即收窄。

Returns true on success; false on empty intersection, OS failure, or off Windows. Side effect: the set of usable cores for every thread in the process narrows immediately.

MassApiSetReservedCores MASSAPI_API bool (int32 NumPhysicalCoresToReserve)

用户友好的核心预留:把最后 N 个物理核(连同其超线程兄弟)从进程亲和中移除、让给其它软件——无需手写掩码。实现:Win32 GetLogicalProcessorInformationEx(RelationProcessorCore) 枚举物理核拓扑(每个物理核一条记录,掩码覆盖其超线程兄弟);首次调用时用静态变量记录原始进程掩码,N ≤ 0 即恢复该原始掩码;物理核掩码按 CountTrailingZeros64 升序排序后从尾部丢弃 N 个(= 逻辑序号最高的物理核;引擎把低核留给游戏/RHI/渲染),并钳制至少保留一个物理核;新掩码经 MassApiSetProcessAffinity 应用并打日志。

User-friendly core reservation: gives up the LAST N PHYSICAL cores (with their hyperthread siblings) to other software by removing them from the process affinity — no masks to write. Implementation: Win32 GetLogicalProcessorInformationEx(RelationProcessorCore) enumerates the physical-core topology (one RelationProcessorCore entry per physical core, its mask covering the hyperthread siblings); a static variable captures the original process mask on the first call and N ≤ 0 restores it; physical-core masks are sorted ascending by CountTrailingZeros64 and N dropped from the TAIL (= the highest logical indices — the engine keeps low cores for game/RHI/render), clamped so at least one physical core remains; the new mask is applied via MassApiSetProcessAffinity and logged.

Input
参数 Param类型 Type说明 Description
NumPhysicalCoresToReserveint32要预留的物理核数 N;0 = 恢复首次调用时记录的原始掩码。内部钳制到「物理核总数 − 1」。Number of physical cores N to reserve; 0 restores the original mask captured on the first call. Internally clamped to (physical core count − 1).
Output

成功返回 false 以外值即 true;拓扑查询失败、当前掩码为 0、应用失败或非 Windows 返回 false。副作用:进程掩码收窄(或恢复)。仅支持单处理器组(≤64 逻辑核)——每个物理核的各组掩码被 OR 进同一个 uint64。

Returns true on success; false when topology queries fail, the current mask is 0, application fails, or off Windows. Side effect: the process mask narrows (or is restored). Single processor group (≤64 logical cores) only — each core's per-group masks are OR-ed into one uint64.

MassApiGetProcessAffinityMask MASSAPI_API uint64 ()

查询当前进程亲和掩码。实现为 Win32 GetProcessAffinityMask(进程掩码 + 系统掩码双出参,仅取进程掩码)。

Queries the current process affinity mask. Implemented over the Win32 GetProcessAffinityMask (dual out-params process mask + system mask; only the process mask is taken).

Input

无参数。

No parameters.

Output

返回当前进程掩码;失败或不支持的平台(非 Windows)返回 0。无副作用。

Returns the current process mask; 0 on failure or unsupported (non-Windows) platforms. No side effects.

控制台命令 Console Commands

同名 .cpp 以 FAutoConsoleCommand 注册了五个命令,把上述函数暴露到运行期控制台(打包游戏同样可用——.cpp 显式引入 HAL/IConsoleManager.h 以保证 unity 包含链差异下的可用性):

The same-named .cpp registers five commands via FAutoConsoleCommand, exposing the functions above to the runtime console (usable in packaged games too — the .cpp explicitly includes HAL/IConsoleManager.h so availability survives unity-include-chain differences):

命令 Command参数 Args说明 Description
massapi.PinTaskWorkers<CoreCount> [StartCore] 或 <0xMask>把调度器 worker 线程钉进指定核;游戏/渲染/RHI 线程不受限。起始核默认 2(引擎把 0-1 留给游戏/RHI/渲染)。0x 前缀按原始掩码解析;否则按「核数 + 起始核」构造连续掩码。Pins scheduler worker threads to the given cores; game/render/RHI stay unrestricted. StartCore defaults to 2 (engine reserves cores 0-1). A 0x prefix parses as a raw mask; otherwise a contiguous mask is built from core count + start core.
massapi.ResetTaskWorkers无把钉住的调度器 worker 恢复到原始亲和掩码。Restores pinned scheduler workers to their original affinity masks.
massapi.SetProcessAffinity<0xMask>整进程的 OS 级亲和(所有线程,含游戏/渲染)。缺参/非 0x 前缀时打印用法与当前掩码。OS-level affinity for the WHOLE process (every thread, incl. game/render). Prints usage and the current mask when the arg is missing or not 0x-prefixed.
massapi.SetReservedCores<N>用户友好:把最后 N 个物理核(含超线程兄弟)让给其它软件;无需掩码。0 还原。User-friendly: gives up the LAST N PHYSICAL cores (with hyperthread siblings) to other software; no masks needed. 0 restores.
massapi.ListThreads无列出所有注册线程的名字/ID,任务 worker 额外标注 <task-worker> —— 亲和目标的发现工具。Lists every registered thread with name/id, tagging task workers with <task-worker> — a discovery tool for affinity targeting.

管线角色 Pipeline Role

本文件是独立工具层,发布树中无其它 C++ 调用点(已 grep 验证:五个函数仅在 MassAPIAffinity.cpp 内被自身控制台命令引用)。使用方式即运行期控制台命令(见上表),典型场景是把任务图 worker——ParallelForEachEntityChunk / ParallelFor 的执行线程,即 Mass 实体分片并行处理的物理载体——收拢到指定核,与游戏/渲染线程隔离,减小大规模实体 tick 的调度抖动。函数不参与任何 tick/处理器管线,调用即生效。

This file is a standalone utility layer with no other C++ call sites in the release tree (grep-verified: the five functions are referenced only by their own console commands inside MassAPIAffinity.cpp). Usage is via the runtime console commands above; the typical scenario pulls the task-graph workers — the threads where ParallelForEachEntityChunk / ParallelFor execute, i.e. the physical carrier of parallel Mass entity-chunk processing — onto dedicated cores away from game/render threads, reducing scheduling jitter for large entity ticks. The functions take part in no tick/processor pipeline; each call takes effect immediately.

补充说明 Notes

全部实现都在 #if PLATFORM_WINDOWS 内;非 Windows 平台各函数返回 0/false(MassApiPinTaskWorkers 额外打「仅支持 Windows」日志)。MassApiSetReservedCores 仅支持单处理器组(≤64 逻辑核)。原始掩码记录规则:GPinnedWorkerMasks 以 FindOrAdd 写入——同一线程多次钉住只记第一次的真实原值,避免把中间态误当原值还原。

All implementations sit inside #if PLATFORM_WINDOWS; off Windows every function returns 0/false (MassApiPinTaskWorkers additionally logs "Windows-only"). MassApiSetReservedCores supports a single processor group (≤64 logical cores) only. Original-mask bookkeeping rule: GPinnedWorkerMasks is written via FindOrAdd — repeated pins of the same thread record only the first true original, preventing an intermediate mask from being mistaken for the original on restore.