补码
我们先来看一个有趣的例子:
在这里我们定义了一个signed char
类型的数据,将其赋值为128,然后系统给我们一个警告,意思就是说它隐式地将数值给转换成了-128,这究竟是为什么呢?
其实在计算机中数值都是以补码的形式存储的:
正数的补码是其自身,负数的补码是其对应的正数的反码加1。
我们先看一下-42在计算机中是如何表示的:
它是先将其对象的正数42,0010 1010然后取反1101 0101,然后再加1,得到1101 0110。
我们知道signed char是占八位的。其存储范围是:0000 0000到1111 1111。那么128的补码还是128,其二进制表示为:1000 0000。但是这里有个问题,因为其类型是有符号的,所以最高位是符号位,也就是说这个数不能用来表示128了,那么它表示多少呢?我们就要看看它是谁的补码就是好了。我们先将1000 0000
取反,然后的到0111 1111
,加一得到1000 0000
,再乘以-1,得到-128。如果不太明白,我我们可以将128变大,变成129:
129为整数,其补码是其自身,1000 0001,又因为其最高位是符号位,所以其表示的是一个负数,它表示多少呢?先将其取反0111 1110
,然后+1得到0111 1111
,这时127,最后乘以-1得到-127。
switch语句为什么比if else快?
因为编译器在编译switch语句的时候会检查每个case常量,并且会创建出一个”跳转表”,这个表用来在表达式的基础上选择执行路径,其时间复杂度是O(1),而if else的时间复杂度是O(n),所以switch case要比if else快很多。
寄存器是什么?
寄存器是一种存储信息的硬件,它和内存一样可以用来存储,但是和内存不同的是,它的速度极其快(比RAM的主存要快得多,它还有一些辅助功能,它常常被用做软件和外设之间通信的桥梁,起到缓冲的作用,同时CPU中寄存器速度非常快,这就让经常需要用到的数据放到寄存器中,而不需要每次都从主存中去读取,从而提高了程序的执行速度。软件将信息写入寄存器,然后外设将信息从寄存器中读出去。如果外设有信息需要软件进行处理,那么它也需要先将信息放到寄存器中,然后有软件从中读取。寄存器的主要功能包括:
- 某些功能的配置和初始化,特别是在初始化阶段
- 缓存存储
- 不同种类的输入输出
- 状态报告,比如某个硬件的状态发生了变化
常见的寄存器包括:MAR(Memory Address Register),这个寄存器主要用来存放下一条即将执行的指令,CPU根据MAR中指令的地址取出指令,然后放到MDR(Memory Data Register中,然后CIR(Current Instruction Regitster)将指令从MDR中拷贝一份。MBR(Memory Buffer Register),用来存放要放入存储器的数据和从存储器中读出的数据。相关资料可以参考YouTube
CPU为什么需要三级缓存?
CPU和内存的速度不匹配是计算机领域的一个重要问题,如果内存中采用和CPU中相同型号的寄存器,那么其代价又会太高,并且根据局部性原理,这种需求是没有必要的。存储器的价格,速度,容量是人们考虑的主要因素,但是它们之间也存在这制约因素:速度越快,价格就越高;容量越大,价格越高;容量越大,速度越低。根据局部性原理(Principle of Locality)我们的程序中常用的数据又基本都是固定的。所以就在CPU和内存之间采用了三级缓存(它内部存储的)来提高获取数据的速度,如果数据能在L1,L2,L3中找到的话就直接从中取,如果没有再从内存取,如果内存没有,再从外存取,这样就会提高获取数据的速度。这里要注意每个处理器都有其自己的缓存区,最后他们共享一块内存,这就会造成数据不一致的严重问题。
上下文切换
在多任务执行的时候,上下文切换是指从一个线程或者进程切换到另外一个线程或者进程,这里的上下文是指:CPU寄存器和程序计数器中的内容。它的具体内容包含以下三个步骤:
- 挂起当前进程,将当前的CPU状态保存至内存
- 从内存中获取接下来将要执行进程的上下文,并且将其存入CPU寄存器中
- 返回程序计数器指定的位置(例如进程被终止的点)并且继续执行进程
这里的切换包含进程切换和线程切换两种情况,它们之间的相同点和不同点为:
一、进程切换
虚拟内存空间不能保持相同;调用操作系统内核;寄存器内容的切入切出和内核操作的转移是最消耗性能的;
二、线程切换
虚拟内存空间保持相同;调用操作系统内核;寄存器内容的切入切出和内核操作的转移是最消耗性能的;
这其中还有一个隐含的性能消耗点是:处理器缓存系统的失效。在虚拟内存空间切换的过程中会导致处理器的Translation Lookaside Buffer (TLB) 失效,这会造成很大的内存损耗。造成上下文切换的原因有:进程自动让出其时间片,或者调度器给它分配的时间片到了,或者硬件发生了中断操作,加锁,或者其它的软件操作也会造成上下文切换。上下文切换很消耗大量的CPU时间造成较大的性能损耗。
什么是线程?
线程是寄存器的一组状态,这组状态用来规定程序的执行顺序,执行地址,以及下条指令的地址。
什么是指令重排?
指令重排就是在执行程序的过程中,在确保不影响程序输出结果的情况下,为了提升性能,编译器和处理器对指令做的重排序:
指令重排可以带来性能的提升,它在单线程的情况下是不会出问题的,这一点是由程序的顺序规则来保证的,如果是在多线程就会出现各种意想不到的问题。比如:
1 |
|
这段代码不仅可以输出:
1 | x = 0 ; y = 1 |
但是在A1和A2,B1和B2重排序之后还可以输出:x = 0 ; y = 0
的情况。
参考资料: