开发者
资源
我要评分
获取效率
正确性
完整性
易理解
在线提单
论坛求助

修复伪共享问题

简要示范全流程使用动态源码优化工具对源码文件进行采集分析,以falsesharing_demo.cpp源码文件为例,发现伪共享问题,根据优化建议进行修复并验证修复成功。整体流程如图1所示。

图1 整体流程图

工具基于采集的数据输出热点问题,无法保证扫描出程序中所有的伪共享问题。

前提条件

以动态源码分析工具安装路径“/home/DevKit-Optimizer-CLI-x.x.x-Linux-Kunpeng”为例。

操作步骤

  1. 定位伪共享问题发生的位置。
    1. 编译源码文件,生成可执行二进制文件。以源码文件存放路径“/home/test/falsesharing_demo.cpp”为例,请根据实际情况进行修改。
      g++ /home/test/falsesharing_demo.cpp -o falsesharing_demo -g -lpthread

      falsesharing_demo.cpp文件内容如下:

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      #include <sched.h>
      #include <cstring>
      #include <pthread.h>
      #include <stdio.h>
      
      #define EXE_TIME 999999990
      #define NUM_THREADS 2
      
      int arr[32];
      
      void *sum_a(void*)
      {
          int cpu_num = 0;
          cpu_set_t mask;
          cpu_set_t get;
          CPU_ZERO(&mask);
          CPU_SET(cpu_num, &mask);
          if (sched_setaffinity(0, sizeof(mask), &mask) == -1) {
              perror("warning: could not set CPU affinity\n");
          }
          CPU_ZERO(&get);
           if (sched_getaffinity(0, sizeof(get), &get) == -1) {
              perror("warning: could not get CPU affinity\n");
          }
      
          if (CPU_ISSET(cpu_num, &get)) {
              printf("sum_a affinity cpu_id: %d, current cpu: %d\n", cpu_num, sched_getcpu());
          }
      
          int s = 0;
          for (int i = 0; i < EXE_TIME; i++) {
              s = arr[0];
              arr[0] += 1;
          }
      }
      
      void *inc_b(void*)
      {
          int cpu_num = 1;
          cpu_set_t mask;
          cpu_set_t get;
          CPU_ZERO(&mask);
          CPU_SET(cpu_num, &mask);
          if (sched_setaffinity(0, sizeof(mask), &mask) == -1) {
              perror("warning: could not set CPU affinity\n");
          }
          CPU_ZERO(&get);
           if (sched_getaffinity(0, sizeof(get), &get) == -1) {
              perror("warning: could not get CPU affinity\n");
          }
      
          if (CPU_ISSET(cpu_num, &get)) {
              printf("inc_b affinity cpu_id: %d, current cpu: %d\n", cpu_num, sched_getcpu());
          }
          
          int s = 0;
          for (int i = 0; i < EXE_TIME; i++) {
              s = arr[1];
              arr[1] += 1;
          }
      }
      
      int main()
      {
          int ret;
          pthread_t tids[NUM_THREADS];
          ret = pthread_create(&tids[0], NULL, sum_a, NULL);
          if (ret != 0) {
              printf("pthread_create error: error code %d\n", ret);
              return -1;
          }
          ret = pthread_create(&tids[1], NULL, inc_b, NULL);
          if (ret != 0) {
              printf("pthread_create error: error code %d\n", ret);
              return -1;
          }
          pthread_join(tids[0], NULL);
          pthread_join(tids[1], NULL);
          return 0;
      }
      
    2. “/home/test”目录执行二进制文件。
      1
      ./falsesharing_demo
      
      返回信息如下:
      inc_b affinity cpu_id: 1, current cpu: 1
      sum_a affinity cpu_id: 0, current cpu: 0
    3. 保持程序运行,进入源码优化工具目录,指定falsesharing_demo进程的 PID ,采集基础性能数据,生成数据文件。
      cd /home/DevKit-Optimizer-CLI-x.x.x-Linux-Kunpeng
      ./devopt.sh record -p 3315674 -d 5 -o /home/test

      进程PID为3315674,采集时间指定为5s,在“/home/test”下生成数据文件。

      返回信息如下:

      Saved the record data to /home/test/devopt_3315674_20260513155321.rawdata
    4. 启用精细化内存采集模式,基于数据文件进行精细化内存分析。
      ./devopt.sh record -p 3315674 -d 5 -m -i /home/test/devopt_3315674_20260513155321.rawdata

      执行上述命令会读取数据文件中的基础性能数据,启动伪共享检测工具进行精细化内存采集,分析检测伪共享问题,将检测结果追加到原始数据文件中。

      返回信息如下:

      Possible false sharing detected
    5. 选择以下两种方式之一,查看分析结果。
      • 使用script命令查看分析结果。
        ./devopt.sh script -i /home/test/devopt_3315674_20260513155321.rawdata -t memory

        返回信息如下:

        FS 1: 0x4009bc <-> 0x400bcc Type:SL CacheLineAddr:0x420040 Access Info: adjacent;+0x20/+0x24;4B/4B
            A: sum_a(void*)@/home/test/falsesharing_demo:32
            B: inc_b(void*)@/home/test/falsesharing_demo:59
        表1 字段说明

        字段

        字段说明

        FS

        FS(False Sharing)表示该事件属于伪共享类型。

        1

        表示伪共享事件的排序序号,表示伪共享对的分析优先级。

        0x4009bc、0x400bcc

        访问缓存行的程序计数器地址。

        Type

        表示内存访问的类型。

        • SS(Store-Store)表示两次访问都是写操作。
        • SL(Store-Load)表示一次访问是写操作,一次访问是读操作。

        CacheLineAddr

        缓存行中发生伪共享问题的起始地址。

        示例中的“0x420040”为发生伪共享问题的缓存行起始地址。

        Access Info

        内存访问信息,包括访问的缓存行地址是否相邻,偏移量和访问宽度。

        • “adjacent”表示两次访问缓存行的地址相邻。
        • “same-line”表示两次访问缓存行的地址不相邻。

        示例中的“adjacent”表示两次访问缓存行的地址相邻,“+0x20/+0x24”表示两次访问的偏移量为“+0x20”和“+0x24”,“4B/4B”表示两次访问的宽度都是4字节。

        A、32

        第一个程序计数器地址对应的函数名和源码行数。

        示例中的“A”和“32”表示第一个程序计数器地址(0x4009bc)对应函数名和源码行数。

        B、59

        第二个程序计数器地址对应的函数名和源码行数。

        示例中的“B”和“59”表示第二个程序计数器地址(0x400bcc)对应函数名和源码行数。

      • 使用report命令查看分析结果。
        ./devopt.sh report -i /home/test/devopt_3315674_20260513155321.rawdata

        返回信息如下:

        总览界面表格区域中各函数的issue列显示值为M,表示该函数检测出了伪共享问题,按Enter键进入明细页面后,建议区域(Code Suggestion)显示源码优化建议。如下图所示。

        以sum_a(void*)函数为例,建议区域(Code Suggestion)显示内容如下:

        Code Suggestions
        Memory:
        FS 1: self=0x4009bc L:32 <-> inc_b(void*) pc=0x400bcc L:59, kind=SL, cacheLine=0x420040, adjacent(+0x20/+0x24, 4B/4B)
        
        Suggestion: isolate hot data on separate cache lines using padding or alignas(64), and prefer thread-local or per-thread state with deferred merge or publish to reduce cache-line contention.

        以inc_b(void*)函数为例,建议区域(Code Suggestion)显示内容如下:

        Code Suggestions 
        Memory: 
        FS1: self=0x400bcc L:59 <-> sum_a(void*) pc=0x4009bc L:33, kind=SL, cacheLine=0x420040, adjacent(+0x20/+0x24, 4B/4B)  
        
        Suggestion: isolate hot data on separate cache lines using padding or alignas(64), and prefer thread-local or per-thread state with deferred merge or publish to reduce cache-line contention.

        字段说明请参见表1

    6. 得出结论。
      1. 源码优化工具检测结果表明:sum_a函数和inc_b函数访问了同一缓存行内两个相邻但不同的4B区域。
      2. 查看源码,sum_a函数绑定到cpu 0,inc_b函数绑定到cpu 1。 对应伪共享冲突代码行分别频繁读写arr[0]和arr[1],而arr是连续的int数组: int arr[32]。
      3. arr[0]和arr[1]共享了同一条缓存行,因此触发了典型的伪共享问题。
  2. 修改伪共享问题。

    修改源码文件,使用padding或alignas(64)隔离。将源码文件中第9行的“int arr[32];”修改为以下内容:

    struct alignas(64) Item {   
         int value;   
         char padding[60]; 
    };

    将源码文件中第32行的“s = arr[0];”和第33行的“arr[0] += 1;”修改为以下内容:

    s = arr[0].value;
    
    arr[0].value += 1;

    将源码文件中第58行的“s = arr[1];”和第59行的“arr[1] += 1;”修改为以下内容:

    s = arr[1].value;
    
    arr[1].value += 1;
  3. 验证是否解决伪共享问题。
    保存修改内容,重新编译运行源码文件,重新执行采集命令。
    ./devopt.sh record -p 3315674 -d 5 -o /home/test

    返回信息如下:

    Saved the record data to /home/test/devopt_3315674_20260513155321.rawdata
    再次进行精细化内存分析。
    ./devopt.sh record -p 3315674 -d 5 -m -i /home/test/devopt_3315674_20260513155321.rawdata

    返回信息如下:

    No false sharing detected

    显示结果表明分析成功,未检测到伪共享问题。

    使用report命令查看分析结果。
    ./devopt.sh report -i /home/test/devopt_3315674_20260513155321.rawdata

    返回的总览界面如下图所示。

    总览界面的表格区域显示sum_a函数和inc_b函数的issue列未显示M,mem_bound数值下降,进入明细界面未显示源码优化建议,表明伪共享问题已解决。