【csapp实验6】性能优化实验

一、题目

本实验,有两个任务:

  1. 任务1:你需要修改rowcol.c,跑得尽可能高的得分。注意考虑矩阵元素在存储器中的排列;
  2. 任务2:你需要修改poly.c,跑得尽可能高的得分。注意你需要首先实现一个指定学号的常系数多项式,然后才能实现其他函数。

【提升】建议有能力的同学考虑能否用SIMD指令完成。

具体请参考实验文档。

二、一些见解

性能优化其实主要考虑时间、空间局部性的利用,相关知识好好上课看看课本就能理解,其实不难理解。做完之后有个同学跟我分享了他的SIMD实现,跑的飞快~!!!建议硬件和能力支持的可以考虑用SIMD来实现(我的设备不允许我这样做哈哈哈哈哈……阴暗爬行🧗(我的为什么不支持)……(扭曲🌀)……(尖叫🙀))还有,注意,如果是笔记本,记得插电做,不要开省电模式,因为插电之后笔记本本身工作频率啥的会比较高,结果会比较好,亲测我的代码插电120,不插电0分……

三、开源代码:重生之学姐为你当牛马

poly.c

/**************************************************************************
	多项式计算函数。按下面的要求编辑此文件:
	1. 将你的学号、姓名,以注释的方式写到下面;
	2. 实现不同版本的多项式计算函数;
	3. 编辑peval_fun_rec peval_fun_tab数组,将你的最好的答案
		(最小CPE、最小C10)作为数组的前两项
***************************************************************************/

/*

*/


#include  <stdio.h>
#include  <stdlib.h>
typedef int (*peval_fun)(int*, int, int);

typedef struct {
  peval_fun f;
  char *descr;
} peval_fun_rec, *peval_fun_ptr;


/**************************************************************************
 Edit this comment to indicate your name and Andrew ID
#ifdef ASSIGN
   Submission by Harry Q. Bovik, bovik@andrew.cmu.edu
#else
   Instructor's version.
   Created by Randal E. Bryant, Randy.Bryant@cs.cmu.edu, 10/07/02
#endif
***************************************************************************/

/*
	实现一个指定的常系数多项式计算
	第一次,请直接运行程序,以便获知你需要实现的常系数是啥
*/


/*
1、register 是表示使用cpu内部寄存器(寄存器是中央处理器内的组成部分。
寄存器是有限存贮容量的高速存贮部件)的变量,而平时的int是把变量放在内存中,
存到寄存器中可以加快变量的读写速度。
2、位移比直接乘除法消耗的时间要短得多
*/
//35 69 50 92
int const_poly_eval(int *not_use, int not_use2, int x)
{
	register int result = 0;
	register int x1, x2, x3;
	register int tmp = x;
	register int tmp1 = tmp * tmp;
	register int tmp2 = tmp1 * tmp;
	x1 = (tmp << 6) + (tmp << 2) + tmp;
	x2 = (tmp1 << 5) + (tmp1 << 4) + (tmp1 << 1) ;
	x3 = (tmp2 << 6) + (tmp2 << 5) - (tmp2 << 2);
	result = 35 + x1 + x2 + x3;
	return result;
}



/* 多项式计算函数。注意:这个只是一个参考实现,你需要实现自己的版本 */

/*
	友情提示:lcc支持ATT格式的嵌入式汇编,例如

	_asm("movl %eax,%ebx");
	_asm("pushl %edx");

	可以在lcc中project->configuration->Compiler->Code Generation->Generate .asm,
	将其选中后,可以在lcc目录下面生成对应程序的汇编代码实现。通过查看汇编文件,
	你可以了解编译器是如何实现你的代码的。有些实现可能非常低效。
	你可以在适当的地方加入嵌入式汇编,来大幅度提高计算性能。
*/
int poly_eval(int *a, int degree, int x)
{
    int result = 0;
    int i;
    int xpwr = 1; // x的幂次 
//    printf("阶=%d\n",degree);
    for (i = 0; i <= degree; i++) {
	result += a[i]*xpwr;
	xpwr   *= x;
    }
    return result;
}
/*
1、并行设计:①如果最高幂次不超过10:每两项做一次运算(如果i=0说明a[0]没有纳入运算,如果不为0则说明刚好算完)
             ②如果超过10:则每10项一算
			 为什么选10做分界线?因为答案测试要测10项的😅
2、次方仍然沿用上面的,提前算好!!!
*/
int poly_eval12(int* a, int degree, int x)
{
	register int result = 0;
	register int i=degree, tmp = x;
	register int x2 = tmp * tmp;
	if (i >= 10) {
		register int x3 = x2 * tmp, x4 = x2 * x2, x5 = x3 * x2, x6 = x3 * x3, x7 = x4 * x3, x8 = x4 * x4, x9 = x5 * x4, x10 = x5 * x5;
		for (; i > 8; i -= 10) {
			result = result * x10 + a[i] * x9 + a[i - 1] * x8 + a[i - 2] * x7 + a[i - 3] * x6 + a[i - 4] * x5
				+ a[i - 5] * x4 + a[i - 6] * x3 + a[i - 7] * x2 + a[i - 8] * tmp + a[i - 9];
		}
	}
	if (i > 0) {
		for (; i > 0; i -= 2) {
			result = result * x2 + a[i] * tmp + a[i - 1];
		}
	}
	result = i == 0 ? result * x + a[0] : result;
	return result;
}
/*
	这个表格包含多个数组元素,每一组元素(函数名字, "描述字符串")
	将你认为最好的两个实现,放在最前面。
	比如:
	{my_poly_eval1, "超级垃圾实现"},
	{my_poly_eval2, "好一点的实现"},
*/
   
peval_fun_rec peval_fun_tab[] = 
{

 /* 第一项,应当是你写的最好CPE的函数实现 */
 {poly_eval12, "my CPE"},
 /* 第二项,应当是你写的在10阶时具有最好性能的实现 */
 {poly_eval12, "my degree=10"},

 {poly_eval, "poly_eval: reference implementation"},

 /* 下面的代码不能修改或者删除!!表明数组列表结束 */
 {NULL, ""}
};







结果是这样的:

matrix.c

/**************************************************************************
	行/列求和函数。按下面的要求编辑此文件:
	1. 将你的学号、姓名,以注释的方式写到下面;
	2. 实现不同版本的行列求和函数;
	3. 编辑rc_fun_rec rc_fun_tab数组,将你的最好的答案
		(最好的行和列求和、最好的列求和)作为数组的前两项
***************************************************************************/

/*

*/


#include  <stdio.h>
#include  <stdlib.h>
#include  "rowcol.h"
#include  <math.h>

/* 参考的列求和函数实现 */
/* 计算矩阵中的每一列的和。请注意对于行和列求和来说,调用参数是
	一样的,只是第2个参数不会用到而已
*/


/*传统双循环遍历求和*/
void c_sum(matrix_t M, vector_t rowsum, vector_t colsum)
{
    int i,j;
    for (j = 0; j < N; j++) {
	colsum[j] = 0;
	for (i = 0; i < N; i++)
	    colsum[j] += M[i][j];
    }
}

/*
矩阵的双循环遍历是不可避免的
but!!!
由于指针的跳跃遍历导致传统的求和效率很低
so
不妨一行一行的遍历,让指针不再跨区跳跃!!!
变量仍然和poly里的一样选择register int
另外,根据一些博主的blog,++i要比i++快!!!
*/
void mybest_c_sum(matrix_t M, vector_t rowsum, vector_t colsum)
{
	register int i, j=0;
	for (; j < N; ++j) {
		colsum[j] = 0;
		for (i=0; i < N; i = i + 2) {
			colsum[j] += M[i][j];
			colsum[j] += M[i + 1][j];
		}
	}
}

/* 参考的列和行求和函数实现 */
/* 计算矩阵中的每一行、每一列的和。 */


/*传统双循环遍历求和*/
void rc_sum(matrix_t M, vector_t rowsum, vector_t colsum)
{
    int i,j;
    for (i = 0; i < N; i++) {
	rowsum[i] = colsum[i] = 0;
	for (j = 0; j < N; j++) {
	    rowsum[i] += M[i][j];
	    colsum[i] += M[j][i];
	}
    }
}


/*
既然双循环遍历无法更改,那怎么办?怎么改进???
并行!!!
可是……我总不能行列同时并行,那样那面让读指针来回跳,效率不升反降
所以我只能一维并行
同上,变量仍然选择register int,用++i而不用i++
*/
void mybest_rc_sum(matrix_t M, vector_t rowsum, vector_t colsum)
{
	register int i=0, j;
	register int h, l, c = N - 3;
	for (; i < N; ++i)
	{
		h = 0, l = 0;
		j = 0;
		for (; j < c; j += 4)
		{
			h += (M[i][j] + M[i][j + 1]) + (M[i][j + 2] + M[i][j + 3]);
			l += (M[j][i] + M[j + 1][i]) + (M[j + 2][i] + M[j + 3][i]);
		}
		for (; j < N;)
		{
			h += M[i][j], l += M[j++][i];
		}
		rowsum[i] = h, colsum[i] = l;
	}
}

/*
	这个表格包含多个数组元素,每一组元素(函数名字, COL/ROWCOL, "描述字符串")
	COL表示该函数仅仅计算每一列的和
	ROWCOL表示该函数计算每一行、每一列的和
	将你认为最好的两个实现,放在最前面。
	比如:
	{my_c_sum1, "超级垃圾列求和实现"},
	{my_rc_sum2, "好一点的行列求和实现"},
*/

rc_fun_rec rc_fun_tab[] =
{
	 /* 第一项,应当是你写的最好列求和的函数实现 */
	{mybest_c_sum, COL, "Best column sum"},
	 /* 第二项,应当是你写的最好行列求和的函数实现 */
	{mybest_rc_sum, ROWCOL, "Best row and column sum"},

	{c_sum, COL, "Column sum, reference implementation"},

	{rc_sum, ROWCOL, "Row and column sum, reference implementation"},

	/* 下面的代码不能修改或者删除!!表明数组列表结束 */
	{NULL,ROWCOL,NULL}
};

结果:

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值