跳转至

实现细节

进程绑定

通常情况下,每个 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 会在一定程度上影响测量结果。可以使用大页映射降低干扰。

开启过程如下:

  1. 查看内核对大页的支持

    查看内核配置文件 /proc/config.gz 中 CONFIG_HUGETLB_PAGE 和 CONFIG_HUGETLBFS 是否开启。

  2. 配置大页

    给某个根目录配置大页,使该目录下的文件操作都使用 2MiB 大页(xxx 为某个目录名,不是文件名。需要在 root 权限下完成操作)。

    mkdir /mnt/xxx
    mount none /mnt/xxx -t hugetlbfs
    
  3. 分配空闲大页

    XX 为分配的大页数量。需要在root权限下完成操作。

    echo XX > /proc/sys/vm/nr_hugepages
    
  4. 代码中使用内存映射替代内存分配操作

    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日
作者:cuibst