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