时刻记住要做合格的程序员——一次高性能程序设计实验课后的反思
MSRA实习期间,我们科大班的同学又多了一门课:高性能计算。授课的是新教员Teng。第二周结束的时候Teng布置了一道习题:用MPI实现K-means 或者Qsort的并行程序。
其实大家的学习热情并不高。我也不例外,每天组里面都有任务,软件工程课还有活要干,都很忙。于是匆匆忙忙就写了个K-means的算法交了作业:
View Code
1 #include<mpi.h>
2 #include<stdio.h>
3 #include<time.h>
4 #include<string.h>
5 #include<float.h>
6 #include<malloc.h>
7 #include<math.h>
8 #include<random>
9
10 #define D 4
11 #define K 4
12 #define N 100
13
14 const int round=100;
15 const float MAXF=9999;
16
17 typedef struct {
18 float dim[D];
19 }Point;
20
21 typedef struct {
22 float dim[D];
23 int n;
24 }CenterFactor;
25
26 int main(int argc,char ** argv)
27 {
28 Point *data,center[K];
29 int processRank,processNum;
30 int i,j,t;
31 int count;
32 int index;
33 float minn,dist;
34 int singleNum,leftNum;
35 MPI_Status mpistat;
36 MPI_Datatype pointType,oldType[1];
37 int blockCounts[1];
38 MPI_Aint offsets[1];
39 CenterFactor *centerFactor,centerTmp;
40 MPI_Request mpireq;
41
42
43 MPI_Datatype centerType,oldTypeCenter[2];
44 int blockCountsCenter[2];
45 MPI_Aint offsetsCenter[2],extent;
46
47
48 MPI_Init(&argc,&argv);
49 MPI_Comm_size(MPI_COMM_WORLD,&processNum);
50 MPI_Comm_rank(MPI_COMM_WORLD,&processRank);
51
52
53 offsets[0]=0;
54 oldType[0]=MPI_FLOAT;
55 blockCounts[0]=D;
56 MPI_Type_struct(1,blockCounts,offsets,oldType,&pointType);
57 MPI_Type_commit(&pointType);
58
59 MPI_Address(¢erTmp,&offsetsCenter[0]);
60 MPI_Address(¢erTmp.n,&offsetsCenter[1]);
61 offsetsCenter[1]-=offsetsCenter[0];
62 offsetsCenter[0]=0;
63 oldTypeCenter[0]=MPI_FLOAT;
64 blockCountsCenter[0]=D;
65 oldTypeCenter[1]=MPI_INT;
66 blockCountsCenter[1]=1;
67 MPI_Type_struct(2,blockCountsCenter,offsetsCenter,oldTypeCenter,¢erType);
68 MPI_Type_commit(¢erType);
69
70
71
72 printf("MPI program: processNum=%d , processRank=%d ",processNum,processRank);
73 fflush(stdout);
74 singleNum=N/(processNum-1);
75
76 if (processRank==0)
77 {
78 srand((unsigned int)time(NULL));
79 data=(Point *)malloc(sizeof(Point)*(N));
80 for (i=0;i<N;i++)
81 {
82 for (j=0;j<D;j++)
83 {
84 data[i].dim[j]=rand()/100;
85 }
86 }
87 for (i=0;i<K;i++)
88 {
89 memcpy(center+i,data+i,sizeof(Point));
90 }
91
92 MPI_Request *mpireqs = (MPI_Request*)malloc(sizeof(MPI_Request) * (processNum - 1));
93 MPI_Status *mpistats = (MPI_Status*)malloc(sizeof(MPI_Status) * (processNum - 1));
94 for (i=1;i<processNum;i++)
95 {
96 if (i==processNum)
97 {
98 leftNum=N-singleNum*(processNum-2);
99 }
100 else leftNum=singleNum;
101 MPI_Isend(data+singleNum*(i-1),leftNum,pointType,i,55,MPI_COMM_WORLD,&mpireqs[i - 1]);
102 }
103 ::MPI_Waitall(processNum - 1, mpireqs, mpistats);
104 free(mpistats);
105 free(mpireqs);
106 centerFactor=(CenterFactor*)malloc(sizeof(CenterFactor)*K*processNum);
107
108 }
109 else
110 {
111 if (processRank==processNum-1)
112 {
113 leftNum=N-singleNum*(processNum-2);
114 }
115 else leftNum=singleNum;
116
117 data=(Point *)malloc(sizeof(Point)*leftNum);
118 MPI_Recv(data,leftNum,pointType,0,55,MPI_COMM_WORLD,&mpistat);
119
120 centerFactor=(CenterFactor*)malloc(sizeof(CenterFactor)*K);
121 }
122
123 count=0;
124 while(count++<round)
125 {
126 if (processRank==0)
127 {
128 MPI_Request *mpireqs = (MPI_Request*)malloc(sizeof(MPI_Request) * 2 * (processNum - 1));
129 MPI_Status *mpistats = (MPI_Status*)malloc(sizeof(MPI_Status) * 2 * (processNum - 1));
130 for (i=1;i<processNum;i++)
131 {
132 MPI_Isend(center,K,pointType,i,55,MPI_COMM_WORLD,&mpireqs[i - 1]);
133 }
134
135 for (i=1;i<processNum;i++)
136 {
137 MPI_Irecv(centerFactor+K*i,K,centerType,i,55,MPI_COMM_WORLD,&mpireqs[(processNum - 1) + (i - 1)]);
138 }
139
140 MPI_Waitall( 2 * (processNum - 1), mpireqs, mpistats );
141 free(mpistats);
142 free(mpireqs);
143
144 for (i=0;i<K;i++)
145 {
146 centerFactor[i].n=0;
147 for (j=0;j<D;j++)
148 {
149 centerFactor[i].dim[j]=0;
150 }
151 }
152
153 for (i=1;i<processNum;i++)
154 for (j=0;j<K;j++)
155 {
156 centerFactor[j].n+=centerFactor[K*i+j].n;
157 for (t=0;t<D;t++)
158 {
159 centerFactor[j].dim[t]+=centerFactor[K*i+j].dim[t];
160 }
161 }
162
163 for (i=0;i<K;i++)
164 {
165 //if (centerFactor[i])
166 for (j=0;j<D;j++)
167 {
168 if (centerFactor[i].n!=0)
169 centerFactor[i].dim[j]/=centerFactor[i].n;
170 center[i].dim[j]=centerFactor[i].dim[j];
171 }
172 }
173 }
174 else
175 {
176 MPI_Recv(center,K,pointType,0,55,MPI_COMM_WORLD,&mpistat);
177 for (i=0;i<K;i++)
178 {
179 centerFactor[i].n=0;
180 for (j=0;j<D;j++)
181 {
182 centerFactor[i].dim[j]=0;
183 }
184 }
185
186
187 for (i=0;i<leftNum;i++)
188 {
189 minn=FLT_MAX;
190 for (j=0;j<K;j++)
191 {
192 dist=0;
193 for (t=0;t<D;t++)
194 {
195 dist+=(center[j].dim[t]-data[i].dim[t])*(center[j].dim[t]-
196
197 data[i].dim[t]);
198 }
199 dist=sqrt(dist);
200 if (dist<minn)
201 {
202 index=j;
203 minn=dist;
204 }
205 }
206 centerFactor[index].n++;
207 for (j=0;j<D;j++)
208 {
209 centerFactor[index].dim[j]+=data[i].dim[j];
210 }
211 }
212 MPI_Send(centerFactor,K,centerType,0,55,MPI_COMM_WORLD);
213 }
214
215 printf("Round %d\n",count);
216 for (i=0;i<K;i++)
217 {
218 printf("Center %d: ",i);
219 for (j=0;j<D;j++)
220 {
221 printf("%lf ",center[i].dim[j]);
222 }
223 printf("\n");
224 }
225 }
226
227 free(data);
228 free(centerFactor);
229
230 MPI_Type_free(&pointType);
231 MPI_Type_free(¢erType);
232 MPI_Finalize();
233
234 }
当时还是回到宾馆,躺在床上敲的。满脑子只想着快点over,然后提交作业。没有注释,没有细分函数,简直就像一陀堆在一起的砖头。甚至连自己都不想再看这个dirty的程序了。
过了两天Teng给我发了feedback邮件。结果让我很惊讶。
没想到Teng会认真地阅读程序的每一行,然后仔细的给出每一点意见。他先表扬了我的几个优点,比如作业提交的最快,用到了自定义结构、非阻塞调用等,然后指出程序的不足,如在程序的那个地方需要加上free()函数释放内存,哪一行应该用MPI_Waitall() 而不是MPI_Wait() ,用MPI_Scatterv()和MPI_Reduce ()来代替逐个进程调用MPI_Send()可以大大提高效率。
一共有13个同学提交了程序,Teng 对每一个同学都认真的回复了.真不知道他在阅读我们的dirty的代码上花了多少时间。有人猜测,Teng给我批改花的时间不会少于我们写程序的时间。在科大呆了三年,从未碰到一个老师/助教会对一件事这么负责。我们老是说自己很忙,现在想想,“忙”只是一种借口,一种不知不觉中被我们当作偷懒的工具罢了。
在被Teng的态度感动的同时,也对自己草率应付的心态感到羞愧。何时才能一名优秀的程序员?或许我要做的第一步,就是时刻记得,写代码,要对得起自己,对得起别人。