CSAPP:代码优化【矩阵读写】

博客园地址:https://www.cnblogs.com/ustca/p/11790314.html

写程序最主要的目标就是使它在所有可能的情况下都正确工作,另一方面,在很多情况下,让程序运行得很快也是一个重要的考虑因素。

编写高效程序需要做到以下两点:

  1. 选择一组合适的算法和数据结构
  2. 编写编译器能够有效优化以转换成高效可执行代码的源代码

第一点合适的算法和数据结构往往是大家写程序时会首先考虑到的,而第二点常被忽略。这里我们就代码优化而言,主要讨论如何编写能够被编译器有效优化的源代码,其中理解优化编译器的能力和局限性是很重要的。

以下我们将举例对常见的矩阵操作进行代码优化。

目标函数:图像逆时针旋转90°

旋转操作用下面两步操作完成:

  1. Transpose: 对第(i,j)个像素,执行Mij和Mji交换
  2. Exchange rows:行i和行N-1-i交换

原理图:
在这里插入图片描述
即对原有图像矩阵先进行一次对折,然后再进行一次翻转,就可以得到我们需要的逆时针旋转90°之后的矩阵。

其中我们用以下结构体表示一张图像的像素点:

typedef struct {
    
    unsigned short red;   /* R value */ 
    unsigned short green; /* G value */ 
    unsigned short blue;  /* B value */ 
} pixel;

red、green、blue分别表示一张彩色图像的红绿蓝三个通道。

原旋转函数如下:

#define RIDX(i,j,n) ((i)*(n)+(j))

void naive_rotate(int dim, pixel *src, pixel *dst) {
    
    int i, j; 
    for(i=0; i < dim; i++) 
        for(j=0; j < dim; j++) 
            dst[RIDX(dim-1-j,i,dim)] = src[RIDX(i,j,dim)]; 
    return;
}

图像是标准的正方形,用一维数组表示,第(i,j)个像素表示为I[RIDX(i,j,n)],n为图像边长。

参数:

  • dim:图像的边长
  • src: 指向原始图像数组首地址
  • dst: 指向目标图像数组首地址

RIDX(i,j,dim)读取目标像素点,RIDX(dim-1-j,i,dim)将i、j参数位置互换,实现了斜角对折,dim-1-j实现了上下翻转。

优化目标:使旋转操作运行的更快

当前我们拥有一个driver.c文件,可以对原函数和我们优化的函数进行测试,得到表示程序运行性能的CPE(每元素周期数)参数。

我们的任务就是实现优化代码,与原有代码同时运行进行参数的对比,查看代码优化情况。

优化的主要方法

  1. 循环展开
  2. 并行计算
  3. 提前计算
  4. 分块运算
  5. 避免复杂运算
  6. 减少函数调用
  7. 提高Cache命中率

循环主体只存在一条语句,该语句为内存的读写(读取一个源像素,再写入目标像素),不涉及函数调用与计算。所以我们的优化手段有提高Cache命中率、避免复杂运算、分块运算、循环展开与并行计算。

优化一:提高Cache命中率

在矩阵运算中,提高Cache命中率是最容易想到的方法,常见的是外循环按行遍历与外循环按列遍历的对比,因为存储顺序是行序,所以前者的运行速度会明显优于后者。

在已给出的naive_rotate函数中,核心循环语句涉及到读取一个像素点与写入一个像素点,显然写入像素点比读取像素点更耗费时间,这是由存储器的性质决定的,所以我们应该优先对写入像素点的索引进行优化。
在这里插入图片描述
上图描述了8种数组索引顺序,位于上方的蓝色方块代表原始图像,黄色箭头表示原始像素的读取顺序,位于下方的蓝色方块代表旋转后图像,红色箭头表示目标像素的写入顺序。

由于循环体执行速度主要与数据写入相关,所以我们优先考虑红色箭头也就是写入像素的cache命中率。

第一组到第四组的写入像素都是按照列序,理论上写入效果应该最差,第五第六组正向行序写入执行效果应该是最好的,第七第八组逆向行序应该稍差。下面我们给出分别按照8种不同顺序索引的代码,使用driver测试出他们的运行效率:

void rotate_leftup(int dim, pixel *src, pixel *dst) 
{
   
    int i, j;
    for (i = 0; i < dim; i++)
	for (j = 0; j < dim; j++)
	    dst[RIDX(dim-1-j, i, dim)] = src[RIDX(i, j, dim)];
}
void rotate_leftdown(int dim, pixel *src, pixel *dst) 
{
   
    int i, j;
    for (i = dim-1; i > -1; i--)
	for (j = 0; j < dim; j++)
	    dst[RIDX(dim-1-j, i, dim)] = src[RIDX(i, j, dim)];
}
void rotate_rightup(int dim, pixel *src, pixel *dst) 
{
   
    int i, j;
    for (i = 0; i < dim; i++)
	for (j = dim-1; j > -1; j--)
	    dst[RIDX(dim-1-j
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值