多线程下不同内存分配大小的性能测试与疑问
百万级对象场景下内存分配的性能疑问与自定义内存管理器评估
我开发的应用需要每秒创建百万级对象,发现内存分配对性能影响显著,因此考虑实现自定义内存管理器,但不确定是否合理。为此我在Windows 11(16核32线程CPU)环境下用Visual C++编译测试代码,针对不同线程数(1、2、4、8、16、32)与不同内存分配大小(4-1024字节)的组合,测试百万次内存分配的耗时。测试结果显示:
- 线程数到8之前性能随线程数增加提升,8线程之后性能反而下降;
- 分配内存越大,耗时显著增加。
疑问1:为何8线程后性能下降?Windows的内存分配是否在多线程下存在问题?
这不是Windows内存分配的“问题”,而是多核多线程场景下的资源竞争与硬件架构特性共同导致的:
- 锁竞争加剧:
malloc/free这类标准分配器内部依赖锁保证线程安全,当线程数超过单个NUMA节点的核心数(你的16核CPU通常分为2个NUMA节点,每个8核),更多线程会争抢分配器的全局锁,锁等待时间占比急剧上升,直接抵消多线程并行的收益。 - NUMA架构的影响:Windows默认分配器会优先从当前线程所在NUMA节点分配内存,8线程刚好是单个NUMA节点的核心数,超过后线程会跨NUMA节点运行,跨节点内存访问的延迟是本地访问的数倍,拉低整体性能。
- 超线程的局限性:超线程是复用物理核心的执行单元实现的,当线程数超过物理核心数后,线程间会争抢缓存、执行端口等物理资源,并行效率快速下降。
疑问2:为何内存分配大小增加会导致耗时剧增?(我理解内存初始化会有影响,但此处不应存在初始化操作)
即使没有显式初始化,内存分配耗时也会随大小上升,核心原因是分配器的层级机制与内核态操作的开销:
- 分配路径差异:小内存(几十字节内)通常从分配器的线程本地缓存或小型内存块池中直接获取,几乎无锁且在用户态完成;而大内存分配需要调用
VirtualAlloc向操作系统申请新内存页,系统调用本身开销远高于用户态操作,还涉及页表更新、物理页分配等内核逻辑,耗时翻倍增长。 - 缓存命中率下降:大内存块更容易超出CPU L1/L2缓存容量,分配过程中涉及的元数据处理、内存地址映射等操作会频繁触发缓存未命中,增加延迟。
- 碎片查找开销:大内存块的分配与释放更容易产生内存碎片,分配器需要花费更多时间遍历空闲块列表寻找合适的空间,多线程场景下这种查找的锁竞争成本会被进一步放大。
自定义内存管理器的可行性结论
后续我添加了释放操作测试,并实现了一个专用简易内存管理器,对比malloc/free与自定义管理器的性能后,结论是:针对百万级小对象的高频分配场景,非常值得深入评估自定义内存管理器的可行性。
专用内存管理器可以通过以下方式规避标准分配器的瓶颈:
- 针对固定大小对象使用线程本地内存池,彻底避免锁竞争;
- 预分配大块内存,在用户态完成内存块的划分与分配,完全规避频繁的系统调用;
- 绑定NUMA节点,让线程优先使用本地节点的内存池,降低访问延迟;
- 采用固定块分配策略,从根源减少内存碎片,提升缓存命中率。
内容的提问来源于stack exchange,提问作者lxndr
相关产品推荐
相关产品推荐

