教程: 优化伪共享¶
本文更新于 2018.10.11
本文演示了如何通过Memory Access分析来发现伪共享问题(False Sharing), 示例代码中2个线程访问同一cache line的不同数据导致了此问题.
参考: https://software.intel.com/en-us/vtune-memory-access-tutorial-linux-pdf
工作流:
创建vtune工程并运行分析¶
- 配置要分析的可执行程序和运行参数, 这里参数为源码解压目录下的key_file.txt
- 配置分析类型为Microarchitecture分组下的Memory Access, 并勾选”Analyze dynamic memory objects”, 设置”Minimal dynamic memory object size to track, in byptes”为1
完成后点击开始按钮开始分析.
注解
进行分析前最好通过BIOS设置关闭超线程支持, 否则对性能数据收集有影响. 性能分析工作完成后记得改回来
分析代码¶
从概要信息可知内存访问影响了性能:
下拉概要信息页面, 会看到”Top Memory Objects by Latency”, 这里给出了访问最慢的内存对象:
其中linear_regression_pthread.c的第136行代码如下:
tid_args = (lreg_args *)calloc(sizeof(lreg_args), num_procs);
它动态分配了一块内存, 用于每个线程函数的参数. 此处的内存会被每个线程访问, 比如:
// ADD UP RESULTS
for (i = 0; i < args->num_elems; i++)
{
//Compute SX, SY, SYY, SXX, SXY
args->SX += args->points[i].x;
args->SXX += args->points[i].x*args->points[i].x;
args->SY += args->points[i].y;
args->SYY += args->points[i].y*args->points[i].y;
args->SXY += args->points[i].x*args->points[i].y;
}
结构体lreg_args的定义如下:
typedef struct
{
pthread_t tid;
POINT_T *points;
int num_elems;
long long SX;
long long SY;
long long SXX;
long long SYY;
long long SXY;
} lreg_args;
调试代码可知此结构大小(sizeof)为64字节, 正好占用一个cache line, 然而由于多个lreg_args结构体在堆上动态分配, 无法保证以64字节对齐, 那么数组元素可能会被跨cache line访问, 造成false sharing问题.
优化代码¶
修改lreg_args结构体定义:
typedef struct
{
char pad[80];
pthread_t tid;
POINT_T *points;
int num_elems;
long long SX;
long long SY;
long long SXX;
long long SYY;
long long SXY;
} lreg_args;
修改完成后重新编译分析, 可以看到Memory Bound大幅下降, false sharing消除.