CPU 应该 搞 0 级 Cache , 而不是 大寄存器

CPU 应该 搞 0 级 Cache ,  而不是 大寄存器  。

 

具体的说,   是 CPU 应该 搞  精简指令集 RISC  和   0 级 Cache ,    而 不是 大寄存器   。

 

0 级 Cache 也可以称为   L0 Cache   。

 

0 级 Cache 是 离 CPU 最近 的 Cache,  访问 只需要 1 个 时钟周期,   和 寄存器 一样  。

 

那  0 级 Cache  和 寄存器 有 什么 区别  呢   ?

 

0 级 Cache  在 内存 地址编制 内,  和  一级 Cache 、二级 Cache 、三级 Cache 、内存 在 一个 统一 的 地址空间 里,  按 统一 的 地址管理  。

 

而 寄存器 是 不在 内存 地址编制 里 的    。

 

0 级 Cache  从 下级存储 (一级 Cache 、二级 Cache 、三级 Cache 、内存)   载入载出 哪些 数据 是 完全 由 程序员 控制 的,  具体的, 是 完全 由 程序员用 汇编指令控制的  。

 

这是 和   一级 Cache 、二级 Cache 、三级 Cache   的 不同  。

 

一级 Cache 、二级 Cache 、三级 Cache   载入载出  哪些 数据 是由  CPU 自己决定的, 比如 根据 命中算法,  程序员 无权干涉  。

 

程序员 用 指令  map_in   将 内存(Cache) 地址 和 数据 映射 进  0 级 Cache ,   如果 0 级 Cache 里 的 存储单元 原来已经 映射 了 地址 和 数据,  此时 将 新的 地址 映射 到 这个 存储单元,   则 旧 的 数据 将 替换 为 新 的 数据,  旧 的 映射地址 将 映射 成 新 的 地址,   如果 旧 的 数据 被 修改过,  则 要 先 写回 对应 的 内存(Cache) 地址  ,  这 称为 map_out ,     也可以称为  载出  。

map_in  也可以 称为 载入  。

 

 

 

0 级 Cache 的 好处 是 :

 

1    指针取值( * 指针) 和 指针字段( 指针 --> 字段  ) 可以 享有 和 局部变量 一样 的 寄存器优化 的 待遇 

      寄存器优化 就是 把 常用 的 数据 存在 寄存器 里 反复使用  。

      在 寄存器 架构下,   指针取值( * 指针) 和 指针字段( 指针 --> 字段  )   不容易 做 寄存器优化,  因为 指针 会 改变,    * 指针 和  指针 --> 字段  会 随 指针 的 改变 而 改变,

      同时,   * 指针 和  指针 --> 字段   可能 被 其它 同样指向这个 地址 的 指针 修改,  比如 指针2 和 指针 相等,    * 指针2 和 指针 -> 字段 修改 的 数据 就是  * 指针 和  指针 --> 字段  的 数据,    但是  * 指针 和  指针 --> 字段  并不知道 数据 被 修改   。

      这还只是  单线程 的 情况   。

      多线程 也会 造成 类似 的 数据 不一致 的 情况   。

      但 使用 0 级 Cache 的 话,   0 级 Cache 是 按 地址 访问 的,  和  一级 Cache 、二级 Cache 、三级 Cache 、内存  同在一个 地址编制 ,   对于 指针取值( * 指针) 和 指针字段( 指针 --> 字段  ),   都是 按 地址访问,   不用 担心 数据不一致 的 问题   。   而  访问  0 级 Cache 的 时间 是  1 个 时钟周期,   和 寄存器 一样快  。

 

2    多核 数据同步 和 单核多线程 并发 数据一致

      这 其实 是 第 1 点 里 说 的 多线程 的 情况,   对于 多核 的 共享数据,  修改 时 要 mutex 并 同步到 各 核 的 Cache,  在 寄存器 架构下,  对于 需要 实时同步 的 多核数据, 是 不能 做  寄存器优化 的,   也就是 要 禁用 寄存器优化,   比如 C++  里 的 atomic<T> 原子类型 是 禁用 寄存器 优化 的  。

      而 现在 用  0 级 Cache,   就不存在这个问题,    0 级 Cache 和 现在 的 1 级 Cache 一样,  修改 原子数据  时 直接 mutex 和 通知 其它 核 同步,

      这样 会不会 影响性能 ? 

      不会  。    读取 时 仍然 是   1 个 时钟周期 ,   修改 时 会 发起 mutex ,  mutex 要 通知 到 其它 核 ,   当然 需要 一些 的 时钟周期,    另外,  若 收到 其它 核 已 改写数据 的 通知,   要从 其它 核 的 Cache 里 把 数据 同步过来,    这 也要 一些 时钟周期   。

      除此以外,  读取 时 是   1 个 时钟周期   。   也就是说,   如果  自己 不改写,  也 没有 收到 其它 核 改写 的 通知,   读取 0 级 Cache 里 的 原子变量 是  1 个 时钟周期,   和 普通变量 一样   。

 

      对于 单核多线程 并发 共享数据,   要 保证 数据 在 并发中一致,  也要 禁用 寄存器优化,   同理,   用  0 级 Cache,    就不存在这个问题    。

 

3    编译器 / 程序员   不用 考虑 把 寄存器 里 的 数据 写回 Cache / 内存   

      在 寄存器 架构下,   常用 的 数据 存在 寄存器 里 反复使用  ,  用完后(比如 函数 结束时),  如果 数据 被 修改 过,  要 写回 Cache / 内存 ,

      用  0 级 Cache  就 不用 编译器 / 程序员 考虑 这件事 了   。

      0 级 Cache 会 记录 哪些 数据 被 修改过,  被 修改 的 才 写回 映射 的 内存地址(当然, 实际上 可能 是 写 Cache , 也可能写 内存),

      这 需要  0 级 Cache 的 硬件电路 将 被 修改过的 存储单元 标记 为  “被修改” ,

      对于 这一点,   硬件电路 很容易 做到  。

 

      事实上,  在 0 级 Cache 里,  程序员 也不用 考虑 在 什么 “时机” 把 数据 写回   一级 Cache ( 二级 Cache 、三级 Cache 、内存 ),

      因为  0 级 Cache 也是 Cache,  和   一级 Cache 、二级 Cache 、三级 Cache 、内存  本身 就是 一个 体系 ,

      就好像 程序员 不用 考虑   一级 Cache 的 数据  “写回”  二级 Cache 、三级 Cache 、内存   。

 

       “时机”  比如 上面说的  “用完后(比如 函数 结束时)”   ,     在 0 级 Cache 里,  程序员 也不用 考虑  这些   。

 

        程序员 只要 考虑 把 哪个 (需要的) 地址 映射 到  0 级 Cache 的 哪个 存储单元,   这个 存储单元 原来 的 数据 如果 修改过的话, 会 自动写回 映射 的 地址  ( 一级 Cache 、二级 Cache 、三级 Cache 、内存 )   。

 

4     Localize   指针访问 一个周期

InitedLength  原子变量  Add() 时 改变   Local Agent

 

 

map out  硬件电路 容易做到

 

 

我提倡 用 模块线路图 来 设计 硬件电路,     硬件电路 本来 就是 模块化 的 ,  用 模块线路图 设计 很适合 。   模块 的 规格,  包括 接口 和 电路参数 作为 模块 的 说明书 单独说明 就好   。

 

其实 设计 CPU 很简单,      主要 是  制造工艺  和   电路计算 比较难   。

 

CPU 应该 搞 0 级 Cache , 而不是 大寄存器

上一篇:linux网络连接激活失败


下一篇:python - 将数据转换成 excl 表格, json 等文件 (dajngo - 打开网页后自动下载)