与全局数组相比,为什么在堆中为局部数组分配内存更快?

Why is it faster to allocate memory in the heap for a local array compared to a global array?

下面是我决定 运行 的一个简单实验。 test1() 是一个为全局数组 g 分配内存的函数,然后有一个 for 循环更新这个数组的所有元素。最后,它释放为 g 分配的内存。 test2 中发生了完全相同的事情,但现在我们使用本地数组而不是称为 l.

#include <iostream>
#define n 1000000000
using namespace std;

int *g;

void test1(){
    g = new int[n];
    int i;
    for(i=0;i<n;i++) g[i] = i;
    delete[] g;
}

void test2(){

    int *l = new int[n];
    int i;
    for(i=0;i<n;i++) l[i] = i;
    delete[] l;
}
int main()
{

    timespec cpu_time_s;
    timespec cpu_time_e;

    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_time_s);
    test1();
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_time_e);
    long long int ns = (cpu_time_e.tv_sec * 1000000000 
    + cpu_time_e.tv_nsec - (cpu_time_s.tv_sec * 1000000000 
    +  cpu_time_s.tv_nsec));
    cout<<ns<<" ns"<<endl;

    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_time_s);
    test2();
    clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &cpu_time_e);
    ns = (cpu_time_e.tv_sec * 1000000000 
    + cpu_time_e.tv_nsec - (cpu_time_s.tv_sec * 1000000000 
    +  cpu_time_s.tv_nsec));
    cout<<ns<<" ns"<<endl;

    return 0;
}

我 运行 进行了一些测试,我绕过 2709066246 ns 用于 test1()2459390299 ns 用于 test2

由于第一个测试 运行 较慢,无论它是哪个,那么 OS 可能需要更长的时间来分配 4GB,然后在第一次通过时将其映射到可写内存。在极端情况下,它可能需要将其他内容保存到交换文件中以使 RAM 第一次可用,但第二次则不然。在轻负载下,您希望这不是必需的,但 OS 仍然可能需要做一些事情,例如删除磁盘缓存并重新使用它之前占用的内存。这是快速但不是即时的。

在第二遍中,您刚刚释放了 4GB 内存,所有这些内存最近都已映射,因此要做的工作可能会少一些。释放的 4GB 甚至可能仍然与您的进程相关联,因此 OS 根本没有任何关系,尽管您不希望有那么大的块。

您可以通过 运行ning test2 两次进行检查,看看第一次是否始终较慢。如果大体上是这样的话,那就是分配和写入数组,而不是你如何做的细节。

如果全局无论顺序如何都变慢,那么我能想到的最明显的可能原因是编译器可能不会将 g 保存在寄存器中,而是可能会重复将其从寄存器中加载出来全局区域。您可以通过查看它发出的代码来检查。

这个现象和我在this post的回答中描述的一样。

TL;DR 当您访问之前分配的内存时,OS 需要分配页面。当您释放并重新分配时,您最终会进入相同的内存区域(或几乎相同),并且页面已经由先前的访问分配。

请注意,页面分配完全独立于内存分配。分配 4GB 不会使用 4GB 或物理内存,直到您访问它。

要对此进行测试,请尝试在第一个循环中只访问一半的内存,在第二个循环中尝试访问另一半。结果应该是相同的。您也可以尝试只访问奇数页,然后访问偶数页(页面长 4096 字节,1024 整数)。