面试官:关于cpu你了解多少?(代码片段)

Sivan_Xin Sivan_Xin     2023-04-04     674

关键词:

CPU是如何执行程序的?

程序执行的基本过程

  • 第一步,CPU 读取「程序计数器」的值,这个值是指令的内存地址,然后 CPU 的「控制单元」操作「地址总线」指定需要访问的内存地址,接着通知内存设备准备数据,数据准备好后通过「数据总线」将指令数据传给 CPU,CPU 收到内存传来的数据后,将这个指令数据存入到「指令寄存器」。
  • 第二步,「程序计数器」的值自增,表示指向下一条指令。这个自增的大小,由 CPU 的位宽决定,比如 32 位的 CPU,指令是 4 个字节,需要 4 个内存地址存放,因此「程序计数器」的值会自增 4;
  • 第三步,CPU 分析「指令寄存器」中的指令,确定指令的类型和参数,如果是计算类型的指令,就把指令交给「逻辑运算单元」运算;如果是存储类型的指令,则交由「控制单元」执行;

简单总结:一个程序执行的时候,CPU 会根据程序计数器里的内存地址,从内存里面把需要执行的指令读取到指令寄存器里面执行,然后程序计数器根据指令长度自增,开始顺序读取下一条指令。

  • 在指令寄存器中,CPU会分析指令寄存器中的指令,确定指令的类型和参数,如果是计算类型的指令,就把指令交给「逻辑运算单元」运算;如果是存储类型的指令,则交由「控制单元」执行;
  • 程序计数器自增的长度与CPU位宽决定,比如 32 位的 CPU,指令是 4 个字节,需要 4 个内存地址存放,因此「程序计数器」的值会自增 4;

CPU 从程序计数器读取指令、到执行、再到下一条指令,这个过程会不断循环,直到程序执行结束,这个不断循环的过程被称为 CPU 的指令周期


  • 冯诺依曼模型定义了计算机基本结构:运算器、控制器、存储器、输入输出设备。

  • 内存

    我们的程序和数据都是存储在内存,存储的区域是线性的。

    在计算机数据存储中,存储数据的基本单位是字节(byte,1 字节等于 8 位(8 bit)。每一个字节都对应一个内存地址。

  • 中央处理器(CPU)

    中央处理器也就是我们常说的 CPU,32 位和 64 位 CPU 最主要区别在于一次能计算多少字节数据。这里的32位和64位,通常表示CPU的位宽。

    • 32 位 CPU 一次可以计算 4 个字节;(4个字节就是32位 4 * 8)
    • 64 位 CPU 一次可以计算 8 个字节;

    CPU内部还有一些组件,常见的有寄存器,控制单元和逻辑运算单元

    • 控制单元:负责控制CPU的工作。

    • 逻辑运算单元:负责运算。

    • 寄存器:存储计算时的数据,由于内存离CPU太远了,而寄存器就在CPU内部,计算速度更快。寄存器有以下几种:

      • 通用寄存器:存放需要运算的数据。
      • 程序计数器:存储CPU要执行的下一条指令的地址。
      • 指令寄存器:存放当前正在执行的指令。
  • 总线

    负责各种设备之间的通信。比如CPU读取内存数据时,要通过三个总线:

    1. 先通过地址总线来指定内存的地址。
    2. 再通过控制总线来指定读或写的命令。
    3. 最后通过数据总线来传递数据。
  • 线路位宽与CPU位宽

    • 线路位宽:数据在线路中传输,其实是通过操作电压,低电压表示0,高电压表示1。这样一位一位进行传输的方式称为串行,想要一次性多传输数据,可以增加线路的位宽。比如CPU 想要操作内存地址就需要地址总线,可以通过增加线路位宽的方式,增加CPU能操作的最大内存地址数量。(注意,不是说同时操作的最大内存地址数量)

      • 如果地址总线有 2 条,那么能表示 00、01、10、11 这四种地址,所以 CPU 能操作的内存地址最大数量为 4(2^2)个。那么,想要 CPU 操作 4G 大的内存,那么就需要 32 条地址总线,因为 2 ^ 32 = 4G。(32位对应有232个地址,对应的内存数是232 * 8bit=4Gbyte即4GB)
    • CPU位宽:CPU 的位宽最好不要小于线路位宽,否则工作起来会非常复杂且麻烦。如果计算的数额不超过 32 位数字的情况下,32 位和 64 位 CPU 之间没什么区别的,只有当计算超过 32 位数字的情况下,64 位的优势才能体现出来。

      • 另外,32 位 CPU 最大只能操作 4GB 内存,就算你装了 8 GB 内存条,也没用。而 64 位 CPU 寻址范围则很大,理论最大的寻址空间为 2^64
  • a = 1 + 2的执行具体过程

    • 程序—>汇编语言—>计算机指令

      CPU 是不认识 a = 1 + 2 这个字符串,这些字符串只是方便我们程序员认识,要想这段程序能跑起来,还需要把整个程序翻译成汇编语言的程序,这个过程称为编译成汇编代码。针对汇编代码,我们还需要用汇编器翻译成机器码,这些机器码由 0 和 1 组成的机器语言,这一条条机器码,就是一条条的计算机指令,这个才是 CPU 能够真正认识的东西。

    • 程序编译过程中,编译器分析代码,发现1和2是数据,所以放在内存中的「数据段」,编译器会把a = 1 + 2翻译成4条指令,存放到正文段中。

    • 编译完成后,具体执行程序的时候,程序计数器会被设置为 0x100 地址,然后依次执行这 4 条指令。

  • 上面的例子中,由于是在 32 位 CPU 执行的,因此一条指令是占 32 位大小,所以你会发现每条指令间隔 4 个字节。而数据的大小是根据你在程序中指定的变量类型,比如 int 类型的数据则占 4 个字节,char 类型的数据则占 1 个字节。

  • 你知道软件的 32 位和 64 位之间的区别吗?再来 32 位的操作系统可以运行在 64 位的电脑上吗?64 位的操作系统可以运行在 32 位的电脑上吗?如果不行,原因是什么?

    • 64 位和 32 位软件,实际上代表指令是 64 位还是 32 位的:

      • 如果 32 位指令在 64 位机器上执行,需要一套兼容机制,就可以做到兼容运行了。但是如果 64 位指令在 32 位机器上执行,就比较困难了,因为 32 位的寄存器存不下 64 位的指令
      • 操作系统其实也是一种程序,我们也会看到操作系统会分成 32 位操作系统、64 位操作系统,其代表意义就是操作系统中程序的指令是多少位,比如 64 位操作系统,指令也就是 64 位,因此不能装在 32 位机器上。

      总之,硬件的 64 位和 32 位指的是 CPU 的位宽,软件的 64 位和 32 位指的是指令的位宽。

  • CPU时钟频率:1GHz表示该CPU的时钟频率是1G,表示1秒会发出1G次数的脉冲信号,每一次脉冲信号的高低电平就是一个时钟周期。时钟周期时间越短,CPU运算的越快。

磁盘比内存慢几万倍?

  • 机械硬盘、固态硬盘、内存这三个存储器,到底和 CPU L1 Cache 相比速度差多少倍呢?

    • CPU L1 Cache 随机访问延时是 1 纳秒,内存则是 100 纳秒,所以 CPU L1 Cache 比内存快 100 倍左右

    • SSD 随机访问延时是 150 微秒,所以 CPU L1 Cache 比 SSD 快 150000 倍左右

    • 最慢的机械硬盘随机访问延时已经高达 10 毫秒,CPU L1 Cache 比机械硬盘快 10000000 倍左右;


  • 寄存器

    寄存器用来存储计算的数据,是最靠近 CPU 的控制单元和逻辑计算单元的存储器。

    寄存器的价格很贵,数量通常在几十到几百之间,每个寄存器可以用来存储一定的字节(byte)的数据。比如:

    • 32 位 CPU 中大多数寄存器可以存储 4 个字节;
    • 64 位 CPU 中大多数寄存器可以存储 8 个字节。

    寄存器的访问速度非常快,一般要求在半个 CPU 时钟周期内完成读写,CPU 时钟周期跟 CPU 主频息息相关,比如 2 GHz 主频的 CPU,那么它的时钟周期就是 1/2G,也就是 0.5ns(纳秒)。

  • CPU Cache

    CPU Cache 用的是一种叫 SRAM(Static Random-Access Memory,静态随机存储器) 的芯片。静态,说明有电时数据一直存在,但掉电数据丢失

    在 SRAM 里面,一个 bit 的数据,通常需要 6 个晶体管,所以 SRAM 的存储密度不高,同样的物理空间下,能存储的数据是有限的,不过也因为 SRAM 的电路简单,所以访问速度非常快。

    CPU 的高速缓存,通常可以分为 L1、L2、L3 三层高速缓存,也称为一级缓存、二级缓存、三级缓存。

    • L1 高速缓存是每个CPU Core独有的,访问速度快,只需要 2~4 个时钟周期。大小在几十 KB 到几百 KB 不等。

      L1 高速缓存的指令和数据是分开存放的,所以 L1 高速缓存通常分成指令缓存数据缓存

    • L2 高速缓存同样每个 CPU 核心都有,但是 L2 比L1 距离 CPU 核心更远,访问速度则更慢,速度在 10~20 个时钟周期。但大小比 L1 更大,通常大小在几百 KB 到几 MB不等,

    • L3 高速缓存通常是多个 CPU 核心共用的,位置比 L2 高速缓存距离 CPU 核心 更远,访问速度相对也比较慢一些,访问速度在 20~60 个时钟周期。大小也会更大些,通常大小在几 MB 到几十 MB 不等。

  • 内存

    内存用的芯片和 CPU Cache 有所不同,它使用的是一种叫作 DRAM (Dynamic Random Access Memory,动态随机存取存储器) 的芯片。动态,因为数据会被存储在电容里,电容会不断漏电,所以需要定时刷新电容,才能保证数据不会被丢失。

    相比 SRAM,DRAM 的密度更高,功耗更低,有更大的容量,而且造价比 SRAM 芯片便宜很多。存储一个 bit 数据,只需要一个晶体管和一个电容就能存储。

    DRAM 的数据访问电路和刷新电路都比 SRAM 更复杂,所以访问的速度会更慢,内存速度大概在 200~300 个 时钟周期之间。

  • SSD/HDD硬盘

    固态硬盘(Solid-state disk,SSD) 结构和内存类似。相比寄存器/Cache/内存的优点是断电后数据还存在;缺点是读写速度很慢。

    机械硬盘(Hard Disk Drive, HDD),通过物理读写的方式来访问数据的,因此它访问速度是非常慢的,它的速度比内存慢 10W 倍左右。由于 SSD 的价格快接近机械硬盘了,因此机械硬盘已经逐渐被 SSD 替代了。

  • 存储器的层次关系

    CPU 并不会直接和每一种存储器设备直接打交道,而是每一种存储器设备只和它相邻的存储器设备打交道。比如,CPU Cache 的数据是从内存加载过来的,写回数据的时候也只写回到内存,再从内存写回到硬盘中。

    • 另外,当 CPU 需要访问内存中某个数据的时候,如果寄存器有这个数据,CPU 就直接从寄存器取数据即可,如果寄存器没有这个数据,CPU 就会查询 L1 高速缓存,如果 L1 没有,则查询 L2 高速缓存,L2 还是没有的话就查询 L3 高速缓存,L3 依然没有的话,才去内存中取数据,并把内存中的数据读入到 Cache 中,CPU 再从 CPU Cache 读取数据。

    不同的存储器之间性能差距很大,构造存储器分级很有意义,分级的目的是要构造缓存体系。这样的访问机制,跟我们使用「内存作为硬盘的缓存」的逻辑是一样的。

如何写出让CPU跑的更快的代码?

  • CPU Cache有多快?

    根据摩尔定律,CPU 的访问速度每 18 个月就会翻倍,相当于每年增长 60% 左右,内存的速度当然也会不断增长,但是增长的速度远小于 CPU,平均每年只增长 7% 左右。于是,CPU 与内存的访问性能的差距不断拉大。到现在,一次内存访问所需时间是 200~300 多个时钟周期,这意味着 CPU 和内存的访问速度已经相差 200~300 多倍了。

    为了弥补 CPU 与内存两者之间的性能差异,就在 CPU 内部引入了 CPU Cache,也称高速缓存。

  • CPU Cache 数据结构?

    CPU Cache 是由很多个 Cache Line 组成的,Cache Line (缓存块)是 CPU 从内存读取数据的基本单位

    Cache Line 结构

    • 有效位(Valid bit:标记对应Cache Line 中数据是否有效的有效位。
    • 组标记(Tag:为了区分不同的内存块,在对应的CPU Cache LIne中会存储一个组标记,用来记录当前CPU Cache Line 中存储的数据对应的内存块,区分不同的内存块。
    • 数据(Data:从内存加载过来的数据。
  • 内存地址与Cache Line的直接映射

    CPU 访问内存数据时,是一小块一小块数据读取的,具体这一小块数据的大小,取决于 coherency_line_size 的值,一般 64 字节。在内存中,这一块的数据我们称为内存块(Block,读取的时候我们要拿到数据所在内存块的地址。

    一个内存的访问地址,包括组标记、CPU Cache Line 索引、偏移量这三种信息,于是 CPU 就能通过这些信息,在 CPU Cache 中找到缓存的数据。

    • 对于直接映射 Cache 采用的策略,就是把内存块的地址始终映射在一个 CPU Cache Line 的地址,至于映射关系实现方式,则是使用取模运算,取模运算的结果就是内存块地址对应的 CPU Cache Line(缓存块) 的地址。
    • 使用偏移量,可以在CPU Cache Line 中的数据块找到对应的字。
  • CPU Cache 读写过程?

    1. 根据内存地址中索引信息,计算在 CPU Cache 中的索引,也就是找出对应的 CPU Cache Line 的地址;

    2. 找到对应 CPU Cache Line 后,判断 CPU Cache Line 中的有效位,确认 CPU Cache Line 中数据是否是有效的,如果是无效的,CPU 就会直接访问内存,并重新加载数据,如果数据有效,则往下执行;

    3. 对比内存地址中组标记和 CPU Cache Line 中的组标记,确认 CPU Cache Line 中的数据是我们要访问的内存数据,如果不是的话,CPU 就会直接访问内存,并重新加载数据,如果是的话,则往下执行;

    4. 根据内存地址中偏移量信息,从 CPU Cache Line 的数据块中,读取对应的

      • CPU 在从 CPU Cache 读取数据的时候,并不是读取 CPU Cache Line 中的整个数据块,而是读取 CPU 所需要的一个数据片段,这样的数据统称为一个字(Word
  • 写出让CPU跑得更快的程序

    CPU操作L1 Cache的速度是很快的,提升CPU运行速度,可以提升访问L1 Cache的速度。

    那么L1 Cache 通常分为「数据缓存」和「指令缓存」,因此要分别提高「数据缓存」和「指令缓存」的缓存命中率。

    • 提升数据缓存的命中率

      CPU会一次从内存中加载多少元素到 CPU Cache ,可以在Linux 里通过 coherency_line_size 配置查看 它的大小,通常是 64 个字节。

      如果遇到遍历数组之类的情况时,按照内存布局顺序访问,将可以有效的利用 CPU Cache 带来的好处,这样我们代码的性能就会得到很大的提升。

    • 提升指令缓存的命中率

      使用显式分支预测工具,如果分支预测可以预测到接下来要执行 if 里的指令,还是 else 指令的话,就可以「提前」把这些指令放在指令缓存中,这样 CPU 可以直接从 Cache 读取到指令,于是执行速度就会很快。

      • 比如:如果你肯定代码中的 if 中的表达式判断为 true 的概率比较高,我们可以使用显示分支预测工具,比如在 C/C++ 语言中,如果 if 条件为 ture 的概率大,则可以用 likely 宏把 if 里的表达式包裹起来。

    还可以提升多核CPU的缓存命中率

    • L2 Cache和L1 Cache 是每个核心独有的,如果一个线程在不同核心来回切换,各个核心的缓存命中率就会受到影响,可以把线程绑定到某一个CPU核心上。

      在 Linux 上提供了 sched_setaffinity 方法,来实现将线程绑定到某个 CPU 核心这一功能。

整理自:小林coding

面试官:你精通多少种语言的helloworld?(代码片段)

HelloWorld,是程序员入门编程语言的第一课。不论是C、C++还是Java,我们写的第一个程序就是它了,还记得小编在大一C语言课上,花了一整节课时间才把它打印到控制台上。万事开头难啊,相信看到此文章的你们一定是个积极上进... 查看详情

面试官:你对redis缓存了解吗?面对这11道面试题你是否有很多问号?(代码片段)

...什么要用缓存?缓存使用不当会造成什么后果?面试官心理分析这个问题,互联网公司必问,要是一个人连缓存都不太清楚,那确实比较尴尬。只要问到缓存&#x 查看详情

面试官:你对redis缓存了解吗?面对这11道面试题你是否有很多问号?(代码片段)

...什么要用缓存?缓存使用不当会造成什么后果?面试官心理分析这个问题,互联网公司必问,要是一个人连缓存都不太清楚,那确实比较尴尬。只要问到缓存&#x 查看详情

面试干货7——刁钻面试官:关于redis,你都了解什么?(代码片段)

面试高频问题一、问题1:1.为什么要用redis?业务场景有哪些?追问1:可以说说redis数据类型吗?追问2:说到命令,那你对redis的事务了解吗?追问3:如何防止数据丢失?对持久化有了解吗&... 查看详情

面试官:你工作中做过jvm调优吗?怎么做的?(代码片段)

最近很多小伙伴跟我说,自己学了不少JVM的调优知识,但是在实际工作中却不知道何时对JVM进行调优。今天,我就为大家介绍几种JVM调优的场景。在阅读本文时,假定大家已经了解了运行时的数据区域和常用的垃... 查看详情

面试官:你工作中做过jvm调优吗?怎么做的?(代码片段)

最近很多小伙伴跟我说,自己学了不少JVM的调优知识,但是在实际工作中却不知道何时对JVM进行调优。今天,我就为大家介绍几种JVM调优的场景。在阅读本文时,假定大家已经了解了运行时的数据区域和常用的垃... 查看详情

闭包(代码片段)

Question1:JS闭包,你了解多少?`应该有面试官问过你:什么是闭包?闭包有哪些实际运用场景?闭包是如何产生的?闭包产生的变量如何被回收?这些问题其实都可以被看作是同一个问题,那就是面试官在问你:你对JS闭包了解多... 查看详情

面试干货7——刁钻面试官:关于redis,你都了解什么?(代码片段)

面试高频问题推荐:在准备面试的同学可以看看这个系列一、问题1:1.为什么要用redis?业务场景有哪些?追问1:可以说说redis数据类型吗?追问2:说到命令,那你对redis的事务了解吗?追问3&#x... 查看详情

面试官:怎么排查内存溢出?(代码片段)

...人。本文GitHubhttps://github.com/JavaFamily已收录,有一线大厂面试完整考点、资料以及我的系列文章。上次给老公们说过了死循环cpu飙高的排查过程,今天就带着老公们看看堆内存溢出我们一般怎么排查的。cpu100%排查文章在排查之前... 查看详情

面试官:我们来聊一聊redis吧,你了解多少就答多少

...小奇一个赞吧文章持续更新,可以微信搜索【小奇JAVA面试】第一时间阅读,回复【资料】更有我为大家准备的福利哟!文章目录一、前言二、面试三、Redis基本数据类型 查看详情

美团面试官问我一个字符的string.length()是多少,我说是1,面试官说你回去好好学一下吧(代码片段)

本文首发于微信公众号:程序员乔戈里publicclasstestTpublicstaticvoidmain(String[]args)StringA="hi你是乔戈里";System.out.println(A.length());以上结果输出为7。小萌边说边在IDEA中的win环境下选中String.length()函数,使用ctrl+B快捷键进入到Strin... 查看详情

关于线程上下文切换,你知道多少?(代码片段)

点击关注公众号,实用技术文章及时了解由于现在大多计算机都是多核CPU,多线程往往会比单线程更快,更能够提高并发,但提高并发并不意味着启动更多的线程来执行。更多的线程意味着线程创建销毁开销加大... 查看详情

handler的知识点你有了解多少?(代码片段)

...理?不不不,这个问题已经烂大街了,我要是面试官,我会这么问。我们知道在Handler中,存在一个方法叫sendMessageDelay,作用是延时发送消息,请解释一下Handler是如何实现延时发送消息的?Looper.lo 查看详情

面试官一上来就问我chrome底层原理和http协议(万字长文)(代码片段)

...更是对你未来的职业发展大有裨益。下面,我总结了4个面试常考的关于浏览器和网络通信的问题,为你重新梳理浏览器,网络通信、页面渲染、JavaScript、浏览器安全等知识,从而让你对整个前端后端体系有全新的认识。第一问... 查看详情

ios底层探索之多线程(十四)—关于@synchronized锁你了解多少?(代码片段)

对于多线程你了解多少?对于锁你又了解多少?锁的原理你又知道吗?iOS底层探索之多线程(一)—进程和线程iOS底层探索之多线程(二)—线程和锁iOS底层探索之多线程(三)—初识GCDiOS底层探索之多线程(四)—GCD的队列iOS... 查看详情

撸一撸多线程的来龙去脉你就知道,为什么面试官折磨喜欢问这种问题了!(代码片段)

撸一撸多线程的来龙去脉你就知道,为什么面试官折磨喜欢问这种问题了!有一天张三去面试,被面试官问了一个问题:​什么叫多线程?张三:多线程就是多个线程在cpu上执行,抢占Cpu资源,提高CPU执... 查看详情

撸一撸多线程的来龙去脉你就知道,为什么面试官折磨喜欢问这种问题了!(代码片段)

撸一撸多线程的来龙去脉你就知道,为什么面试官折磨喜欢问这种问题了!有一天张三去面试,被面试官问了一个问题:​什么叫多线程?张三:多线程就是多个线程在cpu上执行,抢占Cpu资源,提高CPU执... 查看详情

关于java中的lambda表达式你了解多少?(代码片段)

...克杯等)直接跳到末尾去评论区领书这篇文章我们将讨论关于Java中的Lambda 查看详情