Sort all includes in src/gromacs
[alexxy/gromacs.git] / src / gromacs / timing / wallcycle.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2008, The GROMACS development team.
6  * Copyright (c) 2013,2014, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #include "gmxpre.h"
38
39 #include "wallcycle.h"
40
41 #include "config.h"
42
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include "gromacs/legacyheaders/md_logging.h"
47 #include "gromacs/legacyheaders/types/commrec.h"
48 #include "gromacs/timing/cyclecounter.h"
49 #include "gromacs/utility/cstringutil.h"
50 #include "gromacs/utility/fatalerror.h"
51 #include "gromacs/utility/gmxmpi.h"
52 #include "gromacs/utility/smalloc.h"
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_int64_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 printing characters
95    (ie. terminating null can be twentieth) */
96 static const char *wcn[ewcNR] =
97 {
98     "Run", "Step", "PP during PME", "Domain decomp.", "DD comm. load",
99     "DD comm. bounds", "Vsite constr.", "Send X to PME", "Neighbor search", "Launch GPU ops.",
100     "Comm. coord.", "Born radii", "Force", "Wait + Comm. F", "PME mesh",
101     "PME redist. X/F", "PME spread/gather", "PME 3D-FFT", "PME 3D-FFT Comm.", "PME solve LJ", "PME solve Elec",
102     "PME wait for PP", "Wait + Recv. PME F", "Wait GPU nonlocal", "Wait GPU local", "NB X/F buffer ops.",
103     "Vsite spread", "COM pull force",
104     "Write traj.", "Update", "Constraints", "Comm. energies",
105     "Enforced rotation", "Add rot. forces", "Coordinate swapping", "IMD", "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 gmx_unused *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[ewcNR+ewcsNR];
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
389     wcc = wc->wcc;
390
391     for (i = 0; i < ewcNR; i++)
392     {
393         if (is_pme_counter(i) || (i == ewcRUN && cr->duty == DUTY_PME))
394         {
395             wcc[i].c *= wc->nthreads_pme;
396
397             if (wc->wcc_all)
398             {
399                 for (j = 0; j < ewcNR; j++)
400                 {
401                     wc->wcc_all[i*ewcNR+j].c *= wc->nthreads_pme;
402                 }
403             }
404         }
405         else
406         {
407             wcc[i].c *= wc->nthreads_pp;
408
409             if (wc->wcc_all)
410             {
411                 for (j = 0; j < ewcNR; j++)
412                 {
413                     wc->wcc_all[i*ewcNR+j].c *= wc->nthreads_pp;
414                 }
415             }
416         }
417     }
418
419     if (wcc[ewcDDCOMMLOAD].n > 0)
420     {
421         wcc[ewcDOMDEC].c -= wcc[ewcDDCOMMLOAD].c;
422     }
423     if (wcc[ewcDDCOMMBOUND].n > 0)
424     {
425         wcc[ewcDOMDEC].c -= wcc[ewcDDCOMMBOUND].c;
426     }
427     if (wcc[ewcPME_FFTCOMM].n > 0)
428     {
429         wcc[ewcPME_FFT].c -= wcc[ewcPME_FFTCOMM].c;
430     }
431
432     if (cr->npmenodes == 0)
433     {
434         /* All nodes do PME (or no PME at all) */
435         if (wcc[ewcPMEMESH].n > 0)
436         {
437             wcc[ewcFORCE].c -= wcc[ewcPMEMESH].c;
438         }
439     }
440     else
441     {
442         /* The are PME-only nodes */
443         if (wcc[ewcPMEMESH].n > 0)
444         {
445             /* This must be a PME only node, calculate the Wait + Comm. time */
446             wcc[ewcPMEWAITCOMM].c = wcc[ewcRUN].c - wcc[ewcPMEMESH].c;
447         }
448     }
449
450     /* Store the cycles in a double buffer for summing */
451     for (i = 0; i < ewcNR; i++)
452     {
453         cycles_n[i] = (double)wcc[i].n;
454         cycles[i]   = (double)wcc[i].c;
455     }
456     nsum = ewcNR;
457 #ifdef GMX_CYCLE_SUBCOUNTERS
458     for (i = 0; i < ewcsNR; i++)
459     {
460         wc->wcsc[i].c    *= wc->nthreads_pp;
461         cycles_n[ewcNR+i] = (double)wc->wcsc[i].n;
462         cycles[ewcNR+i]   = (double)wc->wcsc[i].c;
463     }
464     nsum += ewcsNR;
465 #endif
466
467 #ifdef GMX_MPI
468     if (cr->nnodes > 1)
469     {
470         MPI_Allreduce(cycles_n, buf, nsum, MPI_DOUBLE, MPI_MAX,
471                       cr->mpi_comm_mysim);
472         for (i = 0; i < ewcNR; i++)
473         {
474             wcc[i].n = (int)(buf[i] + 0.5);
475         }
476 #ifdef GMX_CYCLE_SUBCOUNTERS
477         for (i = 0; i < ewcsNR; i++)
478         {
479             wc->wcsc[i].n = (int)(buf[ewcNR+i] + 0.5);
480         }
481 #endif
482
483         MPI_Allreduce(cycles, wc->cycles_sum, nsum, MPI_DOUBLE, MPI_SUM,
484                       cr->mpi_comm_mysim);
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     else
505 #endif
506     {
507         for (i = 0; i < nsum; i++)
508         {
509             wc->cycles_sum[i] = cycles[i];
510         }
511     }
512 }
513
514 static void print_cycles(FILE *fplog, double c2t, const char *name,
515                          int nnodes, int nthreads,
516                          int ncalls, double c_sum, double tot)
517 {
518     char   nnodes_str[6];
519     char   nthreads_str[6];
520     char   ncalls_str[11];
521     double wallt;
522
523     if (c_sum > 0)
524     {
525         if (ncalls > 0)
526         {
527             snprintf(ncalls_str, sizeof(ncalls_str), "%10d", ncalls);
528             if (nnodes < 0)
529             {
530                 snprintf(nnodes_str, sizeof(nnodes_str), "N/A");
531             }
532             else
533             {
534                 snprintf(nnodes_str, sizeof(nnodes_str), "%4d", nnodes);
535             }
536             if (nthreads < 0)
537             {
538                 snprintf(nthreads_str, sizeof(nthreads_str), "N/A");
539             }
540             else
541             {
542                 snprintf(nthreads_str, sizeof(nthreads_str), "%4d", nthreads);
543             }
544         }
545         else
546         {
547             nnodes_str[0]   = 0;
548             nthreads_str[0] = 0;
549             ncalls_str[0]   = 0;
550         }
551         /* Convert the cycle count to wallclock time for this task */
552         wallt = c_sum*c2t;
553
554         fprintf(fplog, " %-19.19s %4s %4s %10s  %10.3f %14.3f %5.1f\n",
555                 name, nnodes_str, nthreads_str, ncalls_str, wallt,
556                 c_sum*1e-9, 100*c_sum/tot);
557     }
558 }
559
560 static void print_gputimes(FILE *fplog, const char *name,
561                            int n, double t, double tot_t)
562 {
563     char num[11];
564     char avg_perf[11];
565
566     if (n > 0)
567     {
568         snprintf(num, sizeof(num), "%10d", n);
569         snprintf(avg_perf, sizeof(avg_perf), "%10.3f", t/n);
570     }
571     else
572     {
573         sprintf(num, "          ");
574         sprintf(avg_perf, "          ");
575     }
576     if (t != tot_t)
577     {
578         fprintf(fplog, " %-29s %10s%12.3f   %s   %5.1f\n",
579                 name, num, t/1000, avg_perf, 100 * t/tot_t);
580     }
581     else
582     {
583         fprintf(fplog, " %-29s %10s%12.3f   %s   %5.1f\n",
584                 name, "", t/1000, avg_perf, 100.0);
585     }
586 }
587
588 static void print_header(FILE *fplog, int nrank_pp, int nth_pp, int nrank_pme, int nth_pme)
589 {
590     int nrank_tot = nrank_pp + nrank_pme;
591     if (0 == nrank_pme)
592     {
593         fprintf(fplog, "On %d MPI rank%s", nrank_tot, nrank_tot == 1 ? "" : "s");
594         if (nth_pp > 1)
595         {
596             fprintf(fplog, ", each using %d OpenMP threads", nth_pp);
597         }
598         /* Don't report doing PP+PME, because we can't tell here if
599          * this is RF, etc. */
600     }
601     else
602     {
603         fprintf(fplog, "On %d MPI rank%s doing PP", nrank_pp, nrank_pp == 1 ? "" : "s");
604         if (nth_pp > 1)
605         {
606             fprintf(fplog, ",%s using %d OpenMP threads", nrank_pp > 1 ? " each" : "", nth_pp);
607         }
608         fprintf(fplog, ", and\non %d MPI rank%s doing PME", nrank_pme, nrank_pme == 1 ? "" : "s");
609         if (nth_pme > 1)
610         {
611             fprintf(fplog, ",%s using %d OpenMP threads", nrank_pme > 1 ? " each" : "", nth_pme);
612         }
613     }
614
615     fprintf(fplog, "\n\n");
616     fprintf(fplog, " Computing:          Num   Num      Call    Wall time         Giga-Cycles\n");
617     fprintf(fplog, "                     Ranks Threads  Count      (s)         total sum    %%\n");
618 }
619
620 void wallcycle_print(FILE *fplog, int nnodes, int npme, double realtime,
621                      gmx_wallcycle_t wc, wallclock_gpu_t *gpu_t)
622 {
623     double     *cyc_sum;
624     double      tot, tot_for_pp, tot_for_rest, tot_gpu, tot_cpu_overlap, gpu_cpu_ratio, tot_k;
625     double      c2t, c2t_pp, c2t_pme;
626     int         i, j, npp, nth_pp, nth_pme, nth_tot;
627     char        buf[STRLEN];
628     const char *hline = "-----------------------------------------------------------------------------";
629
630     if (wc == NULL)
631     {
632         return;
633     }
634
635     nth_pp  = wc->nthreads_pp;
636     nth_pme = wc->nthreads_pme;
637
638     cyc_sum = wc->cycles_sum;
639
640     npp     = nnodes - npme;
641     nth_tot = npp*nth_pp + npme*nth_pme;
642
643     /* When using PME-only nodes, the next line is valid for both
644        PP-only and PME-only nodes because they started ewcRUN at the
645        same time. */
646     tot        = cyc_sum[ewcRUN];
647     tot_for_pp = 0;
648
649     /* Conversion factor from cycles to seconds */
650     if (tot > 0)
651     {
652         c2t     = realtime/tot;
653         c2t_pp  = c2t * nth_tot / (double) (npp*nth_pp);
654         c2t_pme = c2t * nth_tot / (double) (npme*nth_pme);
655     }
656     else
657     {
658         c2t     = 0;
659         c2t_pp  = 0;
660         c2t_pme = 0;
661     }
662
663     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");
664
665     print_header(fplog, npp, nth_pp, npme, nth_pme);
666
667     fprintf(fplog, "%s\n", hline);
668     for (i = ewcPPDURINGPME+1; i < ewcNR; i++)
669     {
670         if (is_pme_subcounter(i))
671         {
672             /* Do not count these at all */
673         }
674         else if (npme > 0 && is_pme_counter(i))
675         {
676             /* Print timing information for PME-only nodes, but add an
677              * asterisk so the reader of the table can know that the
678              * walltimes are not meant to add up. The asterisk still
679              * fits in the required maximum of 19 characters. */
680             char buffer[STRLEN];
681             snprintf(buffer, STRLEN, "%s *", wcn[i]);
682             print_cycles(fplog, c2t_pme, buffer,
683                          npme, nth_pme,
684                          wc->wcc[i].n, cyc_sum[i], tot);
685         }
686         else
687         {
688             /* Print timing information when it is for a PP or PP+PME
689                node */
690             print_cycles(fplog, c2t_pp, wcn[i],
691                          npp, nth_pp,
692                          wc->wcc[i].n, cyc_sum[i], tot);
693             tot_for_pp += cyc_sum[i];
694         }
695     }
696     if (wc->wcc_all != NULL)
697     {
698         for (i = 0; i < ewcNR; i++)
699         {
700             for (j = 0; j < ewcNR; j++)
701             {
702                 snprintf(buf, 20, "%-9.9s %-9.9s", wcn[i], wcn[j]);
703                 print_cycles(fplog, c2t_pp, buf,
704                              npp, nth_pp,
705                              wc->wcc_all[i*ewcNR+j].n,
706                              wc->wcc_all[i*ewcNR+j].c,
707                              tot);
708             }
709         }
710     }
711     tot_for_rest = tot * (npp * nth_pp) / (double) nth_tot;
712     print_cycles(fplog, c2t_pp, "Rest",
713                  npp, nth_pp,
714                  -1, tot_for_rest - tot_for_pp, tot);
715     fprintf(fplog, "%s\n", hline);
716     print_cycles(fplog, c2t, "Total",
717                  npp, nth_pp,
718                  -1, tot, tot);
719     fprintf(fplog, "%s\n", hline);
720
721     if (npme > 0)
722     {
723         fprintf(fplog,
724                 "(*) Note that with separate PME ranks, the walltime column actually sums to\n"
725                 "    twice the total reported, but the cycle count total and %% are correct.\n"
726                 "%s\n", hline);
727     }
728
729     if (wc->wcc[ewcPMEMESH].n > 0)
730     {
731         fprintf(fplog, " Breakdown of PME mesh computation\n");
732         fprintf(fplog, "%s\n", hline);
733         for (i = ewcPPDURINGPME+1; i < ewcNR; i++)
734         {
735             if (is_pme_subcounter(i))
736             {
737                 print_cycles(fplog, npme > 0 ? c2t_pme : c2t_pp, wcn[i],
738                              npme > 0 ? npme : npp, nth_pme,
739                              wc->wcc[i].n, cyc_sum[i], tot);
740             }
741         }
742         fprintf(fplog, "%s\n", hline);
743     }
744
745 #ifdef GMX_CYCLE_SUBCOUNTERS
746     fprintf(fplog, " Breakdown of PP computation\n");
747     fprintf(fplog, "%s\n", hline);
748     for (i = 0; i < ewcsNR; i++)
749     {
750         print_cycles(fplog, c2t_pp, wcsn[i],
751                      npp, nth_pp,
752                      wc->wcsc[i].n, cyc_sum[ewcNR+i], tot);
753     }
754     fprintf(fplog, "%s\n", hline);
755 #endif
756
757     /* print GPU timing summary */
758     if (gpu_t)
759     {
760         const char *k_log_str[2][2] = {
761             {"Nonbonded F kernel", "Nonbonded F+ene k."},
762             {"Nonbonded F+prune k.", "Nonbonded F+ene+prune k."}
763         };
764
765         tot_gpu = gpu_t->pl_h2d_t + gpu_t->nb_h2d_t + gpu_t->nb_d2h_t;
766
767         /* add up the kernel timings */
768         tot_k = 0.0;
769         for (i = 0; i < 2; i++)
770         {
771             for (j = 0; j < 2; j++)
772             {
773                 tot_k += gpu_t->ktime[i][j].t;
774             }
775         }
776         tot_gpu += tot_k;
777
778         tot_cpu_overlap = wc->wcc[ewcFORCE].c;
779         if (wc->wcc[ewcPMEMESH].n > 0)
780         {
781             tot_cpu_overlap += wc->wcc[ewcPMEMESH].c;
782         }
783         tot_cpu_overlap *= realtime*1000/tot; /* convert s to ms */
784
785         fprintf(fplog, "\n GPU timings\n%s\n", hline);
786         fprintf(fplog, " Computing:                         Count  Wall t (s)      ms/step       %c\n", '%');
787         fprintf(fplog, "%s\n", hline);
788         print_gputimes(fplog, "Pair list H2D",
789                        gpu_t->pl_h2d_c, gpu_t->pl_h2d_t, tot_gpu);
790         print_gputimes(fplog, "X / q H2D",
791                        gpu_t->nb_c, gpu_t->nb_h2d_t, tot_gpu);
792
793         for (i = 0; i < 2; i++)
794         {
795             for (j = 0; j < 2; j++)
796             {
797                 if (gpu_t->ktime[i][j].c)
798                 {
799                     print_gputimes(fplog, k_log_str[i][j],
800                                    gpu_t->ktime[i][j].c, gpu_t->ktime[i][j].t, tot_gpu);
801                 }
802             }
803         }
804
805         print_gputimes(fplog, "F D2H",  gpu_t->nb_c, gpu_t->nb_d2h_t, tot_gpu);
806         fprintf(fplog, "%s\n", hline);
807         print_gputimes(fplog, "Total ", gpu_t->nb_c, tot_gpu, tot_gpu);
808         fprintf(fplog, "%s\n", hline);
809
810         gpu_cpu_ratio = tot_gpu/tot_cpu_overlap;
811         fprintf(fplog, "\nForce evaluation time GPU/CPU: %.3f ms/%.3f ms = %.3f\n",
812                 tot_gpu/gpu_t->nb_c, tot_cpu_overlap/wc->wcc[ewcFORCE].n,
813                 gpu_cpu_ratio);
814
815         /* only print notes related to CPU-GPU load balance with PME */
816         if (wc->wcc[ewcPMEMESH].n > 0)
817         {
818             fprintf(fplog, "For optimal performance this ratio should be close to 1!\n");
819
820             /* print note if the imbalance is high with PME case in which
821              * CPU-GPU load balancing is possible */
822             if (gpu_cpu_ratio < 0.75 || gpu_cpu_ratio > 1.2)
823             {
824                 /* Only the sim master calls this function, so always print to stderr */
825                 if (gpu_cpu_ratio < 0.75)
826                 {
827                     if (npp > 1)
828                     {
829                         /* The user could have used -notunepme,
830                          * but we currently can't check that here.
831                          */
832                         md_print_warn(NULL, fplog,
833                                       "\nNOTE: The GPU has >25%% less load than the CPU. This imbalance causes\n"
834                                       "      performance loss. Maybe the domain decomposition limits the PME tuning.\n"
835                                       "      In that case, try setting the DD grid manually (-dd) or lowering -dds.");
836                     }
837                     else
838                     {
839                         /* We should not end up here, unless the box is
840                          * too small for increasing the cut-off for PME tuning.
841                          */
842                         md_print_warn(NULL, fplog,
843                                       "\nNOTE: The GPU has >25%% less load than the CPU. This imbalance causes\n"
844                                       "      performance loss.");
845                     }
846                 }
847                 if (gpu_cpu_ratio > 1.2)
848                 {
849                     md_print_warn(NULL, fplog,
850                                   "\nNOTE: The GPU has >20%% more load than the CPU. This imbalance causes\n"
851                                   "      performance loss, consider using a shorter cut-off and a finer PME grid.");
852                 }
853             }
854         }
855     }
856
857     if (wc->wcc[ewcNB_XF_BUF_OPS].n > 0 &&
858         (cyc_sum[ewcDOMDEC] > tot*0.1 ||
859          cyc_sum[ewcNS] > tot*0.1))
860     {
861         /* Only the sim master calls this function, so always print to stderr */
862         if (wc->wcc[ewcDOMDEC].n == 0)
863         {
864             md_print_warn(NULL, fplog,
865                           "NOTE: %d %% of the run time was spent in pair search,\n"
866                           "      you might want to increase nstlist (this has no effect on accuracy)\n",
867                           (int)(100*cyc_sum[ewcNS]/tot+0.5));
868         }
869         else
870         {
871             md_print_warn(NULL, fplog,
872                           "NOTE: %d %% of the run time was spent in domain decomposition,\n"
873                           "      %d %% of the run time was spent in pair search,\n"
874                           "      you might want to increase nstlist (this has no effect on accuracy)\n",
875                           (int)(100*cyc_sum[ewcDOMDEC]/tot+0.5),
876                           (int)(100*cyc_sum[ewcNS]/tot+0.5));
877         }
878     }
879
880     if (cyc_sum[ewcMoveE] > tot*0.05)
881     {
882         /* Only the sim master calls this function, so always print to stderr */
883         md_print_warn(NULL, fplog,
884                       "NOTE: %d %% of the run time was spent communicating energies,\n"
885                       "      you might want to use the -gcom option of mdrun\n",
886                       (int)(100*cyc_sum[ewcMoveE]/tot+0.5));
887     }
888 }
889
890 extern gmx_int64_t wcycle_get_reset_counters(gmx_wallcycle_t wc)
891 {
892     if (wc == NULL)
893     {
894         return -1;
895     }
896
897     return wc->reset_counters;
898 }
899
900 extern void wcycle_set_reset_counters(gmx_wallcycle_t wc, gmx_int64_t reset_counters)
901 {
902     if (wc == NULL)
903     {
904         return;
905     }
906
907     wc->reset_counters = reset_counters;
908 }
909
910 #ifdef GMX_CYCLE_SUBCOUNTERS
911
912 void wallcycle_sub_start(gmx_wallcycle_t wc, int ewcs)
913 {
914     if (wc != NULL)
915     {
916         wc->wcsc[ewcs].start = gmx_cycles_read();
917     }
918 }
919
920 void wallcycle_sub_stop(gmx_wallcycle_t wc, int ewcs)
921 {
922     if (wc != NULL)
923     {
924         wc->wcsc[ewcs].c += gmx_cycles_read() - wc->wcsc[ewcs].start;
925         wc->wcsc[ewcs].n++;
926     }
927 }
928
929 #else
930
931 void wallcycle_sub_start(gmx_wallcycle_t gmx_unused wc, int gmx_unused ewcs)
932 {
933 }
934 void wallcycle_sub_stop(gmx_wallcycle_t gmx_unused wc, int gmx_unused ewcs)
935 {
936 }
937
938 #endif /* GMX_CYCLE_SUBCOUNTERS */