设计模式与代码的结构特性

工程实践介绍

本次工程实践主要是在Linux环境下对服务器板卡做温度控制。本项目的处理卡的等效理论峰值运算能力166.4 TOPS(INT8),可通过双槽位的PCIe Gen3×16集成于现有的各类服务器机架和工作站中,支持被动或主动两种散热方式,典型功耗为80W。处理卡支持最高32GB的DDR4内存容量,并具备ECC数据校验功能。在75w的功耗下,理论峰值速度每秒128万亿次定点运算。

本项目旨在优化服务器内芯片板卡在实际使用过程中出现板卡核心温度过高导致的降频问题。实际过程中板卡在一个板卡核心超过87°时自动保护降频,而且风扇的降温过程中会出现一定的温度波动。

本项目旨在通过监视板卡的功耗和温度,CPU的功耗和温度,以及服务器风扇的功耗等的关系,开发一个应用,编写出板卡温度读取函数库,风扇转速调控函数库,设计出高效的风扇控制算法。最终实现风扇智能控制算法,实现服务器风扇的风速平衡,整个系统低功耗,从而高效的控制服务器机箱内的系统风扇。

本次我们小组采用的设计模式为外观模式。

外观模式隐藏系统的复杂性,并向客户端提供了一个客户端可以访问系统的接口。这种类型的设计模式属于结构型模式,它向现有的系统添加一个接口,来隐藏系统的复杂性。

这种模式涉及到一个单一的类,该类提供了客户端请求的简化方法和对现有系统类方法的委托调用。

因为本项目涉及较多底层编程,因此程序核心部分是使用c语言代码完成。板卡供应商提供了一组访问底层传感器控制器的接口与数据结构。并且接口相当复杂。以访问板卡核心温度的代码为例。

cndevCheckErrors(cndevInit(CNDEV_VERSION_4));

cndevLibVersionInfo_t libVersion;
libVersion.version = CNDEV_VERSION_4;
cndevCheckErrors(cndevGetLibVersion(&libVersion));
printf("cndev lib version:%d.%d.%d\n",
libVersion.LibMajorVersion,
libVersion.LibMinorVersion,
libVersion.LibBuildVersion);

cndevRet_t ret;
cndevCardInfo_t cardInfo;
cardInfo.version = CNDEV_VERSION_3;
cndevCheckErrors(cndevGetDeviceCount(&cardInfo));

cndevTemperatureInfo_t tempInfo;
tempInfo.version = CNDEV_VERSION_3;
cndevCheckErrors(cndevGetTemperatureInfo(&tempInfo,boardId));
cndevCardClusterCount_t cardClusterCount;
cardClusterCount.version = CNDEV_VERSION_3;
cndevCheckErrors(cndevGetClusterCount(&cardClusterCount,boardId));

cndevTemperatureInfo_t tempInfo;
tempInfo.version = CNDEV_VERSION_3;
cndevCheckErrors(cndevGetTemperatureInfo(&tempInfo,boardId));
cndevCardClusterCount_t cardClusterCount;
cardClusterCount.version = CNDEV_VERSION_3;
cndevCheckErrors(cndevGetClusterCount(&cardClusterCount,boardId));
//for (int i = 0; i < cardClusterCount.count; i++)
for(int i = 0;i<cardClusterCount.count;i++) a[i] = tempInfo.Cluster[i];

 

以上只是实现了一个获取核心温度的功能。当时代码与接口相当复杂,且代码与板卡的依赖程度很高,可移植性很差,要获取板卡核心的温度,程序员必须知道供应商提供的库版本号,板卡的数量,板卡的id,板卡上核心的数量。

这些复杂的操作阻碍了程序员写出一个简洁高效的算法。为此我们必须降低访问复杂系统的内部子系统时的复杂度,简化客户端与之的接口。我们将上面的代码封装成了三个函数,提供了两个简洁通用与底层具体实现无关的接口:int getBoardNum(),int getClusterNum(int boardId)。

cndevLibVersionInfo_t getLibVersion(){
cndevLibVersionInfo_t libVersion;
libVersion.version = CNDEV_VERSION_4;
cndevCheckErrors(cndevGetLibVersion(&libVersion));
printf("cndev lib version:%d.%d.%d\n",
libVersion.LibMajorVersion,
libVersion.LibMinorVersion,
libVersion.LibBuildVersion);
return libVersion;
}

 

int getBoardNum(){
cndevRet_t ret;
cndevCardInfo_t cardInfo;
cardInfo.version = CNDEV_VERSION_3;
cndevCheckErrors(cndevGetDeviceCount(&cardInfo));
return cardInfo.Number;
}

 

int getClusterNum(int boardId){
cndevTemperatureInfo_t tempInfo;
tempInfo.version = CNDEV_VERSION_3;
cndevCheckErrors(cndevGetTemperatureInfo(&tempInfo,boardId));
cndevCardClusterCount_t cardClusterCount;
cardClusterCount.version = CNDEV_VERSION_3;
cndevCheckErrors(cndevGetClusterCount(&cardClusterCount,boardId));
return cardClusterCount.count;
}

 

访问传感器与控制风扇的接口如下:

void initialize();

void release();

//append print_str to filename
void output_data(char *filename, char *print_str);

//set the fan speed
void shutdownBMC();

//set the speed
void set_speed(int speed);

//get the lib infomation
cndevLibVersionInfo_t getLibVersion();

//get the num of board
int getBoardNum();

//get the utilization of the board with id
int getBoardUtilization(unsigned Id);

//get the number of cores
int getCoreNum(int boardId);
//get each core's utilization
void getCoreUtilization(unsigned boardId,int a[]);

//get theboard tmperature
int getBoardTemperature(int boardId);

//get cluster num
int getClusterNum(int boardId);
//get cluster temperature
void getClusterTemperature(int boardId,int a[]);

//get the board frequency
int getBoardFreq(int boardId);

//get the board power usage
int getBoardPowerUsage(int boardId);

 

所有接口都与具体底层实现无关,不需要输入任何和板卡相关的信息,以控制风扇温度为例,

首相获取板卡温度int getBoardTemperature();,然后通过一个有效的算法进行计算得出一个风扇转速值,然后设置风扇转速set_speed(). 

该程序的耦合度低。可移植性强,如果以后板卡变化了,那么控制算法不用重写,只需要改变接口函数即可。

 

因为使用的是c语言,不执行类等设计。所以我们的多态设计是通过回调函数实现的。

void startControl(int sleeptime,int kp,int ki,int kd,int target,int interval,void (record*)(int boardId,int boardtemperature,int clusernum,int clustertemperature,int speed,int boardfreq,int boardpowerusage))){
initialize();
int clustertemperature[100];
int boardNum = getBoardNum();
int clusernum = getClusterNum(0);
//shutdownBMC();
temperatureQueue q;
initTmp(&q,interval,target);
while(1){
int boardtemperature = getBoardTemperature(0);
getClusterTemperature(0,clustertemperature);
int boardfreq = getBoardFreq(0);
int boardpowerusage = getBoardPowerUsage(0);
printf("The tmp of board is %dC,core tmp = %dC\n",boardtemperature,clustertemperature[0]);
pushq(&q,boardtemperature);
int speed = getSpeed(&q,kp,ki,kd);
printf("speed is %d\n",speed);
(*record)(0,boardtemperature,clusernum,clustertemperature,speed,boardfreq,boardpowerusage);
set_speed(speed);
sleep(sleeptime);
}
release();
}

 

record 函数

void record(int boardid,int boardtemperature,int clusternum,int *clustertemperature,int speed,int freq,int powerutility){

time_t timep;
time(&timep);
char filename[32];
char rec[256] = {0};
struct tm *local = localtime(&timep);
int MaxTemperature = 0;
for (int i = 0; i < clusternum; i++){
if(clustertemperature[i]>MaxTemperature) MaxTemperature = clustertemperature;
}
sprintf(filename, "../data/%d-%d-%d.csv", (local->tm_year + 1900), (local->tm_mon + 1), local->tm_mday);
sprintf(rec, "%02d:%02d:%02d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
local->tm_hour + 8, local->tm_min, local->tm_sec, boardid, boardtemperature, clustertemperature[0],
clustertemperature[1], clustertemperature[2], clustertemperature[3], clustertemperature[4],
clustertemperature[5], clustertemperature[6], clustertemperature[7], MaxTemperature,
speed, freq, powerutility);
write_data(filename, rec);
}

 

 

项目源码及部署过程在GitHub上,地址 https://github.com/19970126ljl/ustc/tree/master/fan/project

 

 

posted on 2019-12-08 20:42  SA19225239  阅读(165)  评论(0编辑  收藏  举报