1 // This file is part of BOINC.
2 // http://boinc.berkeley.edu
3 // Copyright (C) 2013 University of California
4 //
5 // BOINC is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU Lesser General Public License
7 // as published by the Free Software Foundation,
8 // either version 3 of the License, or (at your option) any later version.
9 //
10 // BOINC is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 // See the GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public License
16 // along with BOINC. If not, see <http://www.gnu.org/licenses/>.
17 //
18 // This program serves as both
19 // - An example BOINC-CUDA application, illustrating the use of the BOINC API
20 // and CUDA API. [ SEE NOTE BELOW ]
21 // - A program for testing various features of BOINC.
22 //
23 // The program reads the input nxn matrix from the "input" file, inverts the
24 // matrix NUM_ITERATIONS times and write to "output" file.
25 //
26 // command line options
27 // -run_slow: sleep 1 second after each character
28 // -cpu_time N: use about N CPU seconds after copying files
29 // -early_exit: exit(10) after 30 chars
30 // -early_crash: crash after 30 chars
31 //
32 // See http://boinc.berkeley.edu/trac/wiki/GPUApp for any compiling issues
33 // Contributor: Tuan Le (tuanle86@berkeley.edu)
34 //
35 // NOTE: As currently written, this sample is of limited usefulness, as it
36 // is missing two important features:
37 // * Code to determine the correct device assigned by BOINC. It needs to get
38 // the device number from the gpu_opencl_dev_index field of init_data.xml
39 // if it exists, else from the gpu_device_num field of init_data.xml if that
40 // exists, else from the --device or -device argument passed by the client.
41 // See api/boinc_opencl.cpp for code which does this.
42 // * Code to select which NVIDIA GPU to use if there are more than one on the
43 // system; it needs to call cudaSetDevice().
44 //
45
46 #include "cuda.h"
47 #include "cuda_config.h"
48
49 using std::string;
50
51 /*** GLOBALS ***/
52 bool run_slow = false;
53 bool early_exit = false;
54 bool early_crash = false;
55 double cpu_time = 20, comp_result;
56
main(int argc,char ** argv)57 int main(int argc, char** argv) {
58 int i, retval, lastInversion=0, checkpointExists=0, dimension=0;
59 double fd;
60 char input_path[512], output_path[512], chkpt_path[512], buf[256];
61 REAL* h_idata;
62 MFILE out;
63 FILE* state, *infile;
64 double num=0;
65
66 generate_random_input_file(MATRIX_SIZE); //call this if you don't want to
67 //construct the input file manually
68
69 for (i=0; i<argc; i++) {
70 if (!strcmp(argv[i], "-early_exit")) early_exit = true;
71 if (!strcmp(argv[i], "-early_crash")) early_crash = true;
72 if (!strcmp(argv[i], "-run_slow")) run_slow = true;
73 if (!strcmp(argv[i], "-cpu_time")) {
74 cpu_time = atof(argv[++i]);
75 }
76 }
77
78 retval = boinc_init();
79 if (retval) {
80 fprintf(stderr,
81 "%s boinc_init returned %d\n",
82 boinc_msg_prefix(buf, sizeof(buf)), retval
83 );
84 exit(retval);
85 }
86
87 // open the input file (resolve logical name first)
88 //
89 boinc_resolve_filename(INPUT_FILENAME, input_path, sizeof(input_path));
90 infile = boinc_fopen(input_path, "r");
91 if (!infile) {
92 fprintf(stderr,
93 "%s Couldn't find input file, resolved name %s.\n",
94 boinc_msg_prefix(buf, sizeof(buf)), input_path
95 );
96 getchar();
97 exit(-1);
98 }
99
100 boinc_resolve_filename(OUTPUT_FILENAME, output_path, sizeof(output_path));
101
102 // See if there's a valid checkpoint file.
103 // If so retrieve the current matrix and inversion number
104 //
105 boinc_resolve_filename(CHECKPOINT_FILE, chkpt_path, sizeof(chkpt_path));
106 state = boinc_fopen(chkpt_path, "r");
107 if (state) {
108 printf("Checkpoint file is detected. Read from checkpoint file ... \n");
109 checkpointExists=fscanf(state, "%d", &lastInversion);
110 if (checkpointExists == 1) {
111 printf("Last inversion # is : %d\n",lastInversion);
112 fscanf(state,"%d",&dimension);
113 cudaMallocHost((void **)&h_idata,dimension*dimension*sizeof(REAL));
114 for (int i=0;i<dimension*dimension;++i) {
115 fscanf(state, "%lf", &num);
116 h_idata[i] = num;
117 }
118 }
119 fclose(state);
120 } else {
121 printf("There's no valid checkpoint file!\n");
122 }
123
124 retval = out.open(output_path, "wb");
125
126 if (retval) {
127 fprintf(stderr,
128 "%s APP: matrix_inversion output open failed:\n",
129 boinc_msg_prefix(buf, sizeof(buf))
130 );
131 fprintf(stderr,
132 "%s resolved name %s, retval %d\n",
133 boinc_msg_prefix(buf, sizeof(buf)), output_path, retval
134 );
135 perror("open");
136 exit(1);
137 }
138
139 #ifdef APP_GRAPHICS
140 // create shared mem segment for graphics, and arrange to update it
141 //
142 shmem = (UC_SHMEM*)boinc_graphics_make_shmem("matrix_inversion", sizeof(UC_SHMEM));
143 if (!shmem) {
144 fprintf(stderr,
145 "%s failed to create shared mem segment\n",
146 boinc_msg_prefix(buf, sizeof(buf))
147 );
148 }
149 update_shmem();
150 boinc_register_timer_callback(update_shmem);
151 #endif
152
153 if (checkpointExists != 1) {
154 dimension=get_matrix_dimension(infile);
155 printf("Matrix dimension: %d\n",dimension);
156 cudaMallocHost((void **)&h_idata,dimension*dimension*sizeof(REAL));
157 fetch_elements_into_host_memory(infile,h_idata);
158 out.printf("\n----------------- Before being inversed ----------------\n\n");
159 printf("Computation is running ... Inverse the matrix %d times. Start at inversion #1\n",
160 NUM_ITERATIONS);
161 } else {
162 out.printf("\n----------------- Last checkpointed inversion #%d ----------------\n\n",
163 lastInversion);
164 printf("Computation is resumed ... Inverse the matrix %d more times. Start at inversion #%d\n",
165 NUM_ITERATIONS-lastInversion,lastInversion+1);
166 }
167 print_to_file(&out,h_idata,dimension);
168
169 for (int i=lastInversion+1;i<=NUM_ITERATIONS;++i) {
170 invert(h_idata,dimension);
171 printf("Finish inversion #%d\n",i);
172 if (run_slow) {
173 boinc_sleep(1.);
174 }
175 if (early_exit && i>30) {
176 exit(-10);
177 }
178 if (early_crash && i>30) {
179 boinc_crash();
180 }
181
182 if (boinc_time_to_checkpoint()) {
183 //if (i==7) {
184 printf("Perform checkpointing at inversion # %d\n",i);
185 //we'll need to write the current matrix to the state file.
186 retval = do_checkpoint(out, i, h_idata, dimension);
187 if (retval) {
188 fprintf(stderr,
189 "%s APP: matrix_inversion checkpoint failed %d\n",
190 boinc_msg_prefix(buf, sizeof(buf)), retval
191 );
192 exit(retval);
193 }
194 boinc_checkpoint_completed();
195 }
196
197 fd = i/NUM_ITERATIONS;
198 if (cpu_time) fd /= 2;
199 boinc_fraction_done(fd);
200 }
201
202 out.printf("\n\n----------------- Final inversion #%d----------------\n\n",
203 NUM_ITERATIONS);
204 print_to_file(&out,h_idata,dimension);
205 cudaFreeHost( h_idata );
206 retval = out.flush(); //force the output file to be closed.
207 if (retval) {
208 fprintf(stderr,
209 "%s APP: matrix_inversion flush failed %d\n",
210 boinc_msg_prefix(buf, sizeof(buf)), retval
211 );
212 exit(1);
213 }
214
215 // burn up some CPU time if needed
216 //
217 if (cpu_time) {
218 printf("\nBurning up some CPU time ... \n");
219 double start = dtime();
220 for (int i=0; ; i++) {
221 double e = dtime()-start;
222 if (e > cpu_time) break;
223 fd = .5 + .5*(e/cpu_time);
224 boinc_fraction_done(fd);
225
226 if (boinc_time_to_checkpoint()) {
227 retval = do_checkpoint(out, NUM_ITERATIONS, h_idata, dimension);
228 if (retval) {
229 fprintf(stderr,
230 "%s APP: maxtrix_inversion checkpoint failed %d\n",
231 boinc_msg_prefix(buf, sizeof(buf)), retval
232 );
233 exit(1);
234 }
235 boinc_checkpoint_completed();
236 }
237 comp_result = do_a_giga_flop(i);
238 }
239 }
240 boinc_fraction_done(1);
241 #ifdef APP_GRAPHICS
242 update_shmem();
243 #endif
244
245 printf("\nDone! Please press ENTER to exit. ");
246 getchar();
247 boinc_finish(0);
248 }
249
250 /*** BOINC FUNCTION DEFINITIONS ***/
251
252 /* Do a billion floating-point ops */
do_a_giga_flop(int foo)253 static double do_a_giga_flop(int foo) {
254 double x = 3.14159*foo;
255 int i;
256 for (i=0; i<500000000; i++) {
257 x += 5.12313123;
258 x *= 0.5398394834;
259 }
260 return x;
261 }
262
263 /* Save the computation state into checkpoint file */
do_checkpoint(MFILE & mf,int n,REAL * h_idata,int dimension)264 int do_checkpoint(MFILE& mf, int n, REAL *h_idata, int dimension) {
265 int retval;
266 string resolved_name;
267
268 FILE* f = fopen("temp", "w");
269 if (!f) {
270 return 1;
271 }
272 fprintf(f, "%d", n); //write inversion number
273 fprintf(f, " ");
274 fprintf(f, "%d", dimension); //write dimension
275 fprintf(f, " ");
276 for (int i=0;i<dimension*dimension;++i) {
277 fprintf(f, " ");
278 fprintf(f, "%f", h_idata[i]);
279 }
280 fclose(f);
281 retval = mf.flush();
282 if (retval) {
283 return retval;
284 }
285 boinc_resolve_filename_s(CHECKPOINT_FILE, resolved_name);
286 retval = boinc_rename("temp", resolved_name.c_str());
287
288 if (retval) {
289 return retval;
290 }
291 return 0; //return 0 to indicate success.
292 }
293
294 /*** FUNCTION DEFINITIONS ***/
295
296 /* Create an input file filled with random data of type cl_float. */
generate_random_input_file(int n)297 void generate_random_input_file(int n) {
298 FILE *infile;
299 infile=fopen(INPUT_FILENAME,"w");
300 REAL *h_idata = (REAL *)malloc(sizeof(REAL)*n*n);
301 srand(n);
302 for( int i = 0; i < n; i++ ) {
303 for (int j = 0; j < n; j++) {
304 h_idata[i*n+j] = 2.0*(rand()%32768)/32768.0 - 1.0;
305 }
306 h_idata[i*n+i] += sqrt((float)n);
307 }
308 int j=0;
309 for (int i=0;i<n*n;++i) {
310 fprintf(infile,"%15f",h_idata[i]);
311 if (j+1==n) {
312 fprintf(infile,"\n");
313 j=0;
314 } else {
315 ++j;
316 }
317 }
318 fclose(infile);
319 free(h_idata);
320 }
321
322 /*
323 * Parse the input file and determine the size of the matrix.
324 * This is an nxn matrix. Note: if width <> height, the matrix is
325 * non-invertible.
326 */
get_matrix_dimension(FILE * infile)327 int get_matrix_dimension(FILE *infile) {
328 int w=0;
329 char c;
330
331 fseek(infile,0,SEEK_SET);
332 while (true) {
333 do {
334 c=fgetc(infile);
335 if (c == EOF || c == '\n') {
336 goto exitLoop;
337 }
338 } while (isspace(c));
339
340 if (isdigit(c) || c=='.' || c=='-') {
341 ++w;
342 }
343
344 do {
345 c=fgetc(infile);
346 if (c == EOF || c == '\n') {
347 goto exitLoop;
348 }
349 } while (isdigit(c) || c=='.' || c=='-');
350
351 if (c==EOF || c == '\n') {
352 break;
353 }
354 }
355 exitLoop:
356 return w;
357 }
358
359 /* Read the REAL values from input file into host array. */
fetch_elements_into_host_memory(FILE * infile,REAL * h_idata)360 void fetch_elements_into_host_memory(FILE *infile, REAL *h_idata) {
361 float num=0;
362 int i=0;
363 fseek(infile,0,SEEK_SET);
364 while (fscanf(infile,"%f",&num)==1) {
365 h_idata[i]=num;
366 ++i;
367 }
368 }
369
370 /* Write the result to output file */
print_to_file(MFILE * out,float * h_odata,int dimension)371 void print_to_file(MFILE *out, float *h_odata, int dimension) {
372 int count=0;
373 int move=0;
374 int num_elements=dimension*dimension;
375 while (num_elements>0) {
376 out->printf("%15f ",h_odata[move]);
377 ++count;
378 ++move;
379 if (count==dimension) {
380 out->printf("\n");
381 count=0;
382 }
383 --num_elements;
384 }
385 }
386