为何栈内存与堆内存的memset性能存在差异?
根据你提供的测试代码和结果,栈内存首次memset性能远高于堆内存,但堆内存第二次执行时性能与栈持平,核心原因在于内存页的首次映射与初始化机制差异,具体如下:
1. 堆内存首次访问的内核态页分配开销
malloc分配的是虚拟内存空间,此时并未真正分配物理内存。当你第一次调用memset写堆内存时,会触发**写时复制(COW)**机制:内核需要为对应的虚拟页分配实际的物理页,同时将物理页清零(符合malloc返回未初始化内存的语义,内核底层会保证页初始为0),这个过程涉及内核态的页分配、映射操作,带来额外开销,导致第一次memset耗时更长。
而栈内存的物理页在程序启动(或线程创建)时已经由内核预先完成映射和初始化,首次memset时不需要触发额外的内核页分配流程,因此耗时更短。
2. 第二次执行时堆内存已完成物理映射
从更新的测试结果可以看到,堆内存的第二次memset耗时和栈完全一致——这是因为第一次执行后,堆的虚拟地址已经绑定了物理页,后续访问不再需要内核介入分配页,只需要用户态的内存写入操作,性能自然和栈持平。
3. 栈内存的预分配特性
你通过编译选项-Wl,--stack,1500000000指定了大栈空间,链接器会告知内核预留足够的栈内存。内核在加载程序时,会提前为栈完成虚拟内存到物理内存的映射(或按栈增长需求提前准备好物理页),避免了栈内存首次访问时的缺页分配开销,这也是栈两次memset性能稳定的原因。
补充验证建议
可以通过perf stat工具统计两次执行的缺页次数和内核态耗时,进一步验证:
perf stat ./stack_heap.exe perf stat ./stack_heap.exe 1
会发现堆测试的第一次执行中,major-faults(大缺页,需要内核分配物理页)的数量远高于栈测试,而第二次堆测试的major-faults会降到0,和栈测试一致。
内容的提问来源于stack exchange,提问作者Ivan Bychkov

