added Verlet scheme and NxN non-bonded functionality
[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_THREAD_MPI
51 #include "tmpi.h"
52 #endif
53
54 /* DEBUG_WCYCLE adds consistency checking for the counters.
55  * It checks if you stop a counter different from the last
56  * one that was opened and if you do nest too deep.
57  */
58 /* #define DEBUG_WCYCLE */
59
60 typedef struct
61 {
62     int          n;
63     gmx_cycles_t c;
64     gmx_cycles_t start;
65     gmx_cycles_t last;
66 } wallcc_t;
67
68 typedef struct gmx_wallcycle
69 {
70     wallcc_t     *wcc;
71     /* variables for testing/debugging */
72     gmx_bool         wc_barrier;
73     wallcc_t     *wcc_all;
74     int          wc_depth;
75 #ifdef DEBUG_WCYCLE
76 #define DEPTH_MAX 6
77     int          counterlist[DEPTH_MAX];
78     int          count_depth;
79 #endif
80     int          ewc_prev;
81     gmx_cycles_t cycle_prev;
82     gmx_large_int_t   reset_counters;
83 #ifdef GMX_MPI
84     MPI_Comm     mpi_comm_mygroup;
85 #endif
86     int          nthreads_pp;
87     int          nthreads_pme;
88 #ifdef GMX_CYCLE_SUBCOUNTERS
89     wallcc_t     *wcsc;
90 #endif
91     double       *cycles_sum;
92 } gmx_wallcycle_t_t;
93
94 /* Each name should not exceed 19 characters */
95 static const char *wcn[ewcNR] =
96 { "Run", "Step", "PP during PME", "Domain decomp.", "DD comm. load",
97   "DD comm. bounds", "Vsite constr.", "Send X to PME", "Neighbor search", "Launch GPU ops.",
98   "Comm. coord.", "Born radii", "Force", "Wait + Comm. F", "PME mesh",
99   "PME redist. X/F", "PME spread/gather", "PME 3D-FFT", "PME 3D-FFT Comm.", "PME solve",
100   "PME wait for PP", "Wait + Recv. PME F", "Wait GPU nonlocal", "Wait GPU local", "NB X/F buffer ops.",
101   "Vsite spread", "Write traj.", "Update", "Constraints", "Comm. energies",
102   "Enforced rotation", "Add rot. forces", "Test" };
103
104 static const char *wcsn[ewcsNR] =
105 { "DD redist.", "DD NS grid + sort", "DD setup comm.",
106   "DD make top.", "DD make constr.", "DD top. other",
107   "NS grid local", "NS grid non-loc.", "NS search local", "NS search non-loc.",
108   "Bonded F", "Nonbonded F", "Ewald F correction",
109   "NB X buffer ops.", "NB F buffer ops."
110 };
111
112 gmx_bool wallcycle_have_counter(void)
113 {
114   return gmx_cycles_have_counter();
115 }
116
117 gmx_wallcycle_t wallcycle_init(FILE *fplog,int resetstep,t_commrec *cr, 
118                                int nthreads_pp, int nthreads_pme)
119 {
120     gmx_wallcycle_t wc;
121     
122     
123     if (!wallcycle_have_counter())
124     {
125         return NULL;
126     }
127
128     snew(wc,1);
129
130     wc->wc_barrier          = FALSE;
131     wc->wcc_all             = NULL;
132     wc->wc_depth            = 0;
133     wc->ewc_prev            = -1;
134     wc->reset_counters      = resetstep;
135     wc->nthreads_pp         = nthreads_pp;
136     wc->nthreads_pme        = nthreads_pme;
137     wc->cycles_sum          = NULL;
138
139 #ifdef GMX_MPI
140     if (PAR(cr) && getenv("GMX_CYCLE_BARRIER") != NULL)
141     {
142         if (fplog) 
143         {
144             fprintf(fplog,"\nWill call MPI_Barrier before each cycle start/stop call\n\n");
145         }
146         wc->wc_barrier = TRUE;
147         wc->mpi_comm_mygroup = cr->mpi_comm_mygroup;
148     }
149 #endif
150
151     snew(wc->wcc,ewcNR);
152     if (getenv("GMX_CYCLE_ALL") != NULL)
153     {
154         if (fplog) 
155         {
156             fprintf(fplog,"\nWill time all the code during the run\n\n");
157         }
158         snew(wc->wcc_all,ewcNR*ewcNR);
159     }
160
161 #ifdef GMX_CYCLE_SUBCOUNTERS
162     snew(wc->wcsc,ewcsNR);
163 #endif
164
165 #ifdef DEBUG_WCYCLE
166     wc->count_depth = 0;
167 #endif
168
169     return wc;
170 }
171
172 void wallcycle_destroy(gmx_wallcycle_t wc)
173 {
174     if (wc == NULL)
175     {
176         return;
177     }
178     
179     if (wc->wcc != NULL)
180     {
181         sfree(wc->wcc);
182     }
183     if (wc->wcc_all != NULL)
184     {
185         sfree(wc->wcc_all);
186     }
187 #ifdef GMX_CYCLE_SUBCOUNTERS
188     if (wc->wcsc != NULL)
189     {
190         sfree(wc->wcsc);
191     }
192 #endif
193     sfree(wc);
194 }
195
196 static void wallcycle_all_start(gmx_wallcycle_t wc,int ewc,gmx_cycles_t cycle)
197 {
198     wc->ewc_prev = ewc;
199     wc->cycle_prev = cycle;
200 }
201
202 static void wallcycle_all_stop(gmx_wallcycle_t wc,int ewc,gmx_cycles_t cycle)
203 {
204     wc->wcc_all[wc->ewc_prev*ewcNR+ewc].n += 1;
205     wc->wcc_all[wc->ewc_prev*ewcNR+ewc].c += cycle - wc->cycle_prev;
206 }
207
208
209 #ifdef DEBUG_WCYCLE
210 static void debug_start_check(gmx_wallcycle_t wc, int ewc)
211 {
212     /* fprintf(stderr,"wcycle_start depth %d, %s\n",wc->count_depth,wcn[ewc]); */
213
214     if (wc->count_depth < 0 || wc->count_depth >= DEPTH_MAX)
215     {
216         gmx_fatal(FARGS,"wallcycle counter depth out of range: %d",
217                   wc->count_depth);
218     }
219     wc->counterlist[wc->count_depth] = ewc;
220     wc->count_depth++;
221 }
222
223 static void debug_stop_check(gmx_wallcycle_t wc, int ewc)
224 {
225     wc->count_depth--;
226
227     /* fprintf(stderr,"wcycle_stop depth %d, %s\n",wc->count_depth,wcn[ewc]); */
228
229     if (wc->count_depth < 0)
230     {
231         gmx_fatal(FARGS,"wallcycle counter depth out of range when stopping %s: %d",wcn[ewc],wc->count_depth);
232     }
233     if (wc->counterlist[wc->count_depth] != ewc)
234     {
235         gmx_fatal(FARGS,"wallcycle mismatch at stop, start %s, stop %s",
236                   wcn[wc->counterlist[wc->count_depth]],wcn[ewc]);
237     }
238 }
239 #endif
240
241 void wallcycle_start(gmx_wallcycle_t wc, int ewc)
242 {
243     gmx_cycles_t cycle;
244
245     if (wc == NULL)
246     {
247         return;
248     }
249
250 #ifdef GMX_MPI
251     if (wc->wc_barrier)
252     {
253         MPI_Barrier(wc->mpi_comm_mygroup);
254     }
255 #endif
256
257 #ifdef DEBUG_WCYCLE
258     debug_start_check(wc,ewc);
259 #endif
260
261     cycle = gmx_cycles_read();
262     wc->wcc[ewc].start = cycle;
263     if (wc->wcc_all != NULL)
264     {
265         wc->wc_depth++;
266         if (ewc == ewcRUN)
267         {
268             wallcycle_all_start(wc,ewc,cycle);
269         }
270         else if (wc->wc_depth == 3)
271         {
272             wallcycle_all_stop(wc,ewc,cycle);
273         }
274     }
275 }
276
277 void wallcycle_start_nocount(gmx_wallcycle_t wc, int ewc)
278 {
279     if (wc == NULL)
280     {
281         return;
282     }
283
284     wallcycle_start(wc, ewc);
285     wc->wcc[ewc].n--;
286 }
287
288 double wallcycle_stop(gmx_wallcycle_t wc, int ewc)
289 {
290     gmx_cycles_t cycle,last;
291     
292     if (wc == NULL)
293     {
294         return 0;
295     }
296     
297 #ifdef GMX_MPI
298     if (wc->wc_barrier)
299     {
300         MPI_Barrier(wc->mpi_comm_mygroup);
301     }
302 #endif
303
304 #ifdef DEBUG_WCYCLE
305     debug_stop_check(wc,ewc);
306 #endif
307     
308     cycle = gmx_cycles_read();
309     last = cycle - wc->wcc[ewc].start;
310     wc->wcc[ewc].c += last;
311     wc->wcc[ewc].n++;
312     if (wc->wcc_all)
313     {
314         wc->wc_depth--;
315         if (ewc == ewcRUN)
316         {
317             wallcycle_all_stop(wc,ewc,cycle);
318         }
319         else if (wc->wc_depth == 2)
320         {
321             wallcycle_all_start(wc,ewc,cycle);
322         }
323     }
324
325     return last;
326 }
327
328 void wallcycle_reset_all(gmx_wallcycle_t wc)
329 {
330     int i;
331
332     if (wc == NULL)
333     {
334         return;
335     }
336
337     for(i=0; i<ewcNR; i++)
338     {
339         wc->wcc[i].n = 0;
340         wc->wcc[i].c = 0;
341     }
342     if (wc->wcc_all)
343     {
344         for(i=0; i<ewcNR*ewcNR; i++)
345         {
346             wc->wcc_all[i].n = 0;
347             wc->wcc_all[i].c = 0;
348         }
349     }
350 #ifdef GMX_CYCLE_SUBCOUNTERS
351     for (i=0; i<ewcsNR; i++)
352     {
353         wc->wcsc[i].n = 0;
354         wc->wcsc[i].c = 0;
355     }
356 #endif
357 }
358
359 static gmx_bool is_pme_counter(int ewc)
360 {
361     return (ewc >= ewcPMEMESH && ewc <= ewcPMEWAITCOMM);
362 }
363
364 static gmx_bool is_pme_subcounter(int ewc)
365 {
366     return (ewc >= ewcPME_REDISTXF && ewc < ewcPMEWAITCOMM);
367 }
368
369 void wallcycle_sum(t_commrec *cr, gmx_wallcycle_t wc)
370 {
371     wallcc_t *wcc;
372     double *cycles;
373     double cycles_n[ewcNR+ewcsNR],buf[ewcNR+ewcsNR],*cyc_all,*buf_all;
374     int    i,j;
375     int    nsum;
376
377     if (wc == NULL)
378     {
379         return;
380     }
381
382     snew(wc->cycles_sum,ewcNR+ewcsNR);
383     cycles = wc->cycles_sum;
384
385     wcc = wc->wcc;
386
387     for(i=0; i<ewcNR; i++)
388     {
389         if (is_pme_counter(i) || (i==ewcRUN && cr->duty == DUTY_PME))
390         {
391             wcc[i].c *= wc->nthreads_pme;
392
393             if (wc->wcc_all)
394             {
395                 for(j=0; j<ewcNR; j++)
396                 {
397                     wc->wcc_all[i*ewcNR+j].c *= wc->nthreads_pme;
398                 }
399             }
400         }
401         else
402         {
403             wcc[i].c *= wc->nthreads_pp;
404
405             if (wc->wcc_all)
406             {
407                 for(j=0; j<ewcNR; j++)
408                 {
409                     wc->wcc_all[i*ewcNR+j].c *= wc->nthreads_pp;
410                 }
411             }
412         }
413     }
414
415     if (wcc[ewcDDCOMMLOAD].n > 0)
416     {
417         wcc[ewcDOMDEC].c -= wcc[ewcDDCOMMLOAD].c;
418     }
419     if (wcc[ewcDDCOMMBOUND].n > 0)
420     {
421         wcc[ewcDOMDEC].c -= wcc[ewcDDCOMMBOUND].c;
422     }
423     if (wcc[ewcPME_FFTCOMM].n > 0)
424     {
425         wcc[ewcPME_FFT].c -= wcc[ewcPME_FFTCOMM].c;
426     }
427
428     if (cr->npmenodes == 0)
429     {
430         /* All nodes do PME (or no PME at all) */
431         if (wcc[ewcPMEMESH].n > 0)
432         {
433             wcc[ewcFORCE].c -= wcc[ewcPMEMESH].c;
434         }
435     }
436     else
437     {
438         /* The are PME-only nodes */
439         if (wcc[ewcPMEMESH].n > 0)
440         {
441             /* This must be a PME only node, calculate the Wait + Comm. time */
442             wcc[ewcPMEWAITCOMM].c = wcc[ewcRUN].c - wcc[ewcPMEMESH].c;
443         }
444     }
445     
446     /* Store the cycles in a double buffer for summing */
447     for(i=0; i<ewcNR; i++)
448     {
449         cycles_n[i] = (double)wcc[i].n;
450         cycles[i]   = (double)wcc[i].c;
451     }
452     nsum = ewcNR;
453 #ifdef GMX_CYCLE_SUBCOUNTERS
454     for(i=0; i<ewcsNR; i++)
455     {
456         wc->wcsc[i].c *= wc->nthreads_pp;
457         cycles_n[ewcNR+i] = (double)wc->wcsc[i].n;
458         cycles[ewcNR+i]   = (double)wc->wcsc[i].c;
459     }
460     nsum += ewcsNR;
461 #endif   
462     
463 #ifdef GMX_MPI
464     if (cr->nnodes > 1)
465     {
466         MPI_Allreduce(cycles_n,buf,nsum,MPI_DOUBLE,MPI_MAX,
467                       cr->mpi_comm_mysim);
468         for(i=0; i<ewcNR; i++)
469         {
470             wcc[i].n = (int)(buf[i] + 0.5);
471         }
472 #ifdef GMX_CYCLE_SUBCOUNTERS
473         for(i=0; i<ewcsNR; i++)
474         {
475             wc->wcsc[i].n = (int)(buf[ewcNR+i] + 0.5);
476         }
477 #endif   
478
479         MPI_Allreduce(cycles,buf,nsum,MPI_DOUBLE,MPI_SUM,
480                       cr->mpi_comm_mysim);
481         for(i=0; i<nsum; i++)
482         {
483             cycles[i] = buf[i];
484         }
485
486         if (wc->wcc_all != NULL)
487         {
488             snew(cyc_all,ewcNR*ewcNR);
489             snew(buf_all,ewcNR*ewcNR);
490             for(i=0; i<ewcNR*ewcNR; i++)
491             {
492                 cyc_all[i] = wc->wcc_all[i].c;
493             }
494             MPI_Allreduce(cyc_all,buf_all,ewcNR*ewcNR,MPI_DOUBLE,MPI_SUM,
495                           cr->mpi_comm_mysim);
496             for(i=0; i<ewcNR*ewcNR; i++)
497             {
498                 wc->wcc_all[i].c = buf_all[i];
499             }
500             sfree(buf_all);
501             sfree(cyc_all);
502         }
503     }
504 #endif
505 }
506
507 static void print_cycles(FILE *fplog, double c2t, const char *name, 
508                          int nnodes_tot,int nnodes, int nthreads,
509                          int n, double c, double tot)
510 {
511     char num[11];
512     char thstr[6];
513     double wallt;
514   
515     if (c > 0)
516     {
517         if (n > 0)
518         {
519             sprintf(num,"%10d",n);
520             if (nthreads < 0)
521                 sprintf(thstr, "N/A");
522             else
523                 sprintf(thstr, "%4d", nthreads);
524         }
525         else
526         {
527             sprintf(num,"          ");
528             sprintf(thstr, "    ");
529         }
530         wallt = c*c2t*nnodes_tot/(double)nnodes;
531         fprintf(fplog," %-19s %4d %4s %10s  %10.3f %12.3f   %5.1f\n",
532                 name,nnodes,thstr,num,wallt,c*1e-9,100*c/tot);
533     }
534 }
535
536 static void print_gputimes(FILE *fplog, const char *name, 
537                            int n, double t, double tot_t)
538 {
539     char num[11];
540     char avg_perf[11];
541
542     if (n > 0)
543     {
544         sprintf(num, "%10d", n);
545         sprintf(avg_perf, "%10.3f", t/n);
546     }
547     else
548     {
549       sprintf(num,"          ");
550       sprintf(avg_perf,"          ");
551     }
552     if (t != tot_t)
553     {
554         fprintf(fplog, " %-29s %10s%12.3f   %s   %5.1f\n",
555                 name, num, t/1000, avg_perf, 100 * t/tot_t); 
556     }
557     else
558     {
559          fprintf(fplog, " %-29s %10s%12.3f   %s   %5.1f\n",
560                name, "", t/1000, avg_perf, 100.0); 
561     }
562 }
563
564 void wallcycle_print(FILE *fplog, int nnodes, int npme, double realtime,
565                      gmx_wallcycle_t wc, wallclock_gpu_t *gpu_t)
566 {
567     double *cycles;
568     double c2t,tot,tot_gpu,tot_cpu_overlap,gpu_cpu_ratio,sum,tot_k;
569     int    i,j,npp,nth_pp,nth_pme;
570     char   buf[STRLEN];
571     const char *hline = "-----------------------------------------------------------------------------";
572     
573     if (wc == NULL)
574     {
575         return;
576     }
577
578     nth_pp  = wc->nthreads_pp;
579     nth_pme = wc->nthreads_pme;
580
581     cycles = wc->cycles_sum;
582
583     if (npme > 0)
584     {
585         npp = nnodes - npme;
586     }
587     else
588     {
589         npp  = nnodes;
590         npme = nnodes;
591     }
592     tot = cycles[ewcRUN];
593
594     /* Conversion factor from cycles to seconds */
595     if (tot > 0)
596     {
597         c2t = realtime/tot;
598     }
599     else
600     {
601         c2t = 0;
602     }
603
604     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");
605
606     fprintf(fplog," Computing:         Nodes   Th.     Count  Wall t (s)     G-Cycles       %c\n",'%');
607     fprintf(fplog,"%s\n",hline);
608     sum = 0;
609     for(i=ewcPPDURINGPME+1; i<ewcNR; i++)
610     {
611         if (!is_pme_subcounter(i))
612         {
613             print_cycles(fplog,c2t,wcn[i],nnodes,
614                          is_pme_counter(i) ? npme : npp,
615                          is_pme_counter(i) ? nth_pme : nth_pp, 
616                          wc->wcc[i].n,cycles[i],tot);
617             sum += cycles[i];
618         }
619     }
620     if (wc->wcc_all != NULL)
621     {
622         for(i=0; i<ewcNR; i++)
623         {
624             for(j=0; j<ewcNR; j++)
625             {
626                 sprintf(buf,"%-9s",wcn[i]);
627                 buf[9] = ' ';
628                 sprintf(buf+10,"%-9s",wcn[j]);
629                 buf[19] = '\0';
630                 print_cycles(fplog,c2t,buf,nnodes,
631                              is_pme_counter(i) ? npme : npp,
632                              is_pme_counter(i) ? nth_pme : nth_pp,
633                              wc->wcc_all[i*ewcNR+j].n,
634                              wc->wcc_all[i*ewcNR+j].c,
635                              tot);
636             }
637         }
638     }
639     print_cycles(fplog,c2t,"Rest",npp,npp,-1,0,tot-sum,tot);
640     fprintf(fplog,"%s\n",hline);
641     print_cycles(fplog,c2t,"Total",nnodes,nnodes,-1,0,tot,tot);
642     fprintf(fplog,"%s\n",hline);
643     
644     if (wc->wcc[ewcPMEMESH].n > 0)
645     {
646         fprintf(fplog,"%s\n",hline);
647         for(i=ewcPPDURINGPME+1; i<ewcNR; i++)
648         {
649             if (is_pme_subcounter(i))
650             {
651                 print_cycles(fplog,c2t,wcn[i],nnodes,
652                              is_pme_counter(i) ? npme : npp,
653                              is_pme_counter(i) ? nth_pme : nth_pp,
654                              wc->wcc[i].n,cycles[i],tot);
655             }
656         }
657         fprintf(fplog,"%s\n",hline);
658     }
659
660 #ifdef GMX_CYCLE_SUBCOUNTERS
661     fprintf(fplog,"%s\n",hline);
662     for(i=0; i<ewcsNR; i++)
663     {
664         print_cycles(fplog,c2t,wcsn[i],nnodes,npp,nth_pp,
665                      wc->wcsc[i].n,cycles[ewcNR+i],tot);
666     }
667     fprintf(fplog,"%s\n",hline);
668 #endif
669
670     /* print GPU timing summary */
671     if (gpu_t)
672     {
673         const char *k_log_str[2][2] = {
674                 {"Nonbonded F kernel", "Nonbonded F+ene k."},
675                 {"Nonbonded F+prune k.", "Nonbonded F+ene+prune k."}};
676
677         tot_gpu = gpu_t->pl_h2d_t + gpu_t->nb_h2d_t + gpu_t->nb_d2h_t;
678
679         /* add up the kernel timings */
680         tot_k = 0.0;
681         for (i = 0; i < 2; i++)
682         {
683             for(j = 0; j < 2; j++)
684             {
685                 tot_k += gpu_t->ktime[i][j].t;
686             }
687         }
688         tot_gpu += tot_k;
689     
690         tot_cpu_overlap = wc->wcc[ewcFORCE].c;
691         if (wc->wcc[ewcPMEMESH].n > 0)
692         {
693             tot_cpu_overlap += wc->wcc[ewcPMEMESH].c;
694         }
695         tot_cpu_overlap *= c2t * 1000; /* convert s to ms */
696
697         fprintf(fplog, "\n GPU timings\n%s\n", hline);
698         fprintf(fplog," Computing:                         Count  Wall t (s)      ms/step       %c\n",'%');
699         fprintf(fplog, "%s\n", hline);
700         print_gputimes(fplog, "Pair list H2D",
701                 gpu_t->pl_h2d_c, gpu_t->pl_h2d_t, tot_gpu);
702          print_gputimes(fplog, "X / q H2D", 
703                 gpu_t->nb_c, gpu_t->nb_h2d_t, tot_gpu);
704
705         for (i = 0; i < 2; i++)
706         {
707             for(j = 0; j < 2; j++)
708             {
709                 if (gpu_t->ktime[i][j].c)
710                 {
711                     print_gputimes(fplog, k_log_str[i][j],
712                             gpu_t->ktime[i][j].c, gpu_t->ktime[i][j].t, tot_gpu);
713                 }
714             }
715         }        
716
717         print_gputimes(fplog, "F D2H",  gpu_t->nb_c, gpu_t->nb_d2h_t, tot_gpu);
718         fprintf(fplog, "%s\n", hline);
719         print_gputimes(fplog, "Total ", gpu_t->nb_c, tot_gpu, tot_gpu);
720         fprintf(fplog, "%s\n", hline);
721
722         gpu_cpu_ratio = tot_gpu/tot_cpu_overlap;
723         fprintf(fplog, "\n Force evaluation time GPU/CPU: %.3f ms/%.3f ms = %.3f\n",
724                 tot_gpu/gpu_t->nb_c, tot_cpu_overlap/wc->wcc[ewcFORCE].n,
725                 gpu_cpu_ratio);
726
727         /* only print notes related to CPU-GPU load balance with PME */
728         if (wc->wcc[ewcPMEMESH].n > 0)
729         {
730             fprintf(fplog, "For optimal performance this ratio should be close to 1!\n");
731
732             /* print note if the imbalance is high with PME case in which
733              * CPU-GPU load balancing is possible */
734             if (gpu_cpu_ratio < 0.75 || gpu_cpu_ratio > 1.2)
735             {
736                 if (gpu_cpu_ratio < 0.75)
737                 {
738                     sprintf(buf, "NOTE: The GPU has >25%% less load than the CPU. This imbalance causes\n"
739                             "      performance loss, consider turning on PME tuning (-tunepme).");
740                 }
741                 if (gpu_cpu_ratio > 1.2)
742                 {
743                     sprintf(buf, "NOTE: The GPU has >20%% more load than the CPU. This imbalance causes\n"
744                             "      performance loss, consider using a shorter cut-off.");
745                 }
746                 if (fplog)
747                 {
748                     fprintf(fplog,"\n%s\n",buf);
749                 }
750                 fprintf(stderr,"\n\n%s\n",buf);
751             }
752         }
753     }
754
755     if (wc->wcc[ewcNB_XF_BUF_OPS].n > 0 &&
756         (cycles[ewcDOMDEC] > tot*0.1 ||
757          cycles[ewcNS] > tot*0.1))
758     {
759         if (wc->wcc[ewcDOMDEC].n == 0)
760         {
761             sprintf(buf,
762                     "NOTE: %d %% of the run time was spent in pair search,\n"
763                     "      you might want to increase nstlist (this has no effect on accuracy)\n",
764                     (int)(100*cycles[ewcNS]/tot+0.5));
765         }
766         else
767         {
768             sprintf(buf,
769                     "NOTE: %d %% of the run time was spent in domain decomposition,\n"
770                     "      %d %% of the run time was spent in pair search,\n"
771                     "      you might want to increase nstlist (this has no effect on accuracy)\n",
772                     (int)(100*cycles[ewcDOMDEC]/tot+0.5),
773                     (int)(100*cycles[ewcNS]/tot+0.5));
774         }
775         if (fplog)
776         {
777             fprintf(fplog,"\n%s\n",buf);
778         }
779         /* Only the sim master calls this function, so always print to stderr */
780         fprintf(stderr,"\n%s\n",buf);
781     }
782
783     if (cycles[ewcMoveE] > tot*0.05)
784     {
785         sprintf(buf,
786                 "NOTE: %d %% of the run time was spent communicating energies,\n"
787                 "      you might want to use the -gcom option of mdrun\n",
788                 (int)(100*cycles[ewcMoveE]/tot+0.5));
789         if (fplog)
790         {
791             fprintf(fplog,"\n%s\n",buf);
792         }
793         /* Only the sim master calls this function, so always print to stderr */
794         fprintf(stderr,"\n%s\n",buf);
795     }
796 }
797
798 extern gmx_large_int_t wcycle_get_reset_counters(gmx_wallcycle_t wc)
799 {
800     if (wc == NULL)
801     {
802         return -1;
803     }
804     
805     return wc->reset_counters;
806 }
807
808 extern void wcycle_set_reset_counters(gmx_wallcycle_t wc, gmx_large_int_t reset_counters)
809 {
810     if (wc == NULL)
811         return;
812
813     wc->reset_counters = reset_counters;
814 }
815
816 #ifdef GMX_CYCLE_SUBCOUNTERS
817
818 void wallcycle_sub_start(gmx_wallcycle_t wc, int ewcs)
819 {
820     if (wc != NULL)
821     {
822         wc->wcsc[ewcs].start = gmx_cycles_read();
823     }
824 }
825
826 void wallcycle_sub_stop(gmx_wallcycle_t wc, int ewcs)
827 {
828     if (wc != NULL)
829     {
830         wc->wcsc[ewcs].c += gmx_cycles_read() - wc->wcsc[ewcs].start;
831         wc->wcsc[ewcs].n++;
832     }
833 }
834
835 #endif /* GMX_CYCLE_SUBCOUNTERS */