【仅供参考】Csapp第五章课后习题答案(欢迎批评指正)

本文探讨了代码性能优化的策略,通过分析关键路径上的操作,如浮点数加法和乘法的CPE(计算性能评估),以及如何通过并行计算和循环展开来提升效率。例如,`inner4`函数的优化减少了迭代次数但保持了乘法操作,而`effective_memset`函数通过地址对齐和批量设置提高了内存操作效率。同时,`polynew`函数利用了多项式计算的特性来减少重复计算。这些方法在实际编程中对于提高代码运行速度具有重要意义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

5.13

【答案】

A.

B.3.0

C.1.0

D.浮点乘法不在关键路径上。

【解释】

A.如图

B.关键路径上只有浮点数加法CPE为3.0

C.整数加法的CPE为1.0

D.结合图理解。

5.14

【答案】

void inner4(vec_ptr u, vec_ptr v, data_t *dest) {
  long i;
  long length = vec_length(u);
  data_t *udata = get_vec_start(u);
  data_t *vdata = get_vec_start(v);
  data_t sum = (data_t) 0;
  for (i = 0; i < length-6; i+=6) {
    sum = sum + udata[i] * vdata[i]
              + udata[i+1] * vdata[i+1]
              + udata[i+2] * vdata[i+2]
              + udata[i+3] * vdata[i+3]
              + udata[i+4] * vdata[i+4]
              + udata[i+5] * vdata[i+5];
  }
  for(; i < length; i++) {
    sum = sum + udata[i] * vdata[i];
  }
  *dest = sum;
}

A.虽然迭代次数减少为6/n,但是每次迭代中有6个乘法操作,所以关键路径上还是一共有n个乘法操作。

B.与上面相同

5.15

【答案】

void inner4(vec_ptr u, vec_ptr v, data_t *dest) {
  long i;
  long length = vec_length(u);
  data_t *udata = get_vec_start(u);
  data_t *vdata = get_vec_start(v);
  data_t sum = (data_t) 0;
  data_t sum1 = (data_t) 0;
  data_t sum2 = (data_t) 0;
  data_t sum3 = (data_t) 0;
  data_t sum4 = (data_t) 0;
  data_t sum5 = (data_t) 0;
  for (i = 0; i < length-6; i+=6) {
    sum = sum + udata[i] * vdata[i];
    sum1 = sum1 + udata[i+1] * vdata[i+1];
    sum2 = sum2 + udata[i+2] * vdata[i+2];
    sum3 = sum3 + udata[i+3] * vdata[i+3];
    sum4 = sum4 + udata[i+4] * vdata[i+4];
    sum5 = sum5 + udata[i+5] * vdata[i+5];
  }
  for(; i < length; i++) {
    sum = sum + udata[i] * vdata[i];
  }
  *dest = sum + sum1 + sum2 + sum3 + sum4 + sum5;
}

因为只有两个加载器

5.16

【答案】

只需要用括号将后面两两括起来,括哪两个都可以,只要是两两括起来就可以。

void inner4(vec_ptr u, vec_ptr v, data_t *dest) {
  long i;
  long length = vec_length(u);
  data_t *udata = get_vec_start(u);
  data_t *vdata = get_vec_start(v);
  data_t sum = (data_t) 0;
  for (i = 0; i < length-6; i+=6) {
    sum = sum + (udata[i] * vdata[i]
              + (udata[i+1] * vdata[i+1]
              + (udata[i+2] * vdata[i+2]
              + (udata[i+3] * vdata[i+3]
              + (udata[i+4] * vdata[i+4]
              + (udata[i+5] * vdata[i+5])))));
  }
  for(; i < length; i++) {
    sum = sum + udata[i] * vdata[i];
  }
  *dest = sum;
}

5.17

 【答案】

void effective_memset(void *s, int c, size_t n){
    unsigned long newc = (unsigned long)(1+(2<<8)+(2<<16)+(2<<24)+(2<<32)+
                        (2<<40)+(2<<48)+(2<<56))*(unsigned char);  
    size_t K = sizeof(unsigned long);                         #newc是用unsigned long
    size_t cnt = 0;                                            类型的数据保存8个c。
    unsigned char *schar = s;                                 #此处的循环目的是地址对齐
    while((size_t)schar % K == 0){
        *schar++ = (unsigned char)c;
        cnt++;
    }
    
    size_t rest = n - cnt;
    size_t part1 = rest / K;
    size_t part2 = rest % K;
    unsigned long *spart1 = (unsigned long *)schar;           #此处目的地址为K的倍数
    size_t i;                                                  使用字级的写
    for(i = 0; i < part1; i++){
        *spart1++ = newc;
    };

    schar = (unsigned char *)spart1;                          #最后的收尾再回到字节级的写
    for(i = 0; i < part2; i++){
        *schar++ = (unsigned char)c;
    };
    return s;
}

5.18

【答案】

double polynew(double a[], double x, long degree) {
  long i = 1;
  double result = a[0];
  double result1 = 0;
  double result2 = 0;
  double xpwr = x;
  double xpwr1 = x * x * x;
  double xpwr2 = x * x * x * x * x;
  double xpwr_loop = x * x * x * x * x * x;
  for (; i <= degree - 6; i+=6) {
    result = result + (a[i]*xpwr + a[i+1]*xpwr*x);
    result1 = result1 + (a[i+2]*xpwr1 + a[i+3]*xpwr1*x);
    result2 = result2 + (a[i+4]*xpwr2 + a[i+5]*xpwr2*x);
    xpwr *= xpwr_loop;
    xpwr1 *= xpwr_loop;
    xpwr2 *= xpwr_loop;
  }
  
  for (; i <= degree; i++) {
    result = result + a[i]*xpwr;
    xpwr *= x;
  }
  return result + result1 + result2;
}

5.19

【答案】

void psumnew(float a[], float p[], long n) {
  long i;
  float val, last_val;
  float tmp1, tmp2, tmp3, tmp4;
  last_val = p[0] = a[0];
  for (i = 1; i < n - 4; i++) {
    temp1 = last_val + a[i];
    temp2 = temp1 + a[i+1];
    temp3 = temp2 + a[i+2];
    temp4 = temp3 + a[i+3];
    p[i] = temp1;
    p[i+1] = temp2;
    p[i+2] = temp3;
    p[i+3] = temp4;
    last_val = temp4;
  }
  for (; i < n; i++) {
    last_val += a[i];
    p[i] = last_val;
  }
}

### CSAPP 第三章 课后习题解答与分析 #### 函数 `aframe` 的栈帧结构解析 在讨论函数 `aframe` 的实现时,注意到此函数调用了库函数 `alloca` 动态分配局部数组 `p` 所需的空间。这种动态内存分配方式使得编译器无法提前知晓所需的具体空间大小,因此引入了变长栈桢的概念来处理这种情况。 当执行到第4行代码时,栈指针被设定为值 \(s_1\);而在第7行处再次调整至新的位置 \(s_2\)。此时,在这两个地址间可能存在一段未使用的额外空间 \(e_1\)。到了第9行,则正式将变量 `p` 指向这段由 `alloca` 分配出来的缓冲区首部[^2]。 ```c void aframe(int n) { int *p; // ... p = (int *)alloca(n * sizeof(int)); } ``` #### 数据成员偏移量计算实例 对于联合体(union)类型的元素访问操作而言,由于其内部各字段共享同一块存储区域,所以不同字段之间的相对位移关系变得尤为重要。例如给定如下定义: ```c typedef union { struct { long y; char z[8]; } e1; struct { short x; long next;} e2; } ele_t; ``` 针对上述数据结构,可以得出以下结论: - 成员 `e1.p` 和 `e1.y` 的字节偏移分别为0和8; - 同样地,`e2.x` 及 `e2.next` 则分别位于偏移0以及8的位置上[^3]。 #### 进一步探讨过程间的参数传递机制 考虑这样一个场景——通过指针间接修改另一个对象的内容。具体来说就是下面这个名为 `proc` 的C语言子程序,它接收一个指向联合体类型 `ele` 的指针作为输入,并利用箭头运算符完成特定逻辑的操作。 ```c void proc(ele_t *up) { up->e2.x = *(up->e2.next->e1.p) - up->e2.next->e1.y; } ``` 在这个例子中,假设传入的对象已经初始化完毕并含有有效数值,那么该语句的作用即是从指定路径读取两个整数相减的结果赋值给目标域 `x`。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值