【CSAPP】第3章 程序的机器级表示学习笔记2

3.6 控制

1.条件码

 除了算术和逻辑操作会设置条件码以外,cmp和test指令也会设置条件码,但这两个指令不改变任何寄存器的值,注意这两条指令也要反着读,比如 cmp S1,S2 算的是S2-S1       

2.访问条件码

1)下面这些指令可以根据条件码设值某一字节的值,可以是寄存器,也可以是内存。

注意setl这个操作,以上一次操作是a-b举例,对于没有溢出的情况,OF=0,SF决定了结果,a>b结果就是正的,SF=0,a<b结果就是负的,SF=1;对于溢出了的情况,分正溢出和负溢出,正溢出是a是正数,b是负数,a-b得了个大数,导致结果正溢出,注意,正溢出时结果是负数,SF=1,那么SF^OF就是1^1=0,a此时是大于b的,所以setl要置0,对于负溢出,a是负数,b是正数,相减得了个特别小的数,导致结果负溢出,注意,负溢出时结果是正数,SF=0,那么SF^OF就是0^1=1,a此时是小于b的,所以setl要置1。

2)下面这些是跳转指令,用以实现分支结构。

跳转可以分两大类,直接跳转和间接跳转,直接跳转是给出一个标号作为跳转目的,间接跳转的跳转目的是由寄存器中的数据或是寄存器中的内存地址的数据给出的。

跳转也可以分有条件跳转和无条件跳转,注意,条件跳转只能是直接跳转 

跳转指令的目标的编码使用PC相对地址,它是由本条指令的目标编码加上下一条指令的地址得到的,这是早期处理器的实现导致的,下面这一习题对于理解这一点很有帮助。注意本条指令的目标编码是由十六进制补码给出的,而下一条指令的地址是十六进制无符号数。

3.分支语句的实现

1)用条件控制来实现条件分支,这是传统的方法,这种机制简单而通用,但是在现代的处理器上他可能会变得低效

2)用条件传送来实现条件分支,这种策略只在一些受限的情况下可行,但是他在现代的处理器上更加高效。这与用条件控制来实现的本值区别就在于,条件传送会计算两个分支的结果,最后再根据分支条件来选择最终结果,而如果用条件控制来实现的话,其会先进行条件语句的比较,再计算正确分支的结果。

4.循环的实现

1)do-while

do
    body-statement
while(test-expr);
loop:
    body-statement
    t=test-expr;
    if(t)
        goto loop;

do-while语句可以写成这样的goto形式

2)while

while(test-expr)
    body-statement
    goto test;
loop : 
    body - statement
test : 
    t = test - expr;
    if (t)
        goto loop;

这是相应while语句的jump to middle模板

另一种叫做guarded-do,他先测试条件,如果不成立直接结束,如果成立则变成一个do-while循环

t=test-expr;
if(!t)
    goto done;
do
    body-statement
    while(test-expr);
done:
t=test-expr;
if(!t)
    goto done;
loop
    body-statement
    t=test-expr;
    if(t)
        goto loop;
done:

3)for

 for循环可以转化成等价的while循环,就可以套用while循环的模板了。

5.switch语句

switch语句用了一个很优雅的跳转表来实现,具体可以看下面这个例子

 

### 关键技术和方法概述 #### 优化编译器的能力和局限性 为了使程序达到最佳性能,理解编译器如何工作及其能力边界至关重要。编译器可以自动完成许多常见的优化措施,比如常量折叠、公共子表达式的消除以及无用代码删除等。然而,在面对复杂的控制结构或涉及指针运算的情况下,其效果可能有限[^1]。 #### 表示程序性能 衡量软件效率通常依赖于时间复杂度分析与实际运行测试相结合的方式。前者通过算法理论来预估执行速度;后者则借助工具记录具体耗时情况,从而找出潜在瓶颈所在之处。 #### 消除循环中的低效部分 针对频繁迭代的部分采取特别处理手段是提升整体表现的有效途径之一。这包括但不限于减少每次重复计算相同值所带来的开销、调整数组访问模式以利于缓存命中率提高等方面的工作。 ```c // 原始版本可能存在多次相同的计算 for (int i = 0; i < n; ++i) { result[i] = a * b + c; } // 改进后的版本提前进行了乘法运算 double ab = a * b; for (int i = 0; i < n; ++i) { result[i] = ab + c; } ``` #### 减少过程调用次数 函数调用虽然有助于增强代码可读性和重用性,但在某些场景下也会引入额外负担。对于那些仅用于局部逻辑实现的小型辅助功能来说,考虑将其内联展开可能是更好的选择——当然这也取决于具体情况而定[^4]。 #### 避免不必要内存引用 当涉及到大量数据交换时,应当尽可能降低对外部资源(如磁盘文件)的请求频率,并合理规划内部缓冲区大小以便更好地适应硬件特性。此外,还应注意防止因不当索引而导致越界访问等问题的发生。 #### 理解现代处理器架构特点 深入研究CPU内部运作机制可以帮助开发者充分利用多核并行优势、指令流水线设计原理等相关概念来进行针对性改进。例如利用SIMD扩展加速向量化操作就是一种常见做法。 #### 循环展开技术应用实例 适当增加循环体内语句数量可以在一定程度上缓解分支预测失败带来的负面影响,进而加快整个序列遍历的速度。不过需要注意的是过度使用该策略可能会造成寄存器溢出风险增大以及其他不利后果。 ```cpp // 单次循环 for(int i=0;i<n;++i){ sum+=arr[i]; } // 展开四倍后 for(int i=0;i<n-3;i+=4){ sum += arr[i]+arr[i+1]+arr[i+2]+arr[i+3]; } if(n%4!=0){ // 处理剩余项 for(;i<n;++i){ sum+=arr[i]; } } ``` #### 提升并行处理水平的方法探讨 除了简单地将任务分配给不同核心外,还可以探索更多高级别的协作方式,像OpenMP这样的库提供了便捷的操作接口让用户轻松定义共享变量范围内的同步关系。另外,也可以尝试采用Amdahl定律指导下的混合编程模型来平衡串行与并发之间的比例关系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值