实现细节
进程绑定
通常情况下,每个 CPU 拥有自己的 L1 Cache。因此在测试之前,最好把程序绑定在指定的 CPU 上,缓解调度带来的 L1 DCache Miss。
参考方法:
#define _GNU_SOURCE
int main() {
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(3, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask) < 0) {
perror("sched_setaffinity");
}
// More code follows
}
计时方式
单次访存的延迟太小,所以需要每次实验有足够的访存次数。
计时最好使用 c++ 中的 clock(),使用方法可以参考矩阵乘法程序。
访存相关
x86-64 架构的处理器的 CPU 和 L1 Cache 之间还存在一些 Buffer 优化访存操作。其中 Store 操作收到的影响要远小于 Load 操作受到的影响。所以建议访存时使用 Store 操作。
(c++17 之前)对于需要计时的代码段内部的中间变量,建议使用 register 关键字将其与寄存器绑定。但注意 register 关键字是一种推荐,而不是强制。同时 x86-64 架构的通用寄存器数量不算多。
如果希望使用 load 操作,但担心被编译器优化,可以使用内联汇编的方式,也可以尝试使用 volatile 关键字。
TLB 相关
实验过程中 TLB miss 会在一定程度上影响测量结果。可以使用大页映射降低干扰。
开启过程如下:
-
查看内核对大页的支持
查看内核配置文件
/proc/config.gz
中 CONFIG_HUGETLB_PAGE 和 CONFIG_HUGETLBFS 是否开启。 -
配置大页
给某个根目录配置大页,使该目录下的文件操作都使用 2MiB 大页(xxx 为某个目录名,不是文件名。需要在 root 权限下完成操作)。
mkdir /mnt/xxx mount none /mnt/xxx -t hugetlbfs
-
分配空闲大页
XX 为分配的大页数量。需要在root权限下完成操作。
echo XX > /proc/sys/vm/nr_hugepages
-
代码中使用内存映射替代内存分配操作
int fd = open("/mnt/wsl/huge/temp", O_CREAT | O_RDWR, 0755); if (fd < 0) { perror("cannot open huge page"); } int *array = (int*) mmap(0, ALLOCATE_SIZE * sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // use array in the following codes
最后更新:
2023年5月14日
作者: