CPU 缓存基础知识

并发编程首先需要简单了解下现代CPU相关知识。通过一些简单的图,简单的代码,来认识CPU以及一些常见的问题。

目录

    • CPU存储与缓存的引入
      • 常见的三级缓存结构
      • 缓存一致性协议
        • MESI协议
        • 缓存行 cache line
      • 通过代码实例认识缓存行的重要性
    • CPU指令的乱序执行
      • 通过代码实例认识到CPU指令乱序执行

CPU存储与缓存的引入

下图描述了存储器金字塔层次结构
在这里插入图片描述

在这里插入图片描述

正因为CPU的计算速度与内存速度的严重不匹配,所以加上了多级缓存,让CPU能执行更多的指令

常见的三级缓存结构

如下图,描述一个有2个CPU且多核心的
在这里插入图片描述

  • L1高速缓存:也叫一级缓存。一般内置在内核旁边,是与CPU结合最为紧密的CPU缓存。一次访问只需要2~4个时钟周期
  • L2高速缓存:也叫二级缓存。空间比L1缓存大,速度比L1缓存略慢。一次访问约需要10多个时钟周期
  • L3高速缓存:也叫三级缓存。部分单CPU多核心的才会有的缓存,介于多核和内存之间。存储空间已达Mb级别,一次访问约需要数十个时钟周期。

当CPU要读取一个数据时,首先从L1缓存查找,命中则返回;若未命中,再从L2缓存中查找,如果还没有则从L3缓存查找(如果有L3缓存的话)。如果还是没有,则从内存中查找,并将读取到的数据逐级放入缓存。如下图所示

在这里插入图片描述

缓存一致性协议

因为现代CPU的架构,所以必然会遇到多个处理器都涉及同一块主内存区域的更改时,这就将导致各自的缓存数据不一致;所以需要采取一定的规范来解决这个问题。如下图所示:

在这里插入图片描述

  • 总线锁是把CPU和内存的通信给锁住了;使得在锁定期间,其它处理器不能操作内存的其它数据,这样开销较大

  • 缓存锁不需锁定总线,只需要"锁定"被缓存的共享对象(实际为:缓存行)即可;接受到lock指令,通过缓存一致性协议,维护本处理器内部缓存和其它处理器缓存的一致性。相比总线锁,会提高CPU利用率。

MESI协议

MESI协议是基于Invalidate的高速缓存一致性协议,并且是支持回写高速缓存的最常用协议之一。

MESI 是指4种状态的首字母。每个缓存行(Cache Line)有4个状态,可用2个bit表示,它们分别是:

状态描述监听任务
M 修改 (Modified)该Cache line有效,数据被修改了,和内存中的数据不一致,数据只存在于本Cache中。缓存行必须时刻监听所有试图读该缓存行相对就主存的操作,这种操作必须在缓存将该缓存行写回主存并将状态变成S(共享)状态之前被延迟执行。
E 独享、互斥 (Exclusive)该Cache line有效,数据和内存中的数据一致,数据只存在于本Cache中。缓存行也必须监听其它缓存读主存中该缓存行的操作,一旦有这种操作,该缓存行需要变成S(共享)状态。
S 共享 (Shared)该Cache line有效,数据和内存中的数据一致,数据存在于很多Cache中。缓存行也必须监听其它缓存使该缓存行无效或者独享该缓存行的请求,并将该缓存行变成无效(Invalid)。
I 无效 (Invalid)该Cache line无效。

当某个cpu修改缓存行数据时,其它的cpu通过监听机制获悉共享缓存行的数据被修改,会使其共享缓存行失效。本cpu会将修改后的缓存行写回到主内存中。此时其它的cpu如果需要此缓存行共享数据,则从主内存中重新加载,并放入缓存,以此完成了缓存一致性。

缓存行 cache line
  • 程序局部性原理(这里解释为:访问内存或缓存的某个位置,顺带的把紧邻的位置一起读取出来)

    1. 缓存行越大,局部性空间效率越高,但读取时间慢
    2. 缓存行越小,局部性空间效率越低,但读取时间快

常见的缓存行一般64字节

通过代码实例认识缓存行的重要性

import java.util.concurrent.CountDownLatch;public class Main {private static class T {public volatile long x;}public static T[] arr = new T[2];static {arr[0] = new T();arr[1] = new T();}// 一亿次static int FOR_COUNT = 100_000_000;public static void main(String[] args) throws Exception{CountDownLatch latch = new CountDownLatch(2);Thread t1 = new Thread(()->{for (int i = 0; i < FOR_COUNT; i ++){arr[0].x ++;}latch.countDown();});Thread t2 = new Thread(()->{for (int i = 0; i < FOR_COUNT; i ++){arr[1].x ++;}latch.countDown();});final long start = System.nanoTime();t1.start();t2.start();latch.await();System.out.println((System.nanoTime() - start) /1_000_000 + " ms");}
}

在自己电脑上(2.3 GHz 双核Intel Core i5)跑如上代码要接近3000豪秒了

如上程序是:两个线程分别处理一个对象数组的不同变量,而这个变量是个volatile long x; ,因为数组的2个变量是在同一个缓存行中的,每次修改都修改了同一个缓存行,要有缓存同步操作,所以比较慢。

再看如下程序,只需修改T的定义

private static class T {public volatile long p1, p2, p3, p4, p5, p6, p7;public volatile long x;public volatile long p8, p9, p10, p11, p12, p13, p14;
}

在这里插入图片描述

可以降低到1000毫秒左右,因为数组的两个元素:arr[0].xarr[1].x 不会在一个缓存行中;这样修改用的各自的缓存行,互不影响:

56字节
x(8字节)
56字节
56字节
x(8字节)
56字节

当然使用@Contended(运行加上`-XX:-RestrictContended)是最方便的
在这里插入图片描述

CPU指令的乱序执行

cpu中为了能够让指令的执行尽可能地并行起来,从而发明了流水线技术。但是如果两条指令的前后存在依赖关系,比如数据依赖,控制依赖等,此时后一条语句就必需等到前一条指令完成后,才能开始。cpu为了提高流水线的运行效率,会做出比如:

  1. 对无依赖的前后指令做适当的乱序和调度;
  2. 对控制依赖的指令做分支预测;
  3. 对读取内存等的耗时操作,做提前预读;

这些都可能会导致指令乱序


附: 指令流水线是为提高处理器执行指令的效率,把一条指令的操作分成多个细小的步骤(取指、译码、执行、访问主存、写回),每个步骤由专门的电路完成的方式。举个例子:例如一条指令要执行要经过3个阶段:取指令、译码、执行,每个阶段都要花费一个机器周期,如果没有采用流水线技术,那么这条指令执行需要3个机器周期;如果采用了指令流水线技术,那么当这条指令完成取指后进入译码的同时,下一条指令就可以进行取指了,这样就提高了指令的执行效率。

通过代码实例认识到CPU指令乱序执行

google blog: Memory Reordering Caught in the Act

代码如下:

public class Main {static int x, y, a, b;public static void main(String[] args) throws Exception{int i = 0;while (true) {x = 0;y = 0;b = 0;a = 0;Thread A = new Thread(new Runnable() {@Overridepublic void run() {a = 1;x = b;}});Thread B = new Thread(new Runnable() {@Overridepublic void run() {b = 1;y = a;}});A.start();B.start();A.join();B.join();i++;if(x == 0 && y == 0){System.err.println(i + " " + x + " " + y);break;}}System.out.println("main end");}
}

指令有序的话,理论上不会出现x,y都等于0的情况;如果出现,则可以说明指令乱序

如上程序运行一段时间后(需要耐心等待一下),退出输出如下:

在这里插入图片描述

后续在认识线程安全(可见性,原子性,顺序性)的时候还将复习到此知识。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/6492.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【博客之星】年度总结:在云影与墨香中探寻成长的足迹

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、年度回顾 1、创作历程 2、个人成长 3、个人生活与博客事业 二、技术总结 1、赛道选择 2、技术工具 3、实战项目 三、前景与展望 1、云原生未来…

2024 自主创业事业小结和2025展望

一 2024创业事业小结&#xff1a; 1.1 2024 自主创业项目小结&#xff1a; 2024年我们小团队主要做了两大类的项目&#xff1a; 1&#xff0c;工业类 在工业领域的项目&#xff0c;我们做了3个落地的视觉集成项目。 1.1 旋转角度的测量&#xff1a; 由于是外包项目&#…

Redis使用基础

1 redis介绍 Redis&#xff08;Remote Dictionary Server )&#xff0c;即远程字典服务 ! 是完全开源的&#xff0c;遵守 BSD 协议&#xff0c;是一个高性能的 key-value 数据库。 使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并…

激光雷达和相机早期融合

通过外参和内参的标定将激光雷达的点云投影到图像上。 • 传感器标定 首先需要对激光雷达和相机&#xff08;用于获取 2D 图像&#xff09;进行外参和内参标定。这是为了确定激光雷达坐标系和相机坐标系之间的转换关系&#xff0c;包括旋转和平移。通常采用棋盘格等标定工具&…

HMV Challenges 022 Writeup

题目地址&#xff1a;https://hackmyvm.eu/challenges/challenge.php?c022 首先猜测是否为图片隐写&#xff0c;无果 盲猜图片上的小鸟是某种带符号的隐写 去这个网站找找看&#xff1a;https://www.dcode.fr/chiffres-symboles 找到了 参照原图片鸟儿的姿态选择并排放 所…

FPGA与ASIC:深度解析与职业选择

IC&#xff08;集成电路&#xff09;行业涵盖广泛&#xff0c;涉及数字、模拟等不同研究方向&#xff0c;以及设计、制造、封测等不同产业环节。其中&#xff0c;FPGA&#xff08;现场可编程门阵列&#xff09;和ASIC&#xff08;专用集成电路&#xff09;是两种重要的芯片类型…

【前端】Hexo 建站指南

文章目录 前言生成站点本地测试部署云端参考 前言 更好的阅读体验&#xff1a;https://blog.dwj601.cn/FrontEnd/Hexo/build-your-own-website-with-hexo/ 笔记记多了&#xff0c;想要分享给同学们一起交流进步&#xff0c;该怎么办&#xff1f;想要搭建一个属于自己的知识库…

抛弃node和vscode,如何用记事本开发出一个完整的vue前端项目

写这篇文章的初衷并不是要大家真的不用node和vscode&#xff0c;说实话前端发展成今天这样&#xff0c;在实际开发中确实离不开node和vscode这类工具了&#xff0c;但往往工具用多了我们自己也成了一个工具人&#xff01; 这篇文章的缘由 最近在开发wordpress插件的时候&…

Gin 学习笔记

教程地址&#xff1a;https://www.bilibili.com/video/BV1FV4y1C72M?spm_id_from333.788.videopod.sections&vd_source707ec8983cc32e6e065d5496a7f79ee6 01-项目搭建 各常用目录的说明&#xff1a; https://github.com/golang-standards/project-layout/blob/master/REA…

麒麟操作系统服务架构保姆级教程(十四)iptables防火墙四表五链和防火墙应用案例

如果你想拥有你从未拥有过的东西&#xff0c;那么你必须去做你从未做过的事情 防火墙在运维工作中有着不可或缺的重要性。首先&#xff0c;它是保障网络安全的关键防线&#xff0c;通过设置访问控制规则&#xff0c;可精准过滤非法网络流量&#xff0c;有效阻挡外部黑客攻击、恶…

双目立体校正和Q矩阵

立体校正 对两个摄像机的图像平面重投影&#xff0c;使二者位于同一平面&#xff0c;而且左右图像的行对准。 Bouguet 该算法需要用到双目标定后外参(R&#xff0c;T) 从上图中可以看出&#xff0c;该算法主要分为两步&#xff1a; 使成像平面共面 这个办法很直观&#xff…

【C++】string类模拟实现

目录 &#x1f495;1.模拟string类构造函数 &#x1f495;2.模拟构造函数实现 &#x1f495;3.拷贝构造函数模拟实现 &#x1f495;4.析构函数模拟实现 &#x1f495;5.size函数&#xff0c;capacity函数模拟实现 &#x1f495;6.begin函数,end函数&#xff0c;模拟实…

微调Qwen2:7B模型,加入未知信息语料

对于QWen2这样的模型,在微调的时候,语料的投喂格式满足ChatML这样的格式!!! OpenAI - ChatML: 下面是ChatML格式的介绍: https://github.com/openai/openai-python/blob/release-v0.28.0/chatml.mdhttps://github.com/openai/openai-python/blob/release-v0.28.0/chat…

.Net Core微服务入门全纪录(四)——Ocelot-API网关(上)

系列文章目录 1、.Net Core微服务入门系列&#xff08;一&#xff09;——项目搭建 2、.Net Core微服务入门全纪录&#xff08;二&#xff09;——Consul-服务注册与发现&#xff08;上&#xff09; 3、.Net Core微服务入门全纪录&#xff08;三&#xff09;——Consul-服务注…

HTML根元素<html>的语言属性lang:<html lang=“en“>

诸神缄默不语-个人CSDN博文目录 在编写HTML页面时&#xff0c;通常会看到<html lang"en">这行代码&#xff0c;特别是在网页的开头部分&#xff0c;就在<!DOCTYPE html>后面。许多开发者可能对这个属性的含义不太了解&#xff0c;它到底有什么作用&…

小样本学习中的Prototypical Network(原型网络)详解

Few-shot Learning ,即“小样本学习”,是一种机器学习方法,旨在通过极少量样本训练模型,使其能够快速适应新任务或新类别。这种方法在数据稀缺的场景中非常有用。 Prototypical Network(原型网络)是小样本学习中的经典方法之一,特别适用于分类任务。它的核心思想是通过学…

mock可视化生成前端代码

介绍&#xff1a;mock是我们前后端分离的必要一环、ts、axios编写起来也很麻烦。我们就可以使用以下插件&#xff0c;来解决我们的问题。目前支持vite和webpack。&#xff08;配置超级简单&#xff01;&#xff09; 欢迎小伙伴们提issues、我们共建。提升我们的开发体验。 vi…

(回溯分割)leetcode93 复原IP地址

#include<iostream> #include<vector> #include<string> #include<algorithm> using namespace std; //卡尔的图不是按照程序执行过程而是直接画程序会执行的过程 // 实际执行是&#xff1a;n个字符&#xff0c;递推n1后&#xff08;叶子节点&#xff…

Springboot3 自动装配流程与核心文件:imports文件

注&#xff1a;本文以spring-boot v3.4.1源码为基础&#xff0c;梳理spring-boot应用启动流程、分析自动装配的原理 如果对spring-boot2自动装配有兴趣&#xff0c;可以看看我另一篇文章&#xff1a; Springboot2 自动装配之spring-autoconfigure-metadata.properties和spring…

JVM面试题解,垃圾回收之“分代回收理论”剖析

一、什么是分代回收 我们会把堆内存中的对象间隔一段时间做一次GC&#xff08;即垃圾回收&#xff09;&#xff0c;但是堆内存很大一块&#xff0c;内存布局分为新生代和老年代、其对象的特点不一样&#xff0c;所以回收的策略也应该各不相同 对于“刚出生”的新对象&#xf…