c++ 解释此CPU缓存处理器效应:操作数呈指数级下降,但平均时间不变

vi4fp9gy  于 2023-01-28  发布在  其他
关注(0)|答案(1)|浏览(174)

对于上下文,此问题与Cache Processor Effects上的博客文章相关,特别是示例1-2。
在下面的代码片段中,我每次都将步长增加2,也就是说,我每次执行的操作数都减少2倍。从博客文章中,我预计步长为1-16时,完成循环的平均时间大致相同。2作者讨论的主要直觉是1)大部分时间是由内存访问贡献的(即先取数,然后乘),而不是算术运算,2)每次CPU取一个缓存行(即64字节或16整数)。
我试着用下面的代码在我的本地机器上重复这个实验,注意我为每个步长分配了一个新的int数组,这样它们就不会利用之前缓存的数据,出于类似的原因,我也只“重复”了每个步长的内部for循环一次(而不是重复实验多次)。

constexpr long long size = 64 * 1024 * 1024; // 64 MB
for (int step = 1; step <= 1<<15 ; step <<= 1) {
        auto* arr = new int[size]; 
        auto start = std::chrono::high_resolution_clock::now();
        for (size_t i = 0; i < size; i += step) {
            arr[i] *= 3;
        }
        auto finish = std::chrono::high_resolution_clock::now();
        auto microseconds = std::chrono::duration_cast<std::chrono::milliseconds>(finish-start);
        std::cout << step << " : " << microseconds.count() << "ms\n";
        // delete[] arr; (updated - see Paul's comment)
    }

然而,结果与blog post中描述的结果大不相同。
未优化:叮当声++ -g -标准品=c2a -学究式-墙壁-额外-o一个CPU缓存1.cpp
使用-O3优化clang
-g -std=c++2a -Wpedantic -Wall -Wextra -o a cpu缓存1.cpp-O3
请注意,我运行的是Macbook Pro 2019,我的页面大小是4096。从上面的观察,似乎直到1024步长所需的时间大致保持线性。由于每个int是4字节,这似乎与页面的大小有关(即1024*4 = 4096),这让我认为这可能是某种预取/页面相关的优化,即使没有指定优化?
有人知道或解释为什么这些数字会出现吗?

4nkexdtk

4nkexdtk1#

在您的代码中,您调用了new int[size],它本质上是malloc的 Package 器。由于Linux的乐观内存分配策略,内核不会立即分配物理页面/内存给它(请参阅man malloc)。
调用arr[i] *= 3时,如果页面不在转换查找缓冲区(TLB)中,则会发生页面错误。内核将检查所请求的虚拟页面是否有效,但尚未分配关联的物理页面。内核将为所请求的虚拟页面分配物理页面。
对于步骤= 1024,您将请求与arr关联的每一页。对于步骤= 2048,您将请求与arr关联的每隔一页。
分配物理页面的行为是您的瓶颈(根据您的数据,逐个分配64 mb的页面需要大约120 ms)。当您将step从1024增加到2048时,现在内核不需要为每个与arr关联的虚拟页面分配物理页面,因此运行时间减半。
正如@丹尼尔Langr所链接的,您需要“接触”arr的每个元素,或者使用new int[size]{}对arr进行零初始化,这样做的目的是强制内核将物理页面分配给arr的每个虚拟页面。

相关问题