Merge branch 'release-4-5-patches' into rotation-4-5
[alexxy/gromacs.git] / src / mdlib / gmx_wallcycle.c
1 /*  -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
2  *
3  * 
4  *                This source code is part of
5  * 
6  *                 G   R   O   M   A   C   S
7  * 
8  *          GROningen MAchine for Chemical Simulations
9  * 
10  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12  * Copyright (c) 2001-2008, The GROMACS development team,
13  * check out http://www.gromacs.org for more information.
14  
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  * 
20  * If you want to redistribute modifications, please consider that
21  * scientific software is very special. Version control is crucial -
22  * bugs must be traceable. We will be happy to consider code for
23  * inclusion in the official distribution, but derived work must not
24  * be called official GROMACS. Details are found in the README & COPYING
25  * files - if they are missing, get the official version at www.gromacs.org.
26  * 
27  * To help us fund GROMACS development, we humbly ask that you cite
28  * the papers on the package - you can find them in the top README file.
29  * 
30  * For more info, check our website at http://www.gromacs.org
31  * 
32  * And Hey:
33  * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
34  */
35
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #include <string.h>
42 #include "gmx_wallcycle.h"
43 #include "gmx_cyclecounter.h"
44 #include "smalloc.h"
45 #include "gmx_fatal.h"
46
47 #ifdef GMX_LIB_MPI
48 #include <mpi.h>
49 #endif
50 #ifdef GMX_THREADS
51 #include "tmpi.h"
52 #endif
53
54 typedef struct
55 {
56     int          n;
57     gmx_cycles_t c;
58     gmx_cycles_t start;
59     gmx_cycles_t last;
60 } wallcc_t;
61
62 typedef struct gmx_wallcycle
63 {
64     wallcc_t     *wcc;
65     /* variables for testing/debugging */
66     gmx_bool         wc_barrier;
67     wallcc_t     *wcc_all;
68     int          wc_depth;
69     int          ewc_prev;
70     gmx_cycles_t cycle_prev;
71     gmx_large_int_t   reset_counters;
72 #ifdef GMX_MPI
73     MPI_Comm     mpi_comm_mygroup;
74 #endif
75 } gmx_wallcycle_t_t;
76
77 /* Each name should not exceed 19 characters */
78 static const char *wcn[ewcNR] =
79 { "Run", "Step", "PP during PME", "Domain decomp.", "DD comm. load", "DD comm. bounds", "Vsite constr.", "Send X to PME", "Comm. coord.", "Neighbor search", "Born radii", "Force", "Wait + Comm. F", "PME mesh", "PME redist. X/F", "PME spread/gather", "PME 3D-FFT", "PME solve", "Wait + Comm. X/F", "Wait + Recv. PME F", "Vsite spread", "Write traj.", "Update", "Constraints", "Comm. energies", "Enforced rotation", "Test" };
80
81 gmx_bool wallcycle_have_counter(void)
82 {
83   return gmx_cycles_have_counter();
84 }
85
86 gmx_wallcycle_t wallcycle_init(FILE *fplog,int resetstep,t_commrec *cr)
87 {
88     gmx_wallcycle_t wc;
89     
90     
91     if (!wallcycle_have_counter())
92     {
93         return NULL;
94     }
95
96     snew(wc,1);
97
98     wc->wc_barrier = FALSE;
99     wc->wcc_all    = NULL;
100     wc->wc_depth   = 0;
101     wc->ewc_prev   = -1;
102     wc->reset_counters = resetstep;
103
104 #ifdef GMX_MPI
105     if (PAR(cr) && getenv("GMX_CYCLE_BARRIER") != NULL)
106     {
107         if (fplog) 
108         {
109             fprintf(fplog,"\nWill call MPI_Barrier before each cycle start/stop call\n\n");
110         }
111         wc->wc_barrier = TRUE;
112         wc->mpi_comm_mygroup = cr->mpi_comm_mygroup;
113     }
114 #endif
115
116     snew(wc->wcc,ewcNR);
117     if (getenv("GMX_CYCLE_ALL") != NULL)
118     {
119 /*#ifndef GMX_THREADS*/
120         if (fplog) 
121         {
122             fprintf(fplog,"\nWill time all the code during the run\n\n");
123         }
124         snew(wc->wcc_all,ewcNR*ewcNR);
125 /*#else*/
126         gmx_fatal(FARGS, "GMX_CYCLE_ALL is incompatible with threaded code");
127 /*#endif*/
128     }
129     
130     return wc;
131 }
132
133 void wallcycle_destroy(gmx_wallcycle_t wc)
134 {
135     if (wc == NULL)
136     {
137         return;
138     }
139     
140     if (wc->wcc != NULL)
141     {
142         sfree(wc->wcc);
143     }
144     if (wc->wcc_all != NULL)
145     {
146         sfree(wc->wcc_all);
147     }
148     sfree(wc);
149 }
150
151 static void wallcycle_all_start(gmx_wallcycle_t wc,int ewc,gmx_cycles_t cycle)
152 {
153     wc->ewc_prev = ewc;
154     wc->cycle_prev = cycle;
155 }
156
157 static void wallcycle_all_stop(gmx_wallcycle_t wc,int ewc,gmx_cycles_t cycle)
158 {
159     wc->wcc_all[wc->ewc_prev*ewcNR+ewc].n += 1;
160     wc->wcc_all[wc->ewc_prev*ewcNR+ewc].c += cycle - wc->cycle_prev;
161 }
162
163 void wallcycle_start(gmx_wallcycle_t wc, int ewc)
164 {
165     gmx_cycles_t cycle;
166
167     if (wc == NULL)
168     {
169         return;
170     }
171
172 #ifdef GMX_MPI
173     if (wc->wc_barrier)
174     {
175         MPI_Barrier(wc->mpi_comm_mygroup);
176     }
177 #endif
178
179     cycle = gmx_cycles_read();
180     wc->wcc[ewc].start = cycle;
181     if (wc->wcc_all != NULL)
182     {
183         wc->wc_depth++;
184         if (ewc == ewcRUN)
185         {
186             wallcycle_all_start(wc,ewc,cycle);
187         }
188         else if (wc->wc_depth == 3)
189         {
190             wallcycle_all_stop(wc,ewc,cycle);
191         }
192     }
193 }
194
195 double wallcycle_stop(gmx_wallcycle_t wc, int ewc)
196 {
197     gmx_cycles_t cycle,last;
198     
199     if (wc == NULL)
200     {
201         return 0;
202     }
203     
204 #ifdef GMX_MPI
205     if (wc->wc_barrier)
206     {
207         MPI_Barrier(wc->mpi_comm_mygroup);
208     }
209 #endif
210     
211     cycle = gmx_cycles_read();
212     last = cycle - wc->wcc[ewc].start;
213     wc->wcc[ewc].c += last;
214     wc->wcc[ewc].n++;
215     if (wc->wcc_all)
216     {
217         wc->wc_depth--;
218         if (ewc == ewcRUN)
219         {
220             wallcycle_all_stop(wc,ewc,cycle);
221         }
222         else if (wc->wc_depth == 2)
223         {
224             wallcycle_all_start(wc,ewc,cycle);
225         }
226     }
227
228     return last;
229 }
230
231 void wallcycle_reset_all(gmx_wallcycle_t wc)
232 {
233     int i;
234
235     if (wc == NULL)
236     {
237         return;
238     }
239
240     for(i=0; i<ewcNR; i++)
241     {
242         wc->wcc[i].n = 0;
243         wc->wcc[i].c = 0;
244         wc->wcc[i].start = 0;
245         wc->wcc[i].last = 0;
246     }
247 }
248
249 void wallcycle_sum(t_commrec *cr, gmx_wallcycle_t wc,double cycles[])
250 {
251     wallcc_t *wcc;
252     double cycles_n[ewcNR],buf[ewcNR],*cyc_all,*buf_all;
253     int    i;
254
255     if (wc == NULL)
256     {
257         return;
258     }
259
260     wcc = wc->wcc;
261
262     if (wcc[ewcDDCOMMLOAD].n > 0)
263     {
264         wcc[ewcDOMDEC].c -= wcc[ewcDDCOMMLOAD].c;
265     }
266     if (wcc[ewcDDCOMMBOUND].n > 0)
267     {
268         wcc[ewcDOMDEC].c -= wcc[ewcDDCOMMBOUND].c;
269     }
270     if (cr->npmenodes == 0)
271     {
272         /* All nodes do PME (or no PME at all) */
273         if (wcc[ewcPMEMESH].n > 0)
274         {
275             wcc[ewcFORCE].c -= wcc[ewcPMEMESH].c;
276         }
277     }
278     else
279     {
280         /* The are PME-only nodes */
281         if (wcc[ewcPMEMESH].n > 0)
282         {
283             /* This must be a PME only node, calculate the Wait + Comm. time */
284             wcc[ewcPMEWAITCOMM].c = wcc[ewcRUN].c - wcc[ewcPMEMESH].c;
285         }
286     }
287     
288     /* Store the cycles in a double buffer for summing */
289     for(i=0; i<ewcNR; i++)
290     {
291         cycles_n[i] = (double)wcc[i].n;
292         cycles[i]   = (double)wcc[i].c;
293     }
294     
295 #ifdef GMX_MPI
296     if (cr->nnodes > 1)
297     {
298         MPI_Allreduce(cycles_n,buf,ewcNR,MPI_DOUBLE,MPI_MAX,
299                       cr->mpi_comm_mysim);
300         for(i=0; i<ewcNR; i++)
301         {
302             wcc[i].n = (int)(buf[i] + 0.5);
303         }
304         MPI_Allreduce(cycles,buf,ewcNR,MPI_DOUBLE,MPI_SUM,
305                       cr->mpi_comm_mysim);
306         for(i=0; i<ewcNR; i++)
307         {
308             cycles[i] = buf[i];
309         }
310
311         if (wc->wcc_all != NULL)
312         {
313             snew(cyc_all,ewcNR*ewcNR);
314             snew(buf_all,ewcNR*ewcNR);
315             for(i=0; i<ewcNR*ewcNR; i++)
316             {
317                 cyc_all[i] = wc->wcc_all[i].c;
318             }
319             MPI_Allreduce(cyc_all,buf_all,ewcNR*ewcNR,MPI_DOUBLE,MPI_SUM,
320                           cr->mpi_comm_mysim);
321             for(i=0; i<ewcNR*ewcNR; i++)
322             {
323                 wc->wcc_all[i].c = buf_all[i];
324             }
325             sfree(buf_all);
326             sfree(cyc_all);
327         }
328     }
329 #endif
330 }
331
332 static void print_cycles(FILE *fplog, double c2t, const char *name, int nnodes,
333                          int n, double c, double tot)
334 {
335     char num[11];
336   
337     if (c > 0)
338     {
339         if (n > 0)
340         {
341             sprintf(num,"%10d",n);
342         }
343         else
344         {
345             sprintf(num,"          ");
346         }
347         fprintf(fplog," %-19s %4d %10s %12.3f %10.1f   %5.1f\n",
348                 name,nnodes,num,c*1e-9,c*c2t,100*c/tot);
349     }
350 }
351
352 static gmx_bool subdivision(int ewc)
353 {
354     return (ewc >= ewcPME_REDISTXF && ewc <= ewcPME_SOLVE);
355 }
356
357 void wallcycle_print(FILE *fplog, int nnodes, int npme, double realtime,
358                      gmx_wallcycle_t wc, double cycles[])
359 {
360     double c2t,tot,sum;
361     int    i,j,npp;
362     char   buf[STRLEN];
363     const char *myline = "-----------------------------------------------------------------------";
364     
365     if (wc == NULL)
366     {
367         return;
368     }
369
370     if (npme > 0)
371     {
372         npp = nnodes - npme;
373     }
374     else
375     {
376         npp  = nnodes;
377         npme = nnodes;
378     }
379     tot = cycles[ewcRUN];
380     /* Conversion factor from cycles to seconds */
381     if (tot > 0)
382     {
383       c2t = nnodes*realtime/tot;
384     }
385     else
386     {
387       c2t = 0;
388     }
389
390     fprintf(fplog,"\n     R E A L   C Y C L E   A N D   T I M E   A C C O U N T I N G\n\n");
391
392     fprintf(fplog," Computing:         Nodes     Number     G-Cycles    Seconds     %c\n",'%');
393     fprintf(fplog,"%s\n",myline);
394     sum = 0;
395     for(i=ewcPPDURINGPME+1; i<ewcNR; i++)
396     {
397         if (!subdivision(i))
398         {
399             print_cycles(fplog,c2t,wcn[i],
400                          (i==ewcPMEMESH || i==ewcPMEWAITCOMM) ? npme : npp,
401                          wc->wcc[i].n,cycles[i],tot);
402             sum += cycles[i];
403         }
404     }
405     if (wc->wcc_all != NULL)
406     {
407         for(i=0; i<ewcNR; i++)
408         {
409             for(j=0; j<ewcNR; j++)
410             {
411                 sprintf(buf,"%-9s",wcn[i]);
412                 buf[9] = ' ';
413                 sprintf(buf+10,"%-9s",wcn[j]);
414                 buf[19] = '\0';
415                 print_cycles(fplog,c2t,buf,
416                              (i==ewcPMEMESH || i==ewcPMEWAITCOMM) ? npme : npp,
417                              wc->wcc_all[i*ewcNR+j].n,
418                              wc->wcc_all[i*ewcNR+j].c,
419                              tot);
420             }
421         }
422     }
423     print_cycles(fplog,c2t,"Rest",npp,0,tot-sum,tot);
424     fprintf(fplog,"%s\n",myline);
425     print_cycles(fplog,c2t,"Total",nnodes,0,tot,tot);
426     fprintf(fplog,"%s\n",myline);
427     
428     if (wc->wcc[ewcPMEMESH].n > 0)
429     {
430         fprintf(fplog,"%s\n",myline);
431         for(i=ewcPPDURINGPME+1; i<ewcNR; i++)
432         {
433             if (subdivision(i))
434             {
435                 print_cycles(fplog,c2t,wcn[i],
436                              (i>=ewcPMEMESH || i<=ewcPME_SOLVE) ? npme : npp,
437                              wc->wcc[i].n,cycles[i],tot);
438             }
439         }
440         fprintf(fplog,"%s\n",myline);
441     }
442
443     if (cycles[ewcMoveE] > tot*0.05)
444     {
445         sprintf(buf,
446                 "NOTE: %d %% of the run time was spent communicating energies,\n"
447                 "      you might want to use the -gcom option of mdrun\n",
448                 (int)(100*cycles[ewcMoveE]/tot+0.5));
449         if (fplog)
450         {
451             fprintf(fplog,"\n%s\n",buf);
452         }
453         /* Only the sim master calls this function, so always print to stderr */
454         fprintf(stderr,"\n%s\n",buf);
455     }
456 }
457
458 extern gmx_large_int_t wcycle_get_reset_counters(gmx_wallcycle_t wc)
459 {
460     if (wc == NULL)
461     {
462         return -1;
463     }
464     
465     return wc->reset_counters;
466 }
467
468 extern void wcycle_set_reset_counters(gmx_wallcycle_t wc, gmx_large_int_t reset_counters)
469 {
470     if (wc == NULL)
471         return;
472
473     wc->reset_counters = reset_counters;
474 }