You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取进程可共享与共享工作集大小并优化低效代码?

优化方案与替代API建议

一、现有代码的针对性优化

1. 缓存系统页面大小

系统页面大小在运行时不会改变,无需每次调用都重新获取,直接缓存到静态字段:

private static readonly uint _cachedPageSize = GetMemoryPageSize();

2. 预估初始缓冲区大小,减少扩容次数

原代码初始缓冲区仅16字节,会导致多次调用QueryWorkingSet和内存重分配,开销极大。可以先通过GetProcessMemoryInfo获取进程工作集总大小,估算所需条目数:

// 先获取进程工作集大小,预估条目数
PROCESS_MEMORY_COUNTERS_EX pmc = new();
pmc.cb = (uint)Marshal.SizeOf(typeof(PROCESS_MEMORY_COUNTERS_EX));
if (GetProcessMemoryInfo(processHandle, ref pmc, pmc.cb))
{
    uint estimatedEntries = (uint)(pmc.WorkingSetSize / _cachedPageSize);
    size = (uint)(sizeof(ulong) + estimatedEntries * sizeof(nuint));
}
else
{
    // 预估失败时使用较大默认值,比如1024条目的大小
    size = (uint)(sizeof(ulong) + 1024 * sizeof(nuint));
}

这样大概率能一次性分配足够的缓冲区,避免多次扩容和系统调用。

3. 直接统计目标大小,省略不必要的数组分配

如果你的最终需求只是获取共享/可共享工作集大小,不需要保留所有工作集条目,可直接在遍历内存时统计,无需创建nuint[]数组:

ulong* dataPointer = (ulong*)pointer;
uint numberOfEntries = (uint)*dataPointer;
dataPointer += 1;

ulong sharedPageCount = 0;
for (var i = 0; i < numberOfEntries; i++)
{
    // 解析PSAPI_WORKING_SET_BLOCK的Shared标志位(64位系统下为第57位)
    nuint block = *dataPointer;
    bool isShared = (block & (nuint)0x2000000000000000) != 0;
    if (isShared)
    {
        sharedPageCount++;
    }
    dataPointer += 1;
}

// 计算共享工作集大小
ulong sharedWorkingSetSize = sharedPageCount * _cachedPageSize;
// 可共享工作集需根据Win32Protection标志判断,比如PAGE_READWRITE等可共享属性,按需补充逻辑

省略数组分配和复制操作,能大幅降低CPU和内存开销。

4. 批量内存复制替代逐元素循环(若需保留条目)

如果必须保留所有工作集条目,使用Buffer.MemoryCopy替代手动循环赋值,效率更高:

info.WorkingSetInfo = new nuint[numberOfEntries];
fixed (nuint* dest = info.WorkingSetInfo)
{
    Buffer.MemoryCopy(dataPointer, dest, numberOfEntries * sizeof(nuint), numberOfEntries * sizeof(nuint));
}

二、替代API方案

1. 使用QueryWorkingSetEx

该函数返回更详细的工作集页面信息(PSAPI_WORKING_SET_EX_INFORMATION),直接包含页面共享状态、保护属性等字段,无需手动解析位标志,代码更清晰,效率与QueryWorkingSet相当:

[DllImport("psapi.dll", SetLastError = true)]
private static extern bool QueryWorkingSetEx(IntPtr hProcess, void* pv, uint cb);

// 使用示例
uint size = ...; // 预估大小
void* pointer = Alloc(size);
bool result = QueryWorkingSetEx(processHandle.DangerousGetHandle(), pointer, size);
// 后续直接解析PSAPI_WORKING_SET_EX_INFORMATION结构的Shared字段

2. 底层原生API:NtQueryVirtualMemory

若追求极致性能,可调用原生NtQueryVirtualMemory函数(需注意系统兼容性),直接遍历进程虚拟内存区域,判断是否在工作集内、是否为共享页面。不过该API属于未公开接口,需要处理更多底层细节,仅推荐对Windows内核机制熟悉的场景使用。

三、额外优化建议

  • 降低调用频率:如果该函数被高频调用(如每秒多次),会累积CPU开销,可增加调用间隔或仅在必要时触发。
  • 后台线程执行:将该操作放到后台线程,避免阻塞UI线程,同时分散CPU负载。

内容的提问来源于stack exchange,提问作者dt_overflow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 07:03:19