电子说
由于CPU速度非常快,且价格非常昂贵,我们必须得 充分压榨CPU ,得像生产队的驴一样,让它不停地工作!
为了合理利用 CPU 的高性能,同时尽可能地节约成本,现代计算机将这些储存器充分的结合起来,由于这些硬件的数据存取速度差异导致了计算机系统编程中的各种问题:
为了充分压榨CPU的性能, CPU 会对指令乱序执行或者语言的编译器会指令重排 ,让CPU一直工作不停歇,但同时会导致有序性
问题。
在CPU中为了能够让指令的执行尽可能地同时运行起来,采用了 指令流水线 。一个 CPU 指令的执行过程可以分成 4 个阶段:取指、译码、执行、写回
。这 4 个阶段分别由 4 个独立物理执行单元来完成。
理想的情况是:指令之间无依赖,可以使流水线的并行度最大化。但是如果两条指令的前后存在依赖关系,比如数据依赖,控制依赖等,此时后一条语句就必需等到前一条指令完成后,才能开始。所以CPU为了提高流水线的运行效率,对无依赖的前后指令做 适当的乱序和调度 。
还有一种情况 编译器会指令重排 ,比如java语言,JVM 的编译器会对其指令进行重排序的优化( 指令重排 )。
所谓指令重排是指在不改变原语义的情况下,通过调整指令的执行顺序让程序运行的更快。JVM中并没有规定编译器优化相关的内容,也就是说JVM可以自由的进行指令重排序的优化。
无论是编译期的指令重排还是 CPU 的乱序执行 ,主要都是为了让 CPU 内部的指令流水线可以“填满”,提高指令执行的并行度,充分利用CPU的高性能。
为了平衡CPU的寄存器和内存的速度差异,计算机的CPU 增加了高速缓存,但同时导致了 可见性
问题。我们知道当程序执行时,一般CPU会去从内存中读取数据,来进行计算。CPU计算完之后,需要把数据重新放回到内存中。
当CPU的多个核心参与一个程序的运行,从内存中读取一个共享变量的数据,当不同核心间进行了各自的计算,把计算后的值放入自己的缓存中而不选择立即写入内存中(CPU写入内存的时机是不确定的)。那么在CPU的缓存中,这个共享变量有可能存放着不同的数据,这就导致了缓存的可见性问题。即一个线程对数据的修改无法对其他线程可见。
为了平衡CPU 与 I/O 设备
的速度差异,操作系统增加了进程、线程
概念,以分时复用 CPU,但同时导致了原子性
问题。
原子操作就是不可分割的操作,在计算机中,就是指不会因为线程调度被打断的操作。
当一个程序去I/O 设备读取数据, 由于I/O 设备数据存入读取速度,相比于CPU的执行速度来说度日如年,CPU这么牛逼这么昂贵的宝贝,怎么能让它歇着,得让它一直干活,去切换执行其他程序。也就是将CPU的时间进行分片,让各个程序在CPU上轮转执行。但被剥夺执行权的程序,等它从IO读取完数据后,还是得让CPU继续执行的,这时需要一个数据结构来保存,以便之后恢复继续执行,这个就是进程。
一开始进程中 只有一个"执行流",干活的人就一个。随着任务越来越多,发现进程不够用了,经常导致整个程序被阻塞,这时计算机让进程有多个执行流,干活的人变多了,那程序就不会再被阻塞了,"执行流" 就是线程。
如何解决这3个问题,就是并发、多线程需要处理的事,当然这是后话。
参考资料:
《深入理解计算机系统》
《计算机组成原理》
《计算机组成原理》--唐朔飞
https://zhuanlan.zhihu.com/p/379947484
全部0条评论
快来发表一下你的评论吧 !