引言
原因:内存中数据是按行的顺序优先来排列的(即先排第一行再排第二行...),如果按行序访问的话,Cache命中率很高(程序(空间)局部性),而按列序则需要反复访问主存。
int main() { float a = 10000; float b = a + 0.0001f; if(a < b) printf("<"); else if(a == b) printf("="); else printf(">"); /**/ bool c1 = (10000 < 10000.0001); bool c2 = (10000 < 10000.0001f); printf("%d %d",c1,c2); }
输出:=1 0
原因:浮点数的数据表示,绝对值越大,浮点数分布越稀疏,不能精确表示
多线程为何不是越多越好
1.资源竞争:增加线程数量会增加对共享资源(如内存、锁、网络连接等)的竞争,导致线程之间频繁地进行上下文切换和等待,从而降低系统效率。
2.上下文切换开销:线程的切换会带来一定的开销,包括保存和恢复线程上下文、调度开销等。随着线程数量的增加,上下文切换的开销也会增加,可能超过并行执行所带来的好处。
3.资源限制:系统的资源是有限的,包括处理器核心数、内存带宽、I/O 带宽等。增加线程数量可能会超过系统的物理资源限制,导致资源争夺和效率下降。
4.调度开销:线程的调度和管理也需要消耗一定的系统资源。当线程数量过多时,调度器可能需要更频繁地进行调度和管理,增加了系统的开销。
5.性能饱和:在某些情况下,系统的性能可能会饱和,即增加更多的线程并不能进一步提高系统的吞吐量或响应时间。
void f() { int x = -2147483648; int y = 1; if (x < y) cout << "x<y" << endl; if (-x > -y) cout << "-x>-y" << endl; cout << "x=" << x <<" -x="<<-x<< endl; }
0x8000 0000 取反加1还是自己
-x:考虑0 - x ,即0 + [x]补
大纲
≈汇编语言• 表示( Representation )– 数据 如何表示和存储?(不同数据类型:带符号整数、无符号整数、浮点数、数组、结构等)
– 指令 如何表示和编码(译码)?– 存储 地址 (指针)如何表示?– 如何生成复杂数据结构中数据元素的地址?• 转换( Translation )和链接( Link )– 高级语言程序对应的机器级代码是怎样的?– 如何合并成可执行文件?• 执行控制流( Control flow )– 逻辑控制流中的异常事件及其处理
优化问题
利用硬件特性:
编写程序时的优化:
又是冯诺依曼结构
- 计算机由运算器、控制器、存储器、输入设备和输出设备五个基本部件组成。
- 存储器不仅能存放数据,也能存放指令,两者在形式上没有区别,但计算机应能区分它们;控制器应能自动取出指令来执行;运算器应能进行加减乘除基本算术运算及逻辑运算和其它附加运算;操作人员可以通过输入设备、输出设备和主机进行通信。
- 计算机内部以二进制表示指令和数据;每条指令由操作码和地址码两部分组成。操作码指出操作类型,地址码指出操作数的地址。程序由一串指令组成。
- 计算机采用“存储程序”式工作方式。
冯·诺依曼结构最重要的思想是“存储程序” 工作方式 (Stored-program),其基本思想是:
任何要计算机完成的工作都要先被编写成程序, 然后将程序和原始数据送入主存后才能执行程序。一旦程序被启动执行,计算机能在不需操作人员干预下自动完成逐条指令取出和执行的任务。
一个模型机的基本硬件结构如图所示:
其中:
- 主存储器:简称主存,用于存放指令和数据,主存中每个单元都有编号,称为主存单元地址,简称内存地址。
- ALU:Arithmetic Logic Unit,用来进行算数逻辑运算。
- 控制器:自动逐条取出指令并进行译码。
- 输入输出设备:与外界交互
- 总线:取指令和存取数据都需要通过传输介质,连接不同部件进行信息传输的介质称总线,总线又分为:地址线(传输地址信息)、数据线(传输数据信息)、控制线 (传输控制信息)。 CPU访问主存时,先将主存地址、读/写命令分别送到总线的地址线和控制线,然后通过数据线发送和接收数据。
CPU 送到地址线的主存地址要首先存放在 主存地址寄存器 ( MAR ) 中;发送到或从数据线取来的信息要先存放在 主存数据寄存器 ( MDR ) 中;
程序和指令的执行过程
- 程序启动前,指令和数据都存放在存储器中,二者形式上没有差别,都是0/1序列。
- 程序被启动后,计算机自动取出一条一条指令执行,执行过程中无需人的干预。
- 指令执行过程中,指令和数据被从存储器取到CPU,存放在CPU内的寄存器中,指令在IR中,数据在GPRs中。
指令
一串用来指示CPU完成一个特定的原子操作的0/1序列。
例如,设一模型机字长为8位;有4个通用寄存器r0~r3,编号分别为0~3;有16个主存单元,编号为0~15,地址编码4位。主存单元、CPU中的ALU、GPRs、IR、MDR等与处理数据相关的单元的宽度都是8位;PC和MAR等与地址相关的单元的宽度都是4位;连接CPU和主存的总线中地址线为4位、数据线为8位、控制线若干位。机器采用8位定长指令格式,即每条指令都有8位,并有R型和M型两种指令格式(操作数类型不同):
格式 4位 2位 2位 功能 R型 op rt rs R[rt]←R[rt] op R[rs] 或 R[rt]←R[rs]M型 op addr R[0]←M[addr] 或 M[addr]←R[0]R[r]表示编号为r的寄存器中的内容;M[addr]表示地址为addr的主存单元的内容;“←”表示从右向左传送数据。指令集中有四种指令:
指令格式 op 含义 R 0000 寄存器间传送 (mov)0001 加 (add)M 1110 取数 (load)1111 存数 (store)则,1110 0110:R[0]←M[0110],表示将6号主存单元中的内容取到0号寄存器中;
0001 0001:R[0]←R[0] + R[1],表示将0号和1号寄存器的内容相加后将结果送到0号寄存去。
主存地址
|
主存单元内容
|
内容说明
|
指令的符号表示
|
0 |
1110 0110
|
R[0]←M[6],
取数操作
|
load r0, 6#
|
1 |
0000 0100
| R[1]←R[0],传送操作 |
mov r1, r0
|
2 |
1110 0101
| R[0]←M[5],取数操作 | load r0, 5# |
3 |
0001 0001
| R[0]←R[0]+R[1],相加操作 |
add r0, r1
|
4 |
1111 0111
| M[7]←R[0],存数操作 |
store 7#, r0
|
5 |
0001 0000
|
操作数
x
,值为
16
| |
6 |
0010 0001
|
操作数
y
,值为
33
| |
7 |
0000 0000
|
结果
z
,初始值为
0
|
程序的执行过程
程序的执行过程就是周而复始地执行一条一条指令的过程。
每条指令的执行过程包括:从主存取指令、对指令进行译码、PC增量计算、取操作数并执行、将结果送主存或寄存器保存。
程序在执行前
- 数据和指令事先存放在存储器中,每条指令和每个数据都有地址
- 指令按序存放
- 用程序起始地址置PC(PC总指向下一条要执行的指令,这里开始的时候PC=0)
开始执行程序
- 根据PC取指令
- 指令译码,确定操作类型
- 修改PC的值,使其指向下一条要执行的指令
- 取操作数
- 指令执行
- 回写结果,结果写入寄存器或内存单元
按照指令序列,重复上述步骤,直到所有指令执行完毕。
程序的开发和执行过程
机器语言开发
最早的程序开发是用机器语言编写程序,记录在纸带或卡片上;
汇编语言开发
- 汇编语言源程序由汇编指令构成。
- 汇编指令与机器指令一一对应。
- 汇编指令只能描述一些基本的操作
汇编语言和机器语言都是面向机器的语言,统称机器级语言。
高级语言开发
它们与具体的机器结构无关面向问题描述,引入数据类型,相比机器级语言,描述能力大大 增强高级语言中一条语句对应几条、几十条甚至几百条机器指令处理逻辑分为三种结构:顺序结构、选择结构、循环结构
以hello.c为例的执行过程:
计算机系统的层次结构
指令集体系结构(ISA)
ISA是计算机硬件到软件的接口,提供了如何操作计算机硬件的技术规范,是计算机系统中软件和硬件的交界面。
ISA是对计算机组成的抽象,提供了底层硬件为上层软件所能感知的部分。所有的软件都工作在ISA之上。没有ISA,软件就不知道如何使用计算机,也就无从开发程序了。
对ISA之上的软件,只要ISA相同,程序就可以运行在具有不同硬件结构的计算机上;对ISA之下的硬件,硬件必须提供ISA所规定的功能,否则就不能完整支持计算机系统的功能。
ISA定义了一台计算机可以执行的所有指令的集合和软件使用硬件的方式方法。具体包括:
- 有哪些指令,每条指令的格式、功能;
- 指令操作数的类型、寻址方式;
- 通用寄存器的个数、位数、编号、用途;控制寄存器的定义;
- I/O空间的编址方式、输入/输出结构和数据传送方式、存储保护方式;
- 中断结构、机器工作状态的定义和切换等