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.mzph.cn/pingmian/67959.shtml

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

相关文章

计算机网络 (56)交互式音频/视频

一、定义与特点 定义&#xff1a;交互式音频/视频是指用户使用互联网和其他人进行实时交互式通信的技术&#xff0c;包括语音、视频图像等多媒体实时通信。 特点&#xff1a; 实时性&#xff1a;音频和视频数据是实时传输和播放的&#xff0c;用户之间可以进行即时的交流。交互…

pdf与ofd的区别详细对比

PDF&#xff08;Portable Document Format&#xff09;和OFD&#xff08;Open Fixed-layout Document&#xff09;是两种常见的电子文档格式&#xff0c;它们在设计理念、技术实现、应用场景等方面存在显著差异。以下是对这两种格式的详细对比分析&#xff0c;涵盖其历史背景、…

【Linux系统】Linux下的图形库 ncurses(简单认识)

基本介绍 在 Linux 环境下&#xff0c;ncurses 是一个非常重要的库&#xff0c;用于编写可以在终端&#xff08;TTY&#xff09;或模拟终端窗口中运行的 字符界面程序。它提供了一套函数&#xff0c;使得开发者可以轻松地操作文本终端的显示&#xff0c;比如移动光标、创建窗口…

基于C#实现多线程启动停止暂停继续

大部分初学者在学习C#上位机编程时&#xff0c;多线程是一个很难逾越的鸿沟&#xff0c;不合理地使用多线程&#xff0c;会导致经常出现各种奇怪的问题&#xff0c;这也是很多初学者不敢使用多线程的原因。但是在实际开发中&#xff0c;多线程是一个不可避免的技术栈&#xff0…

ESP8266 MQTT服务器+阿里云

MQTT私有平台搭建&#xff08;EMQX 阿里云&#xff09; 阿里云服务器 EMQX 搭建私有MQTT平台 1、搜索EMQX开源版本 2、查看各版本EMQX支持的UBUNTU版本 3、查看服务器Ubuntu版本 4、使用APT安装模式 5、按照官网指示安装并启动 6、下载安装MQTTX测试工具 7、设置云服务…

OpenCV相机标定与3D重建(63)校正图像的畸变函数undistort()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 转换图像以补偿镜头畸变。 该函数通过变换图像来补偿径向和切向镜头畸变。 此函数仅仅是 initUndistortRectifyMap&#xff08;使用单位矩阵 R…

Redis使用基础

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

vector详解(C++)

参考&#xff1a;C vector链接&#xff1a;http://www.cplusplus.com/reference/vector/vector/ 1.vector vector 是 C STL 中一种顺序容器&#xff08;sequence container&#xff09;&#xff0c;其底层实现基于动态数组。与普通数组不同的是&#xff0c;vector 可以根据需…

Spring Boot 事件驱动:构建灵活可扩展的应用

在 Spring Boot 应用中&#xff0c;事件发布和监听机制是一种强大的工具&#xff0c;它允许不同的组件之间以松耦合的方式进行通信。这种机制不仅可以提高代码的可维护性和可扩展性&#xff0c;还能帮助我们构建更加灵活、响应式的应用。本文将深入探讨 Spring Boot 的事件发布…

PostgreSQL主从复制配置

本文主要介绍基于pg_basebackup实现主从复制&#xff08;异步流复制&#xff09; MASTER节点安装的方法可以看这篇文章 PostgreSQL YUM安装_yum install postgresql-CSDN博客 关于基本的配置就不作过多的介绍了&#xff0c;直接开始 MASTER节点 首先在master节点创建一个用于…

2025 OWASP十大智能合约漏洞

随着去中心化金融&#xff08;DeFi&#xff09;和区块链技术的不断发展&#xff0c;智能合约安全的重要性愈发凸显。在此背景下&#xff0c;开放网络应用安全项目&#xff08;OWASP&#xff09;发布了备受期待的《2025年智能合约十大漏洞》报告。 这份最新报告反映了不断演变的…

linux下使用脚本实现对进程的内存占用自动化监测

linux系统中常用cat /proc/{pid}/status和pmap -x {pid}来监测某个进程的内存资源占用情况。 其中注意各参数的含义如下&#xff1a; VmSize&#xff1a;表示进程当前虚拟内存大小 VmPeak&#xff1a;表示进程所占用最大虚拟内存大小 VmRSS&#xff1a;表示进程当前占用物理内…

双足机器人开源项目

双足机器人&#xff08;也称为人形机器人或仿人机器人&#xff09;是一个复杂的领域&#xff0c;涉及机械设计、电子工程、控制理论、计算机视觉等多个学科。对于想要探索或开发双足机器人的开发者来说&#xff0c;有许多开源项目可以提供帮助。这些项目通常包括硬件设计文件、…

关于WPF中ComboBox文本查询功能

一种方法是使用事件&#xff08;包括MVVM的绑定&#xff09; <ComboBox TextBoxBase.TextChanged"ComboBox_TextChanged" /> 然而运行时就会发现&#xff0c;这个事件在疯狂的触发&#xff0c;很频繁 在实际应用中&#xff0c;如果关联查询数据库&#xff0…

mysql之表的外键约束

MySQL表的外键约束详细介绍及代码示例 外键约束是数据库中用于维护数据完整性和一致性的重要机制。它确保一个表中的数据与另一个表中的数据相关联&#xff0c;防止无效的数据引用。本文将详细介绍了外键约束的各个方面&#xff0c;并通过具体的代码示例进行演示。 1. 外键约束…

[Qt]系统相关-网络编程-TCP、UDP、HTTP协议

目录 前言 一、UDP网络编程 1.Qt项目文件 2.UDP类 QUdpSocket QNetworkDatagram 3.UDP回显服务器案例 细节 服务器设计 客户端设计 二、TCP网络编程 1.TCP类 QTcpServer QTcpSocket 2.TCP回显服务器案例 细节 服务器设计 客户端设计 三、HTTP客户端 1.HTTP…

【LeetCode】--- MySQL刷题集合

1.组合两个表&#xff08;外连接&#xff09; select p.firstName,p.lastName,a.city,a.state from Person p left join Address a on p.personId a.personId; 以左边表为基准&#xff0c;去连接右边的表。取两表的交集和左表的全集 2.第二高的薪水 &#xff08;子查询、if…

【2024年华为OD机试】(B卷,100分)- 数据分类 (Java JS PythonC/C++)

一、问题描述 题目描述 对一个数据a进行分类,分类方法为: 此数据a(四个字节大小)的四个字节相加对一个给定的值b取模,如果得到的结果小于一个给定的值c,则数据a为有效类型,其类型为取模的值;如果得到的结果大于或者等于c,则数据a为无效类型。 比如一个数据a=0x010…

Linux:常用命令--文件与目录操作

ls命令 功能&#xff1a;&#xff08;list&#xff09;列出当前目录的文件信息 语法&#xff1a;ls [-l -h -a] [参数] 参数&#xff1a;被查看的文件夹&#xff0c;不提供参数&#xff0c;表示查看当前工作目录-l&#xff0c;以列表形式查看每个文件的属性&#xff0c;包含…

Java 8 实战 书籍知识点散记

一、Lambda表达式 1.1 Lambda表达式的一些基本概念 1.2 Lambda表达式的三个部分 // 简化前Comparator<Apple> byWeightnew Comparator<Apple>() {public int compare(Apple a1, Apple a2){return a1.getWeight().compareTo(a2.getWeight());}};//Lambda表达式Comp…