CPU高速缓存SRAM命中问题的总结与实验

2023-11-15

1,SRAM高速缓存的结构

获取本机CPU的SRAM缓存信息

我使用的是一个叫cpuinfo_x86的小程序,可以获取x86架构的cpu相关信息。下载地址:http://osxbook.com/book/bonus/misc/cpuinfo_x86/cpuinfo_x86.c
gcc编译后运行,以下是当前测试环境的SRAM信息:

L1 Instruction Cache //L1指令缓存
Size : 32K
Line Size : 64B
Sharing : shared between 2 processor threads
Sets : 64
Partitions : 1
Associativity : 8

L1 Data Cache //L1数据缓存
Size : 32K
Line Size : 64B
Sharing : shared between 2 processor threads
Sets : 64
Partitions : 1
Associativity : 8

L2 Unified Cache //L2指令数据缓存
Size : 256K
Line Size : 64B
Sharing : shared between 2 processor threads
Sets : 512
Partitions : 1
Associativity : 8

L3 Unified Cache //L3指令数据缓存
Size : 3M
Line Size : 64B
Sharing : shared between 16 processor threads
Sets : 4096
Partitions : 1
Associativity : 12

Size是该缓存所有数据块的合计大小,Sets是总组数,Associativity是每组的行数,LineSize是每行的数据块大小。指令和数据缓存只用来缓存指令或内存数据,有利于CPU并行取指和访存提速,Unified Cache可以同时缓存两者,L1L2为每核心独享,L3为多核共享,整体结构大概如下图:
在这里插入图片描述
(图1)

内存数据在缓存中的定位与传输

假设有指令要从L1中读一个字节,内存地址为0000000000000000000000000000000000000000000000000000000000000000
由于L1缓存块大小64B, 64 = 2 6 64=2^6 64=26,所以最后六位用于计算数据块中的字节偏移,称为 b 位 \color{orange}b位 b
0000000000000000000000000000000000000000000000000000000000 000000 0000000000000000000000000000000000000000000000000000000000\color{orange}000000 0000000000000000000000000000000000000000000000000000000000000000
组数量也为64,那么可得出组的index为接下来的6位,称为 s 位 \color{red}s位 s
0000000000000000000000000000000000000000000000000000 000000 000000 0000000000000000000000000000000000000000000000000000\color{red}000000\color{orange}000000 0000000000000000000000000000000000000000000000000000000000000000
剩下的位用来寻找该组中存有该数据的行,称为tag标签, t 位 \color{blue}t位 t:
0000000000000000000000000000000000000000000000000000 000000 000000 \color{blue}0000000000000000000000000000000000000000000000000000\color{red}000000\color{orange}000000 0000000000000000000000000000000000000000000000000000000000000000
缓存的控制逻辑硬件解析内存地址后会先用s位定位组,然后并行的用t位定位行。如果找到了组和行既是缓存命中,会用b位来寻找数据位置(或数据起始位置)并返回数据。否则就是缓存不命中,会向下一级缓存读取数据。
每行都有一个数据块,数据块的基本单位为字节,例如L1的数据块为64字节,缓存着内存中0000000000000000000000000000000000000000000000000000000000至 0000000000000000000000000000000000000000000000000000111111的数据。每字节index与b位对应,例如上面要寻找的数据既是在下图行1中的数据块的字节0中:
在这里插入图片描述
(图2)

通过这种机制将内存数据分布在SRAM缓存中有点类似于编程中的散列表。内存中每连续n个字节相当于要存储的value,内存地址的s位相当于key,缓存的组相当于桶,每个行相当于node。与散列表不同的是node数量有限制,如果超出数量限制,将会进行不同策略的行替换,另外每个node之间也没有链接关系,所有行用t位进行标记,由硬件进行并行搜索。
s,b,t位由缓存的结构决定,例如测试环境的L3缓存与L2,L1缓存对比:
L 1 : 0000000000000000000000000000000000000000000000000000 000000 000000 L1:\color{blue}0000000000000000000000000000000000000000000000000000\color{red}000000\color{orange}000000 L1:0000000000000000000000000000000000000000000000000000000000000000
L 2 : 0000000000000000000000000000000000000000000000000 000000000 000000 L2:\color{blue}0000000000000000000000000000000000000000000000000\color{red}000000000\color{orange}000000 L2:0000000000000000000000000000000000000000000000000000000000000000
L 3 : 0000000000000000000000000000000000000000000000 000000000000 000000 L3:\color{blue}0000000000000000000000000000000000000000000000\color{red}000000000000\color{orange}000000 L3:0000000000000000000000000000000000000000000000000000000000000000
这种设计使L1,L2,L3每级缓存之间的数据传输有以下的映射关系(组index由0改为1起始):
这里写图片描述 (图3)

由于内存很大,缓存相对很小,这种映射关系对于正常编程中的数据访问是合理的,但是由于映射关系固定,从L1的角度来看,整个内存地址中有1/64的地址段的数据(对于一个4GB内存来讲既是有64MB的数据)最终都是要缓存到它的某一个组中,如果大量反复访问该范围数据(64MB=1048576个数据块)会造成该组中大量的行替换,增加缓存不命中率,由于行替换会造成访存指令等待,进而导致CPU数据吞吐率降低。

2,缓存读不命中测试

缓存不命中有冷不命中,容量不命中与冲突不命中。缓存在空的状态下,任何读写操作都会造成冷不命中。当工作集(例如循环中要访问的内存地址)整体大小超出该缓存的最大容量时导致的不命中称为容量不命中。
冲突不命中与图3中的映射关系有关,由于内存数据向上一级传输的映射关系是固定的,反复访问内存中的某个子集内的数据会造成大量的不命中,例如根据测试环境,在Unity内进行以下测试:

using System.Collections;
using UnityEngine;

public class TestScript : MonoBehaviour
{

    int[] array1 = new int[10000000];

    // Use this for initialization
    void Start()
    {
        int pTime;
        int cTime;
        int sum = 0;

        Refresh();
        pTime = System.Environment.TickCount;
        sum = Sum1(sum);
        cTime = System.Environment.TickCount;
        Debug.Log("sum=" + sum);
        Debug.Log("以步长8192循环:Time:" + (cTime - pTime));

        Refresh();
        pTime = System.Environment.TickCount;
        sum = 0;
        sum = Sum2(sum);
        cTime = System.Environment.TickCount;
        Debug.Log("sum=" + sum);
        Debug.Log("以步长1循环:Time:" + (cTime - pTime));

        Refresh();
        pTime = System.Environment.TickCount;
        sum = 0;
        sum = Sum3(sum);
        cTime = System.Environment.TickCount;
        Debug.Log("sum=" + sum);
        Debug.Log("以步长8191循环:Time:" + (cTime - pTime));

        Refresh();
        pTime = System.Environment.TickCount;
        sum = 0;
        sum = Sum3(sum);
        cTime = System.Environment.TickCount;
        Debug.Log("sum=" + sum);
        Debug.Log("以步长8193循环:Time:" + (cTime - pTime));
    }

    void Refresh() {
        for (int i = 0; i< array1.Length; i++)
        {
            array1[i] =UnityEngine.Random.Range(1, 100000);
        }
    }

    int Sum1(int sum){
        for (int i = 0,j = 1; j < 5000000;i = 8192 * (j % 1000),j = j + 1){
            sum += array1[i];
        }
        return sum;
    }

    int Sum2(int sum)
    {
        for (int i = 0, j = 1; j < 5000000; i = 1 * (j % 8192), j = j + 1)
        {
            sum += array1[i];
        }
        return sum;
    }

    int Sum3(int sum)
    {
        for (int i = 0, j = 1; j < 5000000; i = 8191 * (j % 1000), j = j + 1)
        {
            sum += array1[i];
        }
        return sum;
    }

    int Sum4(int sum)
    {
        for (int i = 0, j = 1; j < 5000000; i = 8193 * (j % 1000), j = j + 1)
        {
            sum += array1[i];
        }
        return sum;
    }
}

对一个一千万长的int数组(38.15MB)根据不同的步长进行50万次循环,测试结果:
以步长8192循环:Time:330
以步长1循环:Time:84
以步长8191循环:Time:95
以步长8193循环:Time:85

可以看出8192对于测试机来讲是一个神奇的数字,其原因可以从该机CPU的缓存结构中找出。int长度为4个字节32位,8192*4B=32768B=32KB,32KB正好是L1缓存的大小,也既是循环中的所有数据最终都要缓存到L1的组0,CPU对L1的所有访存指令都会造成不命中(除非L1在第一次缓存满八行后只对组中同一行进行行替换,那么在1000次循环中会有7次命中),同一个循环中L2与L3相对L1不命中率会稍微低一些,但是数据从内存到L3的时间会更慢,每次不命中造成的时间惩罚相对L1会更高,对整体的时间惩罚也有相当大的影响。

以上研究的都是读不命中的问题,写不命中时有两种策略,写分配与非写分配,写分配 的行为与读类似,会加载下级缓存数据上来直到L1后修改,非写分配 不加载数据而是不断尝试更改下级缓存中的数据。修改缓存数据后对内存数据的更新又分为写回与直写两种,写回的话每行要维护一个修改位,当该行被替换时如果发现有被修改过会对下一级缓存进行更改。直写是修改数据后立刻更新下一级缓存。这些策略是由硬件决定的。大量连续的直写对总线流量(SRAM<—>DRAM)有影响。写分配比较符合局部性的思想,例如对于小步长的循环,一次写不命中后加载数据到L1会避免之后再次的写不命中。

总之,在应用层面来讲,在循环中以步长为1来对数组进行遍历是最快的,而且最新的CPU还有一个prfetching预取机制(以上测试中的CPU没有),既是在循环前尽量提前将内存中的数组数据向上级缓存传输,在循环过程中提前对缓存中的数据进行更新,这是一个针对步长为1的循环的特殊硬件优化,可以规避容量不命中以及其他一些case。另外根据不同CPU对某个 2 n 2^n 2n为步长对一个超大的数组进行大量的循环是危险的,有可能会造成大量冲突不命中。

————————————————————————————————————
参考:
深入理解计算机系统—Randal E.Bryant, David R.O’Hallaron

维护日志:
2020-2-2:精简,重构

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CPU高速缓存SRAM命中问题的总结与实验 的相关文章

  • 浏览器缓存机制及其分类

    聚沙成塔 每天进步一点点 专栏简介 强缓存 Cache Control 和 Expires 协商缓存 ETag 和 Last Modified 写在最后 专栏简介 前端入门之旅 探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦
  • redis缓存击穿、缓存穿透、缓存雪崩、缓存一致性解决方案的代码实现

    1 0 缓存击穿 概念 一些redis的key过期 同时大量数据请求过期的key或者redis不存在的key 导致大量请求打到数据库 导致数据库瘫痪 解决方案 1 设置热点数据永不过期 2 对热点数据加锁 分布式锁 代码实现 初始化项目 商
  • php中redis memcache等缓存的应用

    欢迎加入 新群号码 99640845 在web开发的过程中缓存是必不可少的工具 无论是mamcache还是redis我想大家都很有所涉略 我分享一下我个人在工作中的一些使用后的想法 缓冲应用设计 对于缓存的使用我想大家应该都会使用 简单的k
  • 更改:为硬件保留的内存

    电脑 联想thinkbook16P 系统 win11 内存 16G 更改前 2 3G 更改后 827MB 一 关机 不同的型号电脑进入boss模式的按键不同 我的是按F1 自己去找进入boss模式的方式 二 进入boss模式 进入boss模
  • Spring的三级缓存解决循环依赖

    一 什么是Spring三级缓存 第一级缓存 也叫单例池 存放已经经历了完整生命周期的Bean对象 第二级缓存 存放早期暴露出来的Bean对象 实例化以后 就把对象放到这个Map中 Bean可能只经过实例化 属性还未填充 第三级缓存 存放早期
  • Redis事务

    7 Redis事务 7 1 背景 假如你给你朋友转账 此时你的账户会减少1bw 你朋友的账户会多1bw 此时如果你转账失败 但是你朋友的账户也多了1bw 此时这对于银行来说 这就是事故 说明你的程序存在很大漏洞 不能保证数据的原子性 此时就
  • Go内存管理及性能观测工具

    内存管理 TCMalloc Golang内存分配算法主要源自Google的TCMalloc算法 TCMalloc将内存分成三层最外层Thread Cache 中间层Central Cache 最里层Page Heap Thread Cach
  • 持久化RDB/AOF-Redis(三)

    上篇文章说了数据持久化 这里再学习一个命令 数据结构 Redis 二 https blog csdn net ke1ying article details 131118016 一 查询所有key scan 0 match zhuge co
  • redis篇

    一 数据库分类 1 关系型数据库SQL Oracle 不开源收费 高帅富 SQL Server 不开源收费 微软自家的产品 DB2 不开源收费 IBM 的产品 Sybase 不开源收费 微软的小基友 关系破裂后家境惨淡 MySQL 大家都在
  • 从一道题目学习Nunjucks模板

    Nunjucks简介 Nunjucks 是一个功能丰富 强大的 JavaScript 专用模板引擎 Nunjucks 提供丰富的语言特性和块继承 自动转移 宏和异步控制等等 重点要关注的是 Nunjucks 模板引擎的代码在沙箱环境中运行
  • java与redis连接过程中遇到问题

    java与redis连接过程中遇到问题 文章目录 java与redis连接过程中遇到问题 前言 一 redis是什么 特征 二 命令 1 redis通用命令 String类型常见命令 Hash常用命令 List常见命令 Set常见命令 三
  • redis 配置与优化

    目录 一 关系数据库和非关系型数据库 二 关系型数据库和非关系型数据库区别 三 非关系型数据库产生背景 四 redis 1 概念 2 redis的优点 3 redis为什么这么快 五 redis安装与配置 一 关系数据库和非关系型数据库 关
  • Redis 7.0 核心技术、实战应用、面试题

    Redis 7 0 核心技术与实战应用 Redis 入门概述 01 Redis 是什么 Redis REmote Dictionary Server 远程字典服务器 官网介绍 https redis io docs about 官网定义 R
  • Redis数据类型

    文章目录 Redis介绍 RedisObject的结构 1 type 2 enconding 3 lru 4 refcount 5 prt Redis源码结构
  • Redis交互速度慢,频繁处理时经常报错 RedisSystemException: RedisException: Connection closed

    Redis交互速度很慢 达到几十到一百毫秒一次 且压力测试下经常报错 org springframework data redis RedisSystemException Redis exception nested exception
  • 华为OD机试真题-文件缓存系统-2023年OD统一考试(C卷)

    题目描述 请设计一个文件缓存系统 该文件缓存系统可以指定缓存的最大值 单位为字节 文件缓存系统有两种操作 存储文件 put 和读取文件 get 操作命令为put fileName fileSize或者get fileName 存储文件是把文
  • SpringBoot中项目启动及定时任务缓存数据库常用数据至内存变量并转换后高频调用

    场景 定时任务中需要获取数据库中数据进行数据转换成需要的格式并进行后续的业务处理 数据库中的数据更新频率不高 可将数据库中数据在项目启动后读取一遍数据 然后再通过定时任务定时查询数据库更新数据 实现数据库缓存的方式有多种 比如以下 Spri
  • 【Redis】Redis 红锁

    1 概述 上一篇文章 redis Redis 分布式锁 redis session Redlock 红锁 Zookeeper锁 本章节主要讲解redis中的红锁 假设我们有个客户端要获取锁 然后向master去获取锁 然后master会把锁
  • Redis生产环境最佳实践

    欢迎关注公众号 通过文章导读关注 11来了 及时收到 AI 前沿项目工具及新技术 的推送 发送 资料 可领取 深入理解 Redis 系列文章结合电商场景讲解 Redis 使用场景 中间件系列笔记 和 编程高频电子书 文章导读地址 点击查看文
  • 淘宝商品类目接口API:获取淘宝商品分类类目信息

    cat get 获得淘宝分类详情 响应参数 名称 类型 必须 示例值 描述 info Mix 0 cid 16 parent cid 0 name 其他女装 is parent true status normal sort order 0

随机推荐

  • 软件测试知识(二)

    软件开发阶段一般又划分成需求分析 概要设计 详细设计 编码与单元测试 组装与系统测试以及安装与验收等6个阶段 系统测试是将软件系统与硬件 外设和网络等其他因素结合 对整个软件系统进行测试 常见的系统测试主要有恢复测试 安全性测试 强度测试
  • 求解三位正整数各位之和

    描述 从键盘上输入一个三位整数 分别求出其的个位 十位和百位数字 并计算三位数字之和 输出格式 使用 format格式输出 请参阅输入输出示例 n input l list n a 0 for i in l a eval i a print
  • Centos5.8 x86_64下安装DRBD+Heartbeat+NFS

    实验环境 vmware workstation os Centos5 8 x86 64 编辑两台虚拟机分别新增一块网卡作为心跳检测 新增一块4G的硬盘 大小保持一致 两台机器的基本情况如下所示 centos1 mypharma com 19
  • python之路 第三章 逻辑判断语句——布尔类型和比较运算符、if语句、if else、if elif else、嵌套

    目录 第三章 逻辑判断语句 01 布尔类型和比较运算符 02 If语句的基本格式 03 if else语句 04 if elif else组合使用的语法 05 判断语句的嵌套 第三章 逻辑判断语句 01 布尔类型和比较运算符 在生活中 我们
  • Feign接口方法返回值设置

    Feign接口方法返回值设置 一 介绍 随着微服务的广泛应用 越来越多的企业都会使用微服务进行项目开发 在各个服务之间需要通过feign来进行通信 所以在feign调用接口中方法会接受其他服务接口不同类型返回值 二 返回值设置 1 依据被调
  • el-table实现跨页全选

    el table实现跨页全选 在开发中 我们会遇到一些需要全选表格的需求 由于我们使用了后端分页 在选中时需要维护一个数组 便于回写 但是我们有时需要跨页全选 一个按钮选中所有 我们维护的数组如果从后台拿到所有数据去维护的话这样我们的性能就
  • idea中回退git历史版本并删除历史提交记录

    本篇文章主要介绍git在idea中的回退历史版本 适用场景为代码提错分支 正常回退版本都可适用 回退前先检查一下本地分支是否和远程分支对应 否则会失败 1 打开idea中git历史提交窗口 快捷键 alt 9 有改动则是自己改的快捷键 2
  • 逍遙安卓和Charles實現https抓包

    Charles是一款来自国外的非常强大抓包神器 具有十分简洁的界面 直观易用 通过这款软件可以帮助用户方便地进行抓包 它可以轻松记录浏览器和Internet之间的所有流量 是非常专业并基于java开发网络http抓包工具软件 非常适合开发人
  • conda使用详细

    目录 Anaconda环境变量 一 常用命令 1 创建Python虚拟环境 2 切换环境 3 对虚拟环境中安装额外的包 4 关闭虚拟环境 即从当前环境退出返回使用PATH环境中的默认python版本 5 删除虚拟环境 6 克隆虚拟环境 7
  • 教你如何简单的在windows 10使用Debug

    很多有Windows 10 系统的小伙伴都想要学习汇编 那么怎样搭建debug环境好呢 小编发现 很多这种类似的教程都是叫你去安装Dosbox 这就有一篇教你用用这种方法搭建的 但是不好的事那个界面有点让小编看着不舒服 当然你也可以选择使用
  • 移动端开发同后端交互安全机制记录

    前言 这两年移动端开发的热度明显不如前几年 而且混合式开发框架诸如appcan的兴起在一定程度上降低了移动端开发的门槛 而最近更加流行的React开发更是把移动端原生开发的热度拉低很多 基于React Native构建的移动APP无论是在体
  • Chrome 和 Chromium 区别

    Chromium Chromium 官网 https www chromium org Chromium 源码 https github com chromium chromium Chromium是谷歌的开源项目 由开源社区维护 拥有诸多
  • 支付宝妥协被银联“收编” 网联“尴尬”吗?

    据上海证券报从知情人士处证实 中国银联与支付宝已于9月10日举行内部签约仪式 就支付清算业务达成了相关合作 这也意味着 两大支付巨头 支付宝和微信支付均被合法清算组织 银联和网联 收编 对此 银联和支付宝方面昨日均表示 不作回应 但值得注意
  • 解决错误提示“error: #5: cannot open source input file “core_cmInstr.h“: No such file or directory“方法

    今天来分享一个我们在初期开发单片机时候遇到的一个很常见的错误 就是提示 error 5 cannot open source input file core cmInstr h No such file or directory 错误信息
  • AIGC数字人直播 ChatGPT MDJOURNey技术学习待续

    AIGC数字人直播 ChatGPT MDJOURNey技术学习待续
  • 第四届蓝桥杯JavaB组省赛-黄金连分数

    第四届蓝桥杯JavaB组省赛 黄金连分数 题目描述 题目描述 黄金分割数0 61803 是个无理数 这个常数十分重要 在许多工程问题中会出现 有时需要把这个数字求得很精确 对于某些精密工程 常数的精度很重要 也许你听说过哈勃太空望远镜 它首
  • 开源人脸识别引擎SeetaFace(一)

    SeetaFace Engine是一个开源的C 人脸识别引擎 它可以在不依赖第三方的条件下载CPU上运行 他包含三个关键部分 即 SeetaFace Detection SeetaFace Alignment和SeetaFace Ident
  • 王道——计算机网络

    第一章 以太网典型网络 协议 网络设备 网络体系结构 计算机网络 概念 网络包含计算机网络 计算机网络 分散的具有独立功能的计算机系统 通过通信设备与线路连接起来 由功能完善的软件实现资源共享和信息传递的系统 在端系统上安装软件 实现资源共
  • Vue3使用v-for按需遍历多选框按钮

    目录 前言 1 template 2 Setup 3 效果 前言 Vue3 setup语法糖 v for按需遍历 记录两次数据的diff算法 用来判断是否发起请求 按需提取源数据内容遍历 1 template template div h1
  • CPU高速缓存SRAM命中问题的总结与实验

    1 SRAM高速缓存的结构 获取本机CPU的SRAM缓存信息 我使用的是一个叫cpuinfo x86的小程序 可以获取x86架构的cpu相关信息 下载地址 http osxbook com book bonus misc cpuinfo x