服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

CPU Cache伪共享问题

日期: 来源:程序喵大人收集编辑:程序喵大人

先看下这两段代码:

代码段1:

const int row = 10240;const int col = 10240;int matrix[row][col];int TestRow() {  //按行遍历  int sum_row = 0;  for (int r = 0; r < row; r++) {    for (int c = 0; c < col; c++) {      sum_row += matrix[r][c];    }  }  return sum_row;}

代码段2:

int TestCol() {  //按列遍历  int sum_col = 0;  for (int c = 0; c < col; c++) {    for (int r = 0; r < row; r++) {      sum_col += matrix[r][c];    }  }  return sum_col;}

两段代码的目的相同,都是为了计算矩阵中所有元素的总和。

但有些区别:一个是按行遍历元素做计算,一个是按列遍历元素做计算。

它俩的运行速度有什么区别吗?

如图:

图中可以看到,行遍历的代码速度比列遍历的代码速度快很多。

为什么按行遍历的代码比按列遍历的代码速度快?这里就是CPU Cache在起作用。

什么是CPU Cache?

可以先看下这个存储器相关的金字塔图:

从下到上,空间虽然越来越小,但是处理速度越来越快,相应的,设备价格也越来越贵。

图中的寄存器和主存估计大家都知道,那中间的L1 、L2、L3是什么?它们起到了什么作用?

它们就是CPU 的Cache,如下图:

可以理解为CPU Cache就是CPU与主存之间的桥梁。

当CPU想要访问主存中的元素时,会先查看Cache中是否存在,如果存在(称为Cache Hit),直接从Cache中获取,如果不存在(称为Cache Miss),才会从主存中获取。Cache的处理速度比主存快得多。

所以,如果每次访问数据时,都能直接从Cache中获取,整个程序的性能肯定会更高。

那,如何提高CPU Cache的命中率?

这里我不多介绍,感兴趣的直接移步到我这篇文章:https://mp.weixin.qq.com/s/iKWQZxn6XYKU9KnlBRynfg

但CPU Cache这里还有个小问题,看下这两段代码:

代码段1:

struct Point {  std::atomic<int> x;  // char a[128];  std::atomic<int> y;};void Test() {  Point point;  std::thread t1(      [](Point *point) {        for (int i = 0; i < 100000000; ++i) {          point->x += 1;        }      },      &point);  std::thread t2(      [](Point *point) {        for (int i = 0; i < 100000000; ++i) {          point->y += 1;        }      },      &point);  t1.join();  t2.join();}

代码段2:

struct Point {  std::atomic<int> x;  char a[128];  std::atomic<int> y;};void Test() {  Point point;  std::thread t1(      [](Point *point) {        for (int i = 0; i < 100000000; ++i) {          point->x += 1;        }      },      &point);  std::thread t2(      [](Point *point) {        for (int i = 0; i < 100000000; ++i) {          point->y += 1;        }      },      &point);  t1.join();  t2.join();}

两端代码的核心逻辑都是对Point结构体中的x和y不停+1。只有一点区别就是在中间塞了128字节的数组。

它们的执行速度却相差很大。

带128的比不带128的代码,执行速度快很多。

为什么?

看过我上面文章的同学应该就知道,每个CPU都有自己的L1和L2 Cache,而Cache line的大小一般是64字节,如果x和y之间没有128字节的填充,它俩就会在同一个Cache line上。

代码中开了两个线程,两个线程大概率会运行在不同的CPU上,每个CPU有自己的Cache。

当CPU1操作x时,会把y装载到Cache中,其他CPU对应的的Cache line失效。

然后CPU2加载y,会触发Cache Miss,它后面又把x装载到了自己的Cache中,其他CPU对应的Cache line失效。

然后CPU1操作x时,又触发Cache Miss。

它俩就会是大体这个流程:

频繁的触发Cache Miss,导致程序的性能相当差。

而如果x和y中间加了128字节的填充,x和y不在同一个Cache line上,不同CPU之前不会影响,它俩都会频繁的命中自己的Cache,整个程序性能就会很高,这就是传说中的False Sharing问题。

所以我们写代码时,可以基于此做深一层思考,如果我们写单线程程序,最好保证访问的数据能够相邻,在一个Cache line上,可以尽可能的命中Cache。

如果写多线程程序,最好保证访问的数据有间隔,让它们不在一个Cache line上,减少False Sharing的频率。

上述内容源于前一段的技术分享,完整PPT在 一个优质的C++学习圈 里,来一起钻研C++吧。

相关阅读

  • Google为Chromium引入Rust?

  • 今天的文章外部link比较多我特意整理了link的汇总需要的朋友可以在后台回复:“rust ”即可收到自动回复,更加方便观看-----------------------------------------------------
  • DoNot Team (APT-C-35) 最新活动的分析

  • 关键词DoNot Team、APT-C-35、APT、Powershell、VBA1. 执行摘要DoNot组织,也被称为APT-C-35,是一个至少从2016年就开始活动的高级可持续威胁(APT)组织。该组织有着高度复杂的技
  • 雷神众测漏洞周报2023.02.06-2023.02.12

  • 以下内容,均摘自于互联网,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,雷神众测以及文章作者不承担任何责任。雷神众测拥有该文章的修
  • 雷神众测漏洞周报2023.02.13-2023.02.19

  • 以下内容,均摘自于互联网,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,雷神众测以及文章作者不承担任何责任。雷神众测拥有该文章的修
  • 微软6月补丁日到来:修复1零日共55个漏洞

  • 今天是微软2022年6月的周二补丁日,微软今天修复了1个零日漏洞和总共 55个漏洞,包括对Windows MSDT“Follina”零日漏洞和新的英特尔MMIO漏洞的修复。微软通过今天的更新修复了
  • 微软2月补丁日到来:修复3零日共77个漏洞

  • 今天是微软 2023 年 2 月的周二补丁日,安全更新修复了3个正在被积极利用的零日漏洞和总共 77 个漏洞。9个漏洞被归类为“严重”,因为它们允许在易受攻击的设备上远程执行代码

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

  • CPU Cache伪共享问题

  • 先看下这两段代码:代码段1:const int row = 10240;const int col = 10240;int matrix[row][col];int TestRow() { //按行遍历 int sum_row = 0; for (int r = 0; r < row;
  • Rust学习资料

  • 最近在研究Rust,目前大多数项目都可以使用Rust开发,但是涉及到和其他语言交互,比如用Rust开发一个SDK,一般还是需要导出C接口。那如何将Rust导出C接口?Rust的FFI就是专门做这件事
  • 《计算机网络:自顶向下方法》全新第8版

  • 20多年持续打磨,国际经典教材计算机网络:自顶向下方法 文末送书!Kurose和他的自顶向下方法就在最近,Kurose和Ross教授合著的《计算机网络:自顶向下方法》中文版也刚刚升级到了第8
  • 百度工程师带你探秘C++内存管理(ptmalloc篇)

  • 作者 | daydreamer前篇《探秘C++内存管理(理论篇)》主要介绍了Linux C++程序内存管理的理论基础,本文作为系列文章《探秘C++内存管理》的第二篇,将会探讨经典内存管理器ptmalloc
  • Google为Chromium引入Rust?

  • 今天的文章外部link比较多我特意整理了link的汇总需要的朋友可以在后台回复:“rust ”即可收到自动回复,更加方便观看-----------------------------------------------------
  • constexpr

  • 前面介绍了模板这种编译期动作,关于编译期动作,有必要介绍下constexpr。在这之前有必要简单提一下constexpr与const的关系,两者字面上都表达常量的意思。主要的区别是:const修饰