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