在检查 False Sharing 时,为什么在使用兄弟线程运行时比使用独立线程运行时有更多的 L1d 缓存未命中

如何解决在检查 False Sharing 时,为什么在使用兄弟线程运行时比使用独立线程运行时有更多的 L1d 缓存未命中

(我知道过去有人问过一些相关的问题,但我找不到关于 L1d 缓存未命中和超线程/SMT 的问题。)

在阅读了几天关于虚假共享、MESI/MOESI 缓存一致性协议等超级有趣的内容后,我决定用 C 编写一个小的“基准”(见下文),以测试虚假共享的实际效果。

我基本上有一个包含 8 个双精度值的数组,因此它适合一个缓存行和两个递增相邻数组位置的线程。

此时我应该声明我使用的是 Ryzen 5 3600 ,其拓扑结构可以在 here 中看到。

我创建了两个线程,然后将它们固定在两个不同的逻辑核心上,每个线程访问并更新自己的数组位置,即线程 A 更新数组[2],线程 B 更新数组[3]。

当我使用属于同一核心的硬件线程 #0#6 运行代码时,(如拓扑图所示)共享 L1d 缓存,执行时间约为 5 秒。

当我使用没有任何共同缓存的线程 #0#11 时,完成大约需要 9.5 秒。这个时间差是意料之中的,因为在这种情况下,“缓存线乒乓”正在​​进行。

然而,这正是我的问题所在,当我使用线程 #0#11 时,L1d 缓存未命中少于使用线程运行 #0#6

我的猜测是,当使用没有公共缓存的线程 #0#11 时,当一个线程更新共享缓存行的内容时,根据到 MESI/MOESI 协议,另一个内核中的缓存行无效。因此,即使正在进行乒乓,也不会发生太多缓存未命中(与使用线程 #0#6 运行时相比),只是一堆无效和缓存行块在内核之间传输。

那么,当使用具有公共 L1d 缓存的线程 #0 和 #6 时,为什么会有更多缓存未命中?

(线程 #0#6 ,也有共同的二级缓存,但我认为它在这里没有任何重要性,因为当缓存行失效时,它必须从主内存 (MESI) 或另一个内核的缓存 (MOESI) 中获取,因此 L2 似乎不可能拥有所需的数据,但也被要求提供)。

当然,当一个线程写入 L1d 缓存行时,缓存行会变得“脏”,但这有什么关系呢?位于同一个物理核心上的另一个线程读取新的“脏”值应该没有问题吗?

TLDR:在测试 False Sharing 时,与使用两个兄弟线程(属于同一物理核心的线程)相比,L1d 缓存未命中率大约 3 倍使用属于两个不同物理内核的线程。 (2.34% 与 0.75% 的未命中率,3.96 亿与 1.18 亿的绝对未命中数)。为什么会这样?

(L1d 缓存未命中等所有统计数据均使用 Linux 中的 perf 工具进行测量。)

另外,次要问题,为什么兄弟线程在 IDs 6 中成对分开?即线程 0 的兄弟是线程 6.. 线程 i 的兄弟是线程 i+6。这有什么帮助吗?我在 Intel 和 AMD CPU 中都注意到了这一点。

我对计算机架构非常感兴趣,我还在学习,所以上面的一些可能是错误的,很抱歉。

所以,这是我的代码。只需创建两个线程,将它们绑定到特定的逻辑内核,然后访问相邻的缓存行位置。

#define _GNU_SOURCE

#include <stdio.h>
#include <sched.h>
#include <stdlib.h>
#include <sys/random.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

struct timespec tstart,tend;
static cpu_set_t cpuset;


typedef struct arg_s
{
       int index;
       double *array_ptr;
} arg_t;

void *work(void *arg)
{
    pthread_setaffinity_np(pthread_self(),sizeof(cpu_set_t),&cpuset);
    int array_index = ((arg_t*)arg)->index;
    double *ptr = ((arg_t*)arg)->array_ptr;

    for(unsigned long i=0; i<1000000000; i++)
    {
            //it doesn't matter which of these is used
            // as long we are hitting adjacent positions
            ptr[array_index] ++;
            // ptr[array_index] += 1.0e5 * 4;
    }
    return NULL;
}

int main()
{
    pthread_t tid[2];

    srand(time(NULL));
    
    static int cpu0 = 0;
    static int cpu6 = 6; //change this to say 11 to run with threads 0 and 11

    CPU_ZERO(&cpuset);
    CPU_SET(cpu0,&cpuset);
    CPU_SET(cpu6,&cpuset);

    double array[8];

    for(int i=0; i<8; i++)
            array[i] = drand48();

    arg_t *arg0 = malloc(sizeof(arg_t));
    arg_t *arg1 = malloc(sizeof(arg_t));

    arg0->index = 0; arg0->array_ptr = array;       
    arg1->index = 1; arg1->array_ptr = array;


    clock_gettime(CLOCK_REALTIME,&tstart);

    pthread_create(&tid[0],NULL,work,(void*)arg0);
    pthread_create(&tid[1],(void*)arg1);

    pthread_join(tid[0],NULL);
    pthread_join(tid[1],NULL);


    clock_gettime(CLOCK_REALTIME,&tend);
 }

我使用的是 GCC 10.2.0 编译为 gcc -pthread p.c -o p

然后在分别使用线程 0,6 和 0,11 时运行 perf record ./p --cpu=0,6 或与 --cpu=0,11 相同的东西。

然后在其他情况下运行 perf stat -d ./p --cpu=0,11 相同

使用线程 06 运行:

Performance counter stats for './p --cpu=0,6':

           9437,29 msec task-clock                #    1,997 CPUs utilized          
                64      context-switches          #    0,007 K/sec                  
                 2      cpu-migrations            #    0,000 K/sec                  
               912      page-faults               #    0,097 K/sec                  
       39569031046      cycles                    #    4,193 GHz                      (75,00%)
        5925158870      stalled-cycles-frontend   #   14,97% frontend cycles idle     (75,00%)
        2300826705      stalled-cycles-backend    #    5,81% backend cycles idle      (75,00%)
       24052237511      instructions              #    0,61  insn per cycle         
                                                  #    0,25  stalled cycles per insn  (75,00%)
        2010923861      branches                  #  213,083 M/sec                    (75,00%)
            357725      branch-misses             #    0,02% of all branches          (75,03%)
       16930828846      L1-dcache-loads           # 1794,034 M/sec                    (74,99%)
         396121055      L1-dcache-load-misses     #    2,34% of all L1-dcache accesses  (74,96%)
   <not supported>     LLC-loads                                                   
   <not supported>     LLC-load-misses                                             

       4,725786281 seconds time elapsed

       9,429749000 seconds user
       0,000000000 seconds sys 

使用线程 011 运行:

 Performance counter stats for './p --cpu=0,11':

          18693,31 msec task-clock                #    1,982 CPUs utilized          
               114      context-switches          #    0,006 K/sec                  
                 1      cpu-migrations            #    0,000 K/sec                  
               903      page-faults               #    0,048 K/sec                  
       78404951347      cycles                    #    4,194 GHz                      (74,97%)
        1763001213      stalled-cycles-frontend   #    2,25% frontend cycles idle     (74,98%)
       71054052070      stalled-cycles-backend    #   90,62% backend cycles idle      (74,98%)
       24055983565      instructions              #    0,31  insn per cycle         
                                                  #    2,95  stalled cycles per insn  (74,97%)
        2012326306      branches                  #  107,650 M/sec                    (74,96%)
            553278      branch-misses             #    0,03% of all branches          (75,07%)
       15715489973      L1-dcache-loads           #  840,701 M/sec                    (75,09%)
         118455010      L1-dcache-load-misses     #    0,75% of all L1-dcache accesses  (74,98%)
   <not supported>      LLC-loads                                                   
   <not supported>      LLC-load-misses                                             

       9,430223356 seconds time elapsed

      18,675328000 seconds user
       0,000000000 seconds sys

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res