Added missing DD cycle counting
[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,2015, 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 <assert.h>
44 #include <stdlib.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/timing/gpu_timing.h"
50 #include "gromacs/utility/cstringutil.h"
51 #include "gromacs/utility/gmxmpi.h"
52 #include "gromacs/utility/smalloc.h"
53 #include "gromacs/utility/snprintf.h"
54
55 /* DEBUG_WCYCLE adds consistency checking for the counters.
56  * It checks if you stop a counter different from the last
57  * one that was opened and if you do nest too deep.
58  */
59 /* #define DEBUG_WCYCLE */
60
61 #ifdef DEBUG_WCYCLE
62 #include "gromacs/utility/fatalerror.h"
63 #endif
64
65 typedef struct
66 {
67     int          n;
68     gmx_cycles_t c;
69     gmx_cycles_t start;
70     gmx_cycles_t last;
71 } wallcc_t;
72
73 typedef struct gmx_wallcycle
74 {
75     wallcc_t        *wcc;
76     /* variables for testing/debugging */
77     gmx_bool         wc_barrier;
78     wallcc_t        *wcc_all;
79     int              wc_depth;
80 #ifdef DEBUG_WCYCLE
81 #define DEPTH_MAX 6
82     int               counterlist[DEPTH_MAX];
83     int               count_depth;
84 #endif
85     int               ewc_prev;
86     gmx_cycles_t      cycle_prev;
87     gmx_int64_t       reset_counters;
88 #ifdef GMX_MPI
89     MPI_Comm          mpi_comm_mygroup;
90 #endif
91     int               nthreads_pp;
92     int               nthreads_pme;
93 #ifdef GMX_CYCLE_SUBCOUNTERS
94     wallcc_t         *wcsc;
95 #endif
96     double           *cycles_sum;
97 } gmx_wallcycle_t_t;
98
99 /* Each name should not exceed 19 printing characters
100    (ie. terminating null can be twentieth) */
101 static const char *wcn[ewcNR] =
102 {
103     "Run", "Step", "PP during PME", "Domain decomp.", "DD comm. load",
104     "DD comm. bounds", "Vsite constr.", "Send X to PME", "Neighbor search", "Launch GPU ops.",
105     "Comm. coord.", "Born radii", "Force", "Wait + Comm. F", "PME mesh",
106     "PME redist. X/F", "PME spread/gather", "PME 3D-FFT", "PME 3D-FFT Comm.", "PME solve LJ", "PME solve Elec",
107     "PME wait for PP", "Wait + Recv. PME F", "Wait GPU nonlocal", "Wait GPU local", "Wait GPU loc. est.", "NB X/F buffer ops.",
108     "Vsite spread", "COM pull force",
109     "Write traj.", "Update", "Constraints", "Comm. energies",
110     "Enforced rotation", "Add rot. forces", "Coordinate swapping", "IMD", "Test"
111 };
112
113 static const char *wcsn[ewcsNR] =
114 {
115     "DD redist.", "DD NS grid + sort", "DD setup comm.",
116     "DD make top.", "DD make constr.", "DD top. other",
117     "NS grid local", "NS grid non-loc.", "NS search local", "NS search non-loc.",
118     "Bonded F",
119     "Bonded-FEP F",
120     "Restraints F",
121     "Listed buffer ops.",
122     "Nonbonded F",
123     "Ewald F correction",
124     "NB X buffer ops.",
125     "NB F buffer ops.",
126 };
127
128 gmx_bool wallcycle_have_counter(void)
129 {
130     return gmx_cycles_have_counter();
131 }
132
133 gmx_wallcycle_t wallcycle_init(FILE *fplog, int resetstep, t_commrec gmx_unused *cr,
134                                int nthreads_pp, int nthreads_pme)
135 {
136     gmx_wallcycle_t wc;
137
138
139     if (!wallcycle_have_counter())
140     {
141         return NULL;
142     }
143
144     snew(wc, 1);
145
146     wc->wc_barrier          = FALSE;
147     wc->wcc_all             = NULL;
148     wc->wc_depth            = 0;
149     wc->ewc_prev            = -1;
150     wc->reset_counters      = resetstep;
151     wc->nthreads_pp         = nthreads_pp;
152     wc->nthreads_pme        = nthreads_pme;
153     wc->cycles_sum          = NULL;
154
155 #ifdef GMX_MPI
156     if (PAR(cr) && getenv("GMX_CYCLE_BARRIER") != NULL)
157     {
158         if (fplog)
159         {
160             fprintf(fplog, "\nWill call MPI_Barrier before each cycle start/stop call\n\n");
161         }
162         wc->wc_barrier       = TRUE;
163         wc->mpi_comm_mygroup = cr->mpi_comm_mygroup;
164     }
165 #endif
166
167     snew(wc->wcc, ewcNR);
168     if (getenv("GMX_CYCLE_ALL") != NULL)
169     {
170         if (fplog)
171         {
172             fprintf(fplog, "\nWill time all the code during the run\n\n");
173         }
174         snew(wc->wcc_all, ewcNR*ewcNR);
175     }
176
177 #ifdef GMX_CYCLE_SUBCOUNTERS
178     snew(wc->wcsc, ewcsNR);
179 #endif
180
181 #ifdef DEBUG_WCYCLE
182     wc->count_depth = 0;
183 #endif
184
185     return wc;
186 }
187
188 void wallcycle_destroy(gmx_wallcycle_t wc)
189 {
190     if (wc == NULL)
191     {
192         return;
193     }
194
195     if (wc->wcc != NULL)
196     {
197         sfree(wc->wcc);
198     }
199     if (wc->wcc_all != NULL)
200     {
201         sfree(wc->wcc_all);
202     }
203 #ifdef GMX_CYCLE_SUBCOUNTERS
204     if (wc->wcsc != NULL)
205     {
206         sfree(wc->wcsc);
207     }
208 #endif
209     sfree(wc);
210 }
211
212 static void wallcycle_all_start(gmx_wallcycle_t wc, int ewc, gmx_cycles_t cycle)
213 {
214     wc->ewc_prev   = ewc;
215     wc->cycle_prev = cycle;
216 }
217
218 static void wallcycle_all_stop(gmx_wallcycle_t wc, int ewc, gmx_cycles_t cycle)
219 {
220     wc->wcc_all[wc->ewc_prev*ewcNR+ewc].n += 1;
221     wc->wcc_all[wc->ewc_prev*ewcNR+ewc].c += cycle - wc->cycle_prev;
222 }
223
224
225 #ifdef DEBUG_WCYCLE
226 static void debug_start_check(gmx_wallcycle_t wc, int ewc)
227 {
228     /* fprintf(stderr,"wcycle_start depth %d, %s\n",wc->count_depth,wcn[ewc]); */
229
230     if (wc->count_depth < 0 || wc->count_depth >= DEPTH_MAX)
231     {
232         gmx_fatal(FARGS, "wallcycle counter depth out of range: %d",
233                   wc->count_depth);
234     }
235     wc->counterlist[wc->count_depth] = ewc;
236     wc->count_depth++;
237 }
238
239 static void debug_stop_check(gmx_wallcycle_t wc, int ewc)
240 {
241     wc->count_depth--;
242
243     /* fprintf(stderr,"wcycle_stop depth %d, %s\n",wc->count_depth,wcn[ewc]); */
244
245     if (wc->count_depth < 0)
246     {
247         gmx_fatal(FARGS, "wallcycle counter depth out of range when stopping %s: %d", wcn[ewc], wc->count_depth);
248     }
249     if (wc->counterlist[wc->count_depth] != ewc)
250     {
251         gmx_fatal(FARGS, "wallcycle mismatch at stop, start %s, stop %s",
252                   wcn[wc->counterlist[wc->count_depth]], wcn[ewc]);
253     }
254 }
255 #endif
256
257 void wallcycle_start(gmx_wallcycle_t wc, int ewc)
258 {
259     gmx_cycles_t cycle;
260
261     if (wc == NULL)
262     {
263         return;
264     }
265
266 #ifdef GMX_MPI
267     if (wc->wc_barrier)
268     {
269         MPI_Barrier(wc->mpi_comm_mygroup);
270     }
271 #endif
272
273 #ifdef DEBUG_WCYCLE
274     debug_start_check(wc, ewc);
275 #endif
276
277     cycle              = gmx_cycles_read();
278     wc->wcc[ewc].start = cycle;
279     if (wc->wcc_all != NULL)
280     {
281         wc->wc_depth++;
282         if (ewc == ewcRUN)
283         {
284             wallcycle_all_start(wc, ewc, cycle);
285         }
286         else if (wc->wc_depth == 3)
287         {
288             wallcycle_all_stop(wc, ewc, cycle);
289         }
290     }
291 }
292
293 void wallcycle_start_nocount(gmx_wallcycle_t wc, int ewc)
294 {
295     if (wc == NULL)
296     {
297         return;
298     }
299
300     wallcycle_start(wc, ewc);
301     wc->wcc[ewc].n--;
302 }
303
304 double wallcycle_stop(gmx_wallcycle_t wc, int ewc)
305 {
306     gmx_cycles_t cycle, last;
307
308     if (wc == NULL)
309     {
310         return 0;
311     }
312
313 #ifdef GMX_MPI
314     if (wc->wc_barrier)
315     {
316         MPI_Barrier(wc->mpi_comm_mygroup);
317     }
318 #endif
319
320 #ifdef DEBUG_WCYCLE
321     debug_stop_check(wc, ewc);
322 #endif
323
324     cycle           = gmx_cycles_read();
325     last            = cycle - wc->wcc[ewc].start;
326     wc->wcc[ewc].c += last;
327     wc->wcc[ewc].n++;
328     if (wc->wcc_all)
329     {
330         wc->wc_depth--;
331         if (ewc == ewcRUN)
332         {
333             wallcycle_all_stop(wc, ewc, cycle);
334         }
335         else if (wc->wc_depth == 2)
336         {
337             wallcycle_all_start(wc, ewc, cycle);
338         }
339     }
340
341     return last;
342 }
343
344 void wallcycle_get(gmx_wallcycle_t wc, int ewc, int *n, double *c)
345 {
346     *n = wc->wcc[ewc].n;
347     *c = (double)wc->wcc[ewc].c;
348 }
349
350 void wallcycle_reset_all(gmx_wallcycle_t wc)
351 {
352     int i;
353
354     if (wc == NULL)
355     {
356         return;
357     }
358
359     for (i = 0; i < ewcNR; i++)
360     {
361         wc->wcc[i].n = 0;
362         wc->wcc[i].c = 0;
363     }
364     if (wc->wcc_all)
365     {
366         for (i = 0; i < ewcNR*ewcNR; i++)
367         {
368             wc->wcc_all[i].n = 0;
369             wc->wcc_all[i].c = 0;
370         }
371     }
372 #ifdef GMX_CYCLE_SUBCOUNTERS
373     for (i = 0; i < ewcsNR; i++)
374     {
375         wc->wcsc[i].n = 0;
376         wc->wcsc[i].c = 0;
377     }
378 #endif
379 }
380
381 static gmx_bool is_pme_counter(int ewc)
382 {
383     return (ewc >= ewcPMEMESH && ewc <= ewcPMEWAITCOMM);
384 }
385
386 static gmx_bool is_pme_subcounter(int ewc)
387 {
388     return (ewc >= ewcPME_REDISTXF && ewc < ewcPMEWAITCOMM);
389 }
390
391 /* Subtract counter ewc_sub timed inside a timing block for ewc_main */
392 static void subtract_cycles(wallcc_t *wcc, int ewc_main, int ewc_sub)
393 {
394     if (wcc[ewc_sub].n > 0)
395     {
396         assert(wcc[ewc_main].c >= wcc[ewc_sub].c);
397
398         wcc[ewc_main].c -= wcc[ewc_sub].c;
399     }
400 }
401
402 void wallcycle_sum(t_commrec *cr, gmx_wallcycle_t wc)
403 {
404     wallcc_t *wcc;
405     double    cycles[ewcNR+ewcsNR];
406     double    cycles_n[ewcNR+ewcsNR], buf[ewcNR+ewcsNR], *cyc_all, *buf_all;
407     int       i, j;
408     int       nsum;
409
410     if (wc == NULL)
411     {
412         return;
413     }
414
415     snew(wc->cycles_sum, ewcNR+ewcsNR);
416
417     wcc = wc->wcc;
418
419     /* The GPU wait estimate counter is used for load balancing only
420      * and will mess up the total due to double counting: clear it.
421      */
422     wcc[ewcWAIT_GPU_NB_L_EST].n = 0;
423     wcc[ewcWAIT_GPU_NB_L_EST].c = 0;
424
425     for (i = 0; i < ewcNR; i++)
426     {
427         if (is_pme_counter(i) || (i == ewcRUN && cr->duty == DUTY_PME))
428         {
429             wcc[i].c *= wc->nthreads_pme;
430
431             if (wc->wcc_all)
432             {
433                 for (j = 0; j < ewcNR; j++)
434                 {
435                     wc->wcc_all[i*ewcNR+j].c *= wc->nthreads_pme;
436                 }
437             }
438         }
439         else
440         {
441             wcc[i].c *= wc->nthreads_pp;
442
443             if (wc->wcc_all)
444             {
445                 for (j = 0; j < ewcNR; j++)
446                 {
447                     wc->wcc_all[i*ewcNR+j].c *= wc->nthreads_pp;
448                 }
449             }
450         }
451     }
452
453     subtract_cycles(wcc, ewcDOMDEC, ewcDDCOMMLOAD);
454     subtract_cycles(wcc, ewcDOMDEC, ewcDDCOMMBOUND);
455
456     subtract_cycles(wcc, ewcPME_FFT, ewcPME_FFTCOMM);
457
458     if (cr->npmenodes == 0)
459     {
460         /* All nodes do PME (or no PME at all) */
461         subtract_cycles(wcc, ewcFORCE, ewcPMEMESH);
462     }
463     else
464     {
465         /* The are PME-only nodes */
466         if (wcc[ewcPMEMESH].n > 0)
467         {
468             /* This must be a PME only node, calculate the Wait + Comm. time */
469             assert(wcc[ewcRUN].c >= wcc[ewcPMEMESH].c);
470             wcc[ewcPMEWAITCOMM].c = wcc[ewcRUN].c - wcc[ewcPMEMESH].c;
471         }
472     }
473
474     /* Store the cycles in a double buffer for summing */
475     for (i = 0; i < ewcNR; i++)
476     {
477         cycles_n[i] = (double)wcc[i].n;
478         cycles[i]   = (double)wcc[i].c;
479     }
480     nsum = ewcNR;
481 #ifdef GMX_CYCLE_SUBCOUNTERS
482     for (i = 0; i < ewcsNR; i++)
483     {
484         wc->wcsc[i].c    *= wc->nthreads_pp;
485         cycles_n[ewcNR+i] = (double)wc->wcsc[i].n;
486         cycles[ewcNR+i]   = (double)wc->wcsc[i].c;
487     }
488     nsum += ewcsNR;
489 #endif
490
491 #ifdef GMX_MPI
492     if (cr->nnodes > 1)
493     {
494         MPI_Allreduce(cycles_n, buf, nsum, MPI_DOUBLE, MPI_MAX,
495                       cr->mpi_comm_mysim);
496         for (i = 0; i < ewcNR; i++)
497         {
498             wcc[i].n = (int)(buf[i] + 0.5);
499         }
500 #ifdef GMX_CYCLE_SUBCOUNTERS
501         for (i = 0; i < ewcsNR; i++)
502         {
503             wc->wcsc[i].n = (int)(buf[ewcNR+i] + 0.5);
504         }
505 #endif
506
507         MPI_Allreduce(cycles, wc->cycles_sum, nsum, MPI_DOUBLE, MPI_SUM,
508                       cr->mpi_comm_mysim);
509
510         if (wc->wcc_all != NULL)
511         {
512             snew(cyc_all, ewcNR*ewcNR);
513             snew(buf_all, ewcNR*ewcNR);
514             for (i = 0; i < ewcNR*ewcNR; i++)
515             {
516                 cyc_all[i] = wc->wcc_all[i].c;
517             }
518             MPI_Allreduce(cyc_all, buf_all, ewcNR*ewcNR, MPI_DOUBLE, MPI_SUM,
519                           cr->mpi_comm_mysim);
520             for (i = 0; i < ewcNR*ewcNR; i++)
521             {
522                 wc->wcc_all[i].c = buf_all[i];
523             }
524             sfree(buf_all);
525             sfree(cyc_all);
526         }
527     }
528     else
529 #endif
530     {
531         for (i = 0; i < nsum; i++)
532         {
533             wc->cycles_sum[i] = cycles[i];
534         }
535     }
536 }
537
538 static void print_cycles(FILE *fplog, double c2t, const char *name,
539                          int nnodes, int nthreads,
540                          int ncalls, double c_sum, double tot)
541 {
542     char   nnodes_str[6];
543     char   nthreads_str[6];
544     char   ncalls_str[11];
545     double wallt;
546     double percentage = (tot > 0.) ? (100. * c_sum / tot) : 0.;
547
548     if (c_sum > 0)
549     {
550         if (ncalls > 0)
551         {
552             snprintf(ncalls_str, sizeof(ncalls_str), "%10d", ncalls);
553             if (nnodes < 0)
554             {
555                 snprintf(nnodes_str, sizeof(nnodes_str), "N/A");
556             }
557             else
558             {
559                 snprintf(nnodes_str, sizeof(nnodes_str), "%4d", nnodes);
560             }
561             if (nthreads < 0)
562             {
563                 snprintf(nthreads_str, sizeof(nthreads_str), "N/A");
564             }
565             else
566             {
567                 snprintf(nthreads_str, sizeof(nthreads_str), "%4d", nthreads);
568             }
569         }
570         else
571         {
572             nnodes_str[0]   = 0;
573             nthreads_str[0] = 0;
574             ncalls_str[0]   = 0;
575         }
576         /* Convert the cycle count to wallclock time for this task */
577         wallt = c_sum*c2t;
578
579         fprintf(fplog, " %-19.19s %4s %4s %10s  %10.3f %14.3f %5.1f\n",
580                 name, nnodes_str, nthreads_str, ncalls_str, wallt,
581                 c_sum*1e-9, percentage);
582     }
583 }
584
585 static void print_gputimes(FILE *fplog, const char *name,
586                            int n, double t, double tot_t)
587 {
588     char num[11];
589     char avg_perf[11];
590
591     if (n > 0)
592     {
593         snprintf(num, sizeof(num), "%10d", n);
594         snprintf(avg_perf, sizeof(avg_perf), "%10.3f", t/n);
595     }
596     else
597     {
598         sprintf(num, "          ");
599         sprintf(avg_perf, "          ");
600     }
601     if (t != tot_t && tot_t > 0)
602     {
603         fprintf(fplog, " %-29s %10s%12.3f   %s   %5.1f\n",
604                 name, num, t/1000, avg_perf, 100 * t/tot_t);
605     }
606     else
607     {
608         fprintf(fplog, " %-29s %10s%12.3f   %s   %5.1f\n",
609                 name, "", t/1000, avg_perf, 100.0);
610     }
611 }
612
613 static void print_header(FILE *fplog, int nrank_pp, int nth_pp, int nrank_pme, int nth_pme)
614 {
615     int nrank_tot = nrank_pp + nrank_pme;
616     if (0 == nrank_pme)
617     {
618         fprintf(fplog, "On %d MPI rank%s", nrank_tot, nrank_tot == 1 ? "" : "s");
619         if (nth_pp > 1)
620         {
621             fprintf(fplog, ", each using %d OpenMP threads", nth_pp);
622         }
623         /* Don't report doing PP+PME, because we can't tell here if
624          * this is RF, etc. */
625     }
626     else
627     {
628         fprintf(fplog, "On %d MPI rank%s doing PP", nrank_pp, nrank_pp == 1 ? "" : "s");
629         if (nth_pp > 1)
630         {
631             fprintf(fplog, ",%s using %d OpenMP threads", nrank_pp > 1 ? " each" : "", nth_pp);
632         }
633         fprintf(fplog, ", and\non %d MPI rank%s doing PME", nrank_pme, nrank_pme == 1 ? "" : "s");
634         if (nth_pme > 1)
635         {
636             fprintf(fplog, ",%s using %d OpenMP threads", nrank_pme > 1 ? " each" : "", nth_pme);
637         }
638     }
639
640     fprintf(fplog, "\n\n");
641     fprintf(fplog, " Computing:          Num   Num      Call    Wall time         Giga-Cycles\n");
642     fprintf(fplog, "                     Ranks Threads  Count      (s)         total sum    %%\n");
643 }
644
645 void wallcycle_print(FILE *fplog, int nnodes, int npme, double realtime,
646                      gmx_wallcycle_t wc, struct gmx_wallclock_gpu_t *gpu_t)
647 {
648     double     *cyc_sum;
649     double      tot, tot_for_pp, tot_for_rest, tot_gpu, tot_cpu_overlap, gpu_cpu_ratio, tot_k;
650     double      c2t, c2t_pp, c2t_pme = 0;
651     int         i, j, npp, nth_pp, nth_pme, nth_tot;
652     char        buf[STRLEN];
653     const char *hline = "-----------------------------------------------------------------------------";
654
655     if (wc == NULL)
656     {
657         return;
658     }
659
660     nth_pp  = wc->nthreads_pp;
661     assert(nth_pp > 0);
662
663     nth_pme = wc->nthreads_pme;
664     assert(nth_pme > 0);
665
666     cyc_sum = wc->cycles_sum;
667
668     assert(nnodes > 0);
669     assert(npme >= 0);
670     npp     = nnodes - npme;
671     /* npme is the number of PME-only ranks used, and we always do PP work */
672     assert(npp > 0);
673
674     nth_tot = npp*nth_pp + npme*nth_pme;
675
676     /* When using PME-only nodes, the next line is valid for both
677        PP-only and PME-only nodes because they started ewcRUN at the
678        same time. */
679     tot        = cyc_sum[ewcRUN];
680     tot_for_pp = 0;
681
682     if (tot <= 0.0)
683     {
684         /* TODO This is heavy handed, but until someone reworks the
685            code so that it is provably robust with respect to
686            non-positive values for all possible timer and cycle
687            counters, there is less value gained from printing whatever
688            timing data might still be sensible for some non-Jenkins
689            run, than is lost from diagnosing Jenkins FP exceptions on
690            runs about whose execution time we don't care. */
691         md_print_warn(NULL, fplog, "WARNING: A total of %f CPU cycles was recorded, so mdrun cannot print a time accounting\n", tot);
692         return;
693     }
694
695     /* Conversion factor from cycles to seconds */
696     c2t     = realtime/tot;
697     c2t_pp  = c2t * nth_tot / (double) (npp*nth_pp);
698     if (npme > 0)
699     {
700         c2t_pme = c2t * nth_tot / (double) (npme*nth_pme);
701     }
702     else
703     {
704         c2t_pme = 0;
705     }
706
707     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");
708
709     print_header(fplog, npp, nth_pp, npme, nth_pme);
710
711     fprintf(fplog, "%s\n", hline);
712     for (i = ewcPPDURINGPME+1; i < ewcNR; i++)
713     {
714         if (is_pme_subcounter(i))
715         {
716             /* Do not count these at all */
717         }
718         else if (npme > 0 && is_pme_counter(i))
719         {
720             /* Print timing information for PME-only nodes, but add an
721              * asterisk so the reader of the table can know that the
722              * walltimes are not meant to add up. The asterisk still
723              * fits in the required maximum of 19 characters. */
724             char buffer[STRLEN];
725             snprintf(buffer, STRLEN, "%s *", wcn[i]);
726             print_cycles(fplog, c2t_pme, buffer,
727                          npme, nth_pme,
728                          wc->wcc[i].n, cyc_sum[i], tot);
729         }
730         else
731         {
732             /* Print timing information when it is for a PP or PP+PME
733                node */
734             print_cycles(fplog, c2t_pp, wcn[i],
735                          npp, nth_pp,
736                          wc->wcc[i].n, cyc_sum[i], tot);
737             tot_for_pp += cyc_sum[i];
738         }
739     }
740     if (wc->wcc_all != NULL)
741     {
742         for (i = 0; i < ewcNR; i++)
743         {
744             for (j = 0; j < ewcNR; j++)
745             {
746                 snprintf(buf, 20, "%-9.9s %-9.9s", wcn[i], wcn[j]);
747                 print_cycles(fplog, c2t_pp, buf,
748                              npp, nth_pp,
749                              wc->wcc_all[i*ewcNR+j].n,
750                              wc->wcc_all[i*ewcNR+j].c,
751                              tot);
752             }
753         }
754     }
755     tot_for_rest = tot * (npp * nth_pp) / (double) nth_tot;
756     print_cycles(fplog, c2t_pp, "Rest",
757                  npp, nth_pp,
758                  -1, tot_for_rest - tot_for_pp, tot);
759     fprintf(fplog, "%s\n", hline);
760     print_cycles(fplog, c2t, "Total",
761                  npp, nth_pp,
762                  -1, tot, tot);
763     fprintf(fplog, "%s\n", hline);
764
765     if (npme > 0)
766     {
767         fprintf(fplog,
768                 "(*) Note that with separate PME ranks, the walltime column actually sums to\n"
769                 "    twice the total reported, but the cycle count total and %% are correct.\n"
770                 "%s\n", hline);
771     }
772
773     if (wc->wcc[ewcPMEMESH].n > 0)
774     {
775         fprintf(fplog, " Breakdown of PME mesh computation\n");
776         fprintf(fplog, "%s\n", hline);
777         for (i = ewcPPDURINGPME+1; i < ewcNR; i++)
778         {
779             if (is_pme_subcounter(i))
780             {
781                 print_cycles(fplog, npme > 0 ? c2t_pme : c2t_pp, wcn[i],
782                              npme > 0 ? npme : npp, nth_pme,
783                              wc->wcc[i].n, cyc_sum[i], tot);
784             }
785         }
786         fprintf(fplog, "%s\n", hline);
787     }
788
789 #ifdef GMX_CYCLE_SUBCOUNTERS
790     fprintf(fplog, " Breakdown of PP computation\n");
791     fprintf(fplog, "%s\n", hline);
792     for (i = 0; i < ewcsNR; i++)
793     {
794         print_cycles(fplog, c2t_pp, wcsn[i],
795                      npp, nth_pp,
796                      wc->wcsc[i].n, cyc_sum[ewcNR+i], tot);
797     }
798     fprintf(fplog, "%s\n", hline);
799 #endif
800
801     /* print GPU timing summary */
802     if (gpu_t)
803     {
804         const char *k_log_str[2][2] = {
805             {"Nonbonded F kernel", "Nonbonded F+ene k."},
806             {"Nonbonded F+prune k.", "Nonbonded F+ene+prune k."}
807         };
808
809         tot_gpu = gpu_t->pl_h2d_t + gpu_t->nb_h2d_t + gpu_t->nb_d2h_t;
810
811         /* add up the kernel timings */
812         tot_k = 0.0;
813         for (i = 0; i < 2; i++)
814         {
815             for (j = 0; j < 2; j++)
816             {
817                 tot_k += gpu_t->ktime[i][j].t;
818             }
819         }
820         tot_gpu += tot_k;
821
822         tot_cpu_overlap = wc->wcc[ewcFORCE].c;
823         if (wc->wcc[ewcPMEMESH].n > 0)
824         {
825             tot_cpu_overlap += wc->wcc[ewcPMEMESH].c;
826         }
827         tot_cpu_overlap *= realtime*1000/tot; /* convert s to ms */
828
829         fprintf(fplog, "\n GPU timings\n%s\n", hline);
830         fprintf(fplog, " Computing:                         Count  Wall t (s)      ms/step       %c\n", '%');
831         fprintf(fplog, "%s\n", hline);
832         print_gputimes(fplog, "Pair list H2D",
833                        gpu_t->pl_h2d_c, gpu_t->pl_h2d_t, tot_gpu);
834         print_gputimes(fplog, "X / q H2D",
835                        gpu_t->nb_c, gpu_t->nb_h2d_t, tot_gpu);
836
837         for (i = 0; i < 2; i++)
838         {
839             for (j = 0; j < 2; j++)
840             {
841                 if (gpu_t->ktime[i][j].c)
842                 {
843                     print_gputimes(fplog, k_log_str[i][j],
844                                    gpu_t->ktime[i][j].c, gpu_t->ktime[i][j].t, tot_gpu);
845                 }
846             }
847         }
848
849         print_gputimes(fplog, "F D2H",  gpu_t->nb_c, gpu_t->nb_d2h_t, tot_gpu);
850         fprintf(fplog, "%s\n", hline);
851         print_gputimes(fplog, "Total ", gpu_t->nb_c, tot_gpu, tot_gpu);
852         fprintf(fplog, "%s\n", hline);
853
854         gpu_cpu_ratio = tot_gpu/tot_cpu_overlap;
855         if (gpu_t->nb_c > 0 && wc->wcc[ewcFORCE].n > 0)
856         {
857             fprintf(fplog, "\nForce evaluation time GPU/CPU: %.3f ms/%.3f ms = %.3f\n",
858                     tot_gpu/gpu_t->nb_c, tot_cpu_overlap/wc->wcc[ewcFORCE].n,
859                     gpu_cpu_ratio);
860         }
861
862         /* only print notes related to CPU-GPU load balance with PME */
863         if (wc->wcc[ewcPMEMESH].n > 0)
864         {
865             fprintf(fplog, "For optimal performance this ratio should be close to 1!\n");
866
867             /* print note if the imbalance is high with PME case in which
868              * CPU-GPU load balancing is possible */
869             if (gpu_cpu_ratio < 0.75 || gpu_cpu_ratio > 1.2)
870             {
871                 /* Only the sim master calls this function, so always print to stderr */
872                 if (gpu_cpu_ratio < 0.75)
873                 {
874                     if (npp > 1)
875                     {
876                         /* The user could have used -notunepme,
877                          * but we currently can't check that here.
878                          */
879                         md_print_warn(NULL, fplog,
880                                       "\nNOTE: The GPU has >25%% less load than the CPU. This imbalance causes\n"
881                                       "      performance loss. Maybe the domain decomposition limits the PME tuning.\n"
882                                       "      In that case, try setting the DD grid manually (-dd) or lowering -dds.");
883                     }
884                     else
885                     {
886                         /* We should not end up here, unless the box is
887                          * too small for increasing the cut-off for PME tuning.
888                          */
889                         md_print_warn(NULL, fplog,
890                                       "\nNOTE: The GPU has >25%% less load than the CPU. This imbalance causes\n"
891                                       "      performance loss.");
892                     }
893                 }
894                 if (gpu_cpu_ratio > 1.2)
895                 {
896                     md_print_warn(NULL, fplog,
897                                   "\nNOTE: The GPU has >20%% more load than the CPU. This imbalance causes\n"
898                                   "      performance loss, consider using a shorter cut-off and a finer PME grid.");
899                 }
900             }
901         }
902     }
903
904     if (wc->wc_barrier)
905     {
906         md_print_warn(NULL, fplog,
907                       "MPI_Barrier was called before each cycle start/stop\n"
908                       "call, so timings are not those of real runs.\n");
909     }
910
911     if (wc->wcc[ewcNB_XF_BUF_OPS].n > 0 &&
912         (cyc_sum[ewcDOMDEC] > tot*0.1 ||
913          cyc_sum[ewcNS] > tot*0.1))
914     {
915         /* Only the sim master calls this function, so always print to stderr */
916         if (wc->wcc[ewcDOMDEC].n == 0)
917         {
918             md_print_warn(NULL, fplog,
919                           "NOTE: %d %% of the run time was spent in pair search,\n"
920                           "      you might want to increase nstlist (this has no effect on accuracy)\n",
921                           (int)(100*cyc_sum[ewcNS]/tot+0.5));
922         }
923         else
924         {
925             md_print_warn(NULL, fplog,
926                           "NOTE: %d %% of the run time was spent in domain decomposition,\n"
927                           "      %d %% of the run time was spent in pair search,\n"
928                           "      you might want to increase nstlist (this has no effect on accuracy)\n",
929                           (int)(100*cyc_sum[ewcDOMDEC]/tot+0.5),
930                           (int)(100*cyc_sum[ewcNS]/tot+0.5));
931         }
932     }
933
934     if (cyc_sum[ewcMoveE] > tot*0.05)
935     {
936         /* Only the sim master calls this function, so always print to stderr */
937         md_print_warn(NULL, fplog,
938                       "NOTE: %d %% of the run time was spent communicating energies,\n"
939                       "      you might want to use the -gcom option of mdrun\n",
940                       (int)(100*cyc_sum[ewcMoveE]/tot+0.5));
941     }
942 }
943
944 extern gmx_int64_t wcycle_get_reset_counters(gmx_wallcycle_t wc)
945 {
946     if (wc == NULL)
947     {
948         return -1;
949     }
950
951     return wc->reset_counters;
952 }
953
954 extern void wcycle_set_reset_counters(gmx_wallcycle_t wc, gmx_int64_t reset_counters)
955 {
956     if (wc == NULL)
957     {
958         return;
959     }
960
961     wc->reset_counters = reset_counters;
962 }
963
964 #ifdef GMX_CYCLE_SUBCOUNTERS
965
966 void wallcycle_sub_start(gmx_wallcycle_t wc, int ewcs)
967 {
968     if (wc != NULL)
969     {
970         wc->wcsc[ewcs].start = gmx_cycles_read();
971     }
972 }
973
974 void wallcycle_sub_start_nocount(gmx_wallcycle_t wc, int ewcs)
975 {
976     if (wc == NULL)
977     {
978         return;
979     }
980
981     wallcycle_sub_start(wc, ewcs);
982     wc->wcsc[ewcs].n--;
983 }
984
985 void wallcycle_sub_stop(gmx_wallcycle_t wc, int ewcs)
986 {
987     if (wc != NULL)
988     {
989         wc->wcsc[ewcs].c += gmx_cycles_read() - wc->wcsc[ewcs].start;
990         wc->wcsc[ewcs].n++;
991     }
992 }
993
994 #else
995
996 void wallcycle_sub_start(gmx_wallcycle_t gmx_unused wc, int gmx_unused ewcs)
997 {
998 }
999 void wallcycle_sub_start_nocount(gmx_wallcycle_t gmx_unused wc, int gmx_unused ewcs)
1000 {
1001 }
1002 void wallcycle_sub_stop(gmx_wallcycle_t gmx_unused wc, int gmx_unused ewcs)
1003 {
1004 }
1005
1006 #endif /* GMX_CYCLE_SUBCOUNTERS */