QA15
为什么用连接器?
- 链接器可以使得分离编译成为可能:我们可以把一个大的应用程序分解成许多小的,更好管理的模块,单独编译每一个模块。
- 在修改的时候,只用编译被修改的模块,重新链接就行。能节省大量时间。
连接器工作内容与步骤?
- 符号解析
- 重定位
局部变量 链接时按照 本地符号 进行解析
全局变量 按照 全局符号 进行解析
链接器对局部变量、参数等符号怎么解析?
- 对符号的解析:
- 规则 1:不允许多个同名的强符号
- 规则 2:若有一个强符号和多个弱符号同名,则选择强符号
- 规则 3:如果有多个弱符号,选择任意一个
goto L0 的地址符号的强弱?链接时怎么处理?
有哪几种目标文件?怎么查看目标文件的各节信息?
- 目标文件:
- 可重定位目标文件:包含数据和代码,编译时多个可重定位目标文件生成可执行目标文件。
- 可执行目标文件:包含数据和代码,可以被直接加载运行。
- 共享目标文件:特殊的目标文件,可以在加载或运行时被动态的加载进内存并链接。(这个用于动态链接)
- 查看目标文件的各节信息
- objdump
可执行目标文件中的重定位主要完成哪些工作?
- 将多个单独的代码节(sections)和数据节合并为单个
节。 - 将符号从它们在.o文件中的相对位置重新定位到可
执行文件中的最终绝对内存位置。 - 用它们的新位置,更新所有对这些符号的引用。
目标文件中的数据节有哪几种,分别有什么区别?
- ELF头
- 段头表/程序头表
- .text节
- .rodata节(只读数据)
- .data节(数据/可读写)
- .bss节(未初始化全局变量)
- .symtab节(符号表)
- .rel.text节(可重定位代码)
- .debug节(调试信息表)
- 节头表Section header table(每个节的偏移量、大小)
可执行目标文件在执行时由操作系统把各节按大小在内存分配,并拷贝相应内容到内存区
QA16
按照从低到高地址,Linux下内存分成哪几个区?
- text段(代码段):存放程序代码,通常只读
- data段(数据段):存储已经初始化的全局变量和静态变量,可读可写。
- bss段(数据段):存储未初始化的全局变量和静态变量
- rodate(数据段):存储数据常量,只读。
- 栈stack(数据段):存储参数变量和局部变量,从高向低地址生长。
- 堆heap:被动态分配的内存段,从低向高生长。
全局变量怎么实现重定位(32位/64位)?
- 未解答
子程序怎么实现重定位?
- 未解答
Linux下归档器/库管理程序是什么?
- Idconfig(不确定)
库管理中每个函数可以生成单独.o也可以多个函数用一个.o,对应的源程序也是这样的。
静态连接时的目标文件生成与库文件的顺序无关。
共享库的动态连接有哪几种方法?
- 加载时链接:当可执行文件首次加载和运行时进行动态链接
- 运行时链接:在程序开始运行后(通过编程指令)进行动态链接
库打桩的应用有哪几个方面?
- 安全
- 监禁confinement (沙箱sandboxing)
- 幕后加密
- 调试
- 监控和性能分析
- 计算函数调用的次数
- 刻画函数的调用位置(sites)和参数
- Malloc 跟踪
- 检测内存泄露
- 生成地址痕迹(traces)
在什么时候进行库打桩?
- 打桩可出现在:
- 编译时:源代码被编译时
- 链接时间:当可重定位目标文件被静态链接来形成一个可执行目标文件时
- 加载/运行时:当一个可执行目标文件被加载到内存中, 动态链接,然后执行时
QA17
从哪几方面优化程序?在底层与核心技术方面、上层与应用方面、算法与UI方面等优化重点有何区别?
- 多个层次进行优化:
- 算法
- 数据表示
- 过程
- 循环
- 区别:
- 不知道
程序的性能优化从哪几方面解决?优化性能为何一定要理解“系统”?
- 程序是怎样被编译和执行的
- 现代处理器+存储系统是怎么运作的
- 怎样测量程序性能,确定“瓶颈”
- 如何在不破坏代码模块性和通用性的前提下提高性能
编译器能做的程序员也通常采用的一般有用的优化有哪几种?
- 代码移动
- 减少计算执行的频率
- 复杂运算简化
- 用更简单的方法替换昂贵的操作
- 移位、加,替代乘法/除法
- 识别乘积的顺序
- 共享公用子表达式
- 重用表达式的一部分
- GCC 使用 –O1 选项实现这个优化
- 去掉不必要的过程调用
妨碍编译器优化的因素有哪些?为什么编译器不能优化呢?
- 函数调用
- 函数可能有副作用
- 对于给定的参数,函数可能返回不同的值
- 内存别名使用
妨碍编译器优化的函数调用怎么进行补救或处理?
- 使用inline内联函数
- 用-O1是GCC这样做,但局限于单个文件之内
- 自己做代码移动
妨碍编译器优化的内存别名怎么进行补救或处理?
- 移除内存别名,不需要存储中间结果。
编译器优化的其他方法与策略?
- 减少函数调用
- 用临时/局部变量累计结果
- 避免每个循环的边界检查
- 使用指令级并行
QA17.5 其他编译相关优化技术
- 寄存器比内存快
- 用快的指令
- 用快的寻址方式
- 位操作比算术运算快
- 整数比浮点运算块
- 宏比函数快
- 简单的结构比复杂结构快
- 算法很重要
- C嵌入汇编快
- 并行比串行快
QA18
简述现代超标量CPU的结构与执行程序特点
- 一个周期执行多条指令。
- 这些指令是从一个连续的指令流获取的,通常被动态调度的。
什么容量、延迟界限、发射时间,他们之间有什么关系?
-
容量:表示能够执行该运算的功能单元的数量
-
发射时间:表示两个连续的同类型的运算之间需要的最小时钟周期数
-
延迟界限:简单理解就是延迟,整数加法的延迟界限为1,整数乘法的为3,浮点加法为3,浮点乘法为5
-
关系:
- 同一组指令,运算单元容量越大,发射时间越短,延迟界限越短
- 单元容量一定,延迟界限越大,发射时间越长。
x = (x OP d[i]) OP d[i+1] 与x = x OP (d[i] OP d[i+1])哪一个并行性好?
- x = x OP (d[i] OP d[i+1]) 的并行性更好
- 下一个循环的操作可以早一些开始,对之前的数据没有依赖。
采用x0 = x0 OP d[i]; x1 = x1 OP d[i+1]; 的分离累加器方法与x = x OP (d[i] OP d[i+1])的在流水线利用方面有何区别?
- 第一种情况为两条独立的流水,第二种情况的运算全部在同一条流水线中进行。
什么是SIMD?为什么会提高整数运算的速度?
- SIMD全称Single Instruction Multiple Data,单指令多数据流,能够复制多个操作数,并把它们打包在大型寄存器的一组指令集。
- 以同步方式,在同一时间内执行同一条指令。(百度答案我感觉不太对)
对CPU分支预测怎么处理的?程序员与编译器怎么应对?
- 对分支预测的处理:
- 静态预测:
- 预测分支永远不会发生(或永远会发生),因此总是顺序取下一条指令推测执行。仅当条件跳转指令被求值确实发生了跳转,则非顺序的代码地址被加载执行。
- 动态预测
- 静态预测:
- 避免不可预测的分支
QA19
以指令执行为例,说明CPU、内存、外存在数据访问与处理上的区别。
内存MM除了存储单元外,还有哪些硬件?
内存中的行缓冲器通常采用SRAM还是DRAM?
- DRAM(百度没有答案,谷歌上的一本书说是RDRAM,RDRAM是一种SDRAM)
SRAM与DRAM物理构成有何区别?
- SRAM存储1位需要4或6个晶体管(双稳态触发器),DRAM需要一个1个电容和1个晶体管(电容的电荷)。
DRAM芯片访问时地址信号线是怎么复用的?
- CPU将地址A放到总线上
- 总存储器从总线上读地址A,取出字x,然后将x放到总线上
- CPU从总线上读入字x,并将其放入寄存器%rax
- CPu将地址A放到总线上,主存储器读地址A并等待数据到来
内存是4G的,此地址空间不包括BIOS的ROM存储器
内存提高访问速度的方法有哪几种?
为什么每出现新一代DRAM芯片,容量至少提高4倍?
- 行地址和列地址分时复用, 每出现新一代存储器芯片,至少要增加一根地址线。每加一根地址线,则行地址和列地址各增加一位,所以行数和列数各增加一倍,因而容量至少提高到4倍。
操作系统对硬盘访问采用逻辑方式(簇),由谁来实现此块号与物理磁面号/道号/扇区号的访问呢?
- 由磁盘控制器维护。
内存与硬盘间传输的块方式采用什么硬件实现?
- DMA
QA20
存储访问局部性原理是什么意思?
- 程序倾向于使用距离最近用过的指令/数据地址相近或相等的指令/数据。
Cache访问不命中的种类?
-
冷不命中(强制不命中):当缓存为空时,对任何数据的请求都会不命中
-
冲突不命中:当缓存足够大,但是被引用的对象都映射到同一缓存块中,此种不命中称为冲突不命中
-
容量不命中:当工作集(working set)的大小超过缓存的大小时,会发生容量不命中
Cache怎么组织,其大小怎么计算?
- Cache共有S(2* s)组,每个组有E(2*
e)行,每行有1个有效位,t个标记为,有B(2**b)字节 - cache大小:C = S * E * B数据字节
程序怎么利用高速缓存访问存储器?
- 程序会先到高速缓存中寻找所需数据。如果能够找到,直接从高速缓存中调用,不再访问内存。当cache中对应位置为空时,发生冷不命中,将从下一级存储中寻找数据并将其放入本级存储。当对应位置不为空但标记位不同是,发生冲突不命中,将从下一级存储中寻找数据并替换此处数据。
空间局部性好的程序也常会发生Cache抖动,导致性能大降,是怎么回事?怎么解决?
- 可能是因为cache的容量小或每组内的行数较少,关联度低,导致冲突不命中的数量增多。(不确定)
为什么用中间位不用高位作为组/索引?
- 如果用高位做索引,连续的存储器块会映射到相同的高速缓存块中,具有良好空间局部性的程序顺序扫描一个数组的元素时,高速缓存总保存着一个块大小的数组原内容,高速缓存使用的效率很低。
- 用中间为作为组索引,相邻的块总是映射到不同的高速缓存行,提高高速缓存的效率。
你的计算机CPU各级Cache多大?是哪一种?C、S、E、B各是多少?
LRU的算法实现?命中行Counter=0,其他行的加1,淘汰计数器最大的-请设计控制逻辑(8路)。
- 算法实现:
I7的CPU,L2Cache为8路的2M容量,则其B= S= E=
- 8路:8路组相连,每组有8行,E = 8
- I7:每块64字节,B = 64
- 总容量2M,S= 2**20 / 64 / 8 = 2 ** 11 = 2048
全相联Cache有冲突不命中吗?为什么
- 全相连Cache没有冲突不命中,只有容量不命中
- 下一级存储器中的数据可对应本级cache中的任意一行,当本级cache未装满时,数据将被装入空白的行,而不会选择替换别的行。
访问高速缓存的地址比内存的地址要短,是吗?
- 是的
- 以I7为例,高速缓存地址为6(块索引)+6(组索引)+35(tag)= 47位,内存地址为64位。
QA21
Cache的写命中策略有哪些?
- 直写(write-through):当cache写命中时,处理器对Cache写入的同时,将数据写入到内存中,内存的数据和Cache中的数据都是同步的。(立即写入存储器)
- 写回(write-back):当CPU对cache写命中时,只修改cache的内容不立即写入主存,只当此行被换出时才写回主存。(需要一个修改位(脏位),与内存不同时为1,表示被修改)
Cache的写不命中策略有哪些?
- 写分配(Write Allocate):将主存装入 Cache 中,然后更新相应单元。(好处是更多的写遵循局部性)
- 非写分配(Not Write Allocate):直接写到主存中,不加载到缓存中。
为什么高速缓存用不命中率而不是命中率来衡量性能?
- 命中率相差较少时,实际性能相差很多。
- 命中率为97%和99%的性能可相差两倍。
怎么编写高速缓存友好的代码?
-
加快经常性事件:专注在核心函数和内循环上
-
使用内层循环的缓存不命中数量降到最低
- 反复引用变量更好(时间局部性)
- 步长为1的数据引用模式更好(空间局部性)
- 关键思想:程序的局部性的这个定性概念通过对缓冲存储器的理解而量化了
重新排列以提升程序的_____局部性
- 重新排列以提升程序的 空间 局部性
使用分块以提升程序的_____局部性
- 使用块来提高 时间 局部性
使用分块提高缓冲器命中率的基本思想与算法
- 充分利用数据的时间局部性。
新的计算机cache技术:指令Cache->微指令Cache、追踪缓存TraceCache等, TLB等
- 指令Cache->微指令Cache
- 追踪缓存TraceCache:
- 一般一级缓存中的指令缓存都是即时解码:而追踪缓存无须每次都进行解码指令,直接做解码,这些指令称为微指令(micro-ops),12K容量能存储12000个微指令。相比可以有效地增加在高速时脉下对指令的解码能力。
- TLB:
- 转译后备缓冲器,也被翻译为页表缓存、转址旁路缓存,为CPU的一种缓存,由存储器管理单元用于改进虚拟地址到物理地址的转译速度。当前所有的桌面型及服务器型处理器(如 x86)皆使用TLB。TLB具有固定数目的空间槽,用于存放将虚拟地址映射至物理地址的标签页表条目。