Fixed two compilation issues.
[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 #include "gromacs/utility/gmxmpi.h"
50
51 /* DEBUG_WCYCLE adds consistency checking for the counters.
52  * It checks if you stop a counter different from the last
53  * one that was opened and if you do nest too deep.
54  */
55 /* #define DEBUG_WCYCLE */
56
57 typedef struct
58 {
59     int          n;
60     gmx_cycles_t c;
61     gmx_cycles_t start;
62     gmx_cycles_t last;
63 } wallcc_t;
64
65 typedef struct gmx_wallcycle
66 {
67     wallcc_t        *wcc;
68     /* variables for testing/debugging */
69     gmx_bool         wc_barrier;
70     wallcc_t        *wcc_all;
71     int              wc_depth;
72 #ifdef DEBUG_WCYCLE
73 #define DEPTH_MAX 6
74     int               counterlist[DEPTH_MAX];
75     int               count_depth;
76 #endif
77     int               ewc_prev;
78     gmx_cycles_t      cycle_prev;
79     gmx_large_int_t   reset_counters;
80 #ifdef GMX_MPI
81     MPI_Comm          mpi_comm_mygroup;
82 #endif
83     int               nthreads_pp;
84     int               nthreads_pme;
85 #ifdef GMX_CYCLE_SUBCOUNTERS
86     wallcc_t         *wcsc;
87 #endif
88     double           *cycles_sum;
89 } gmx_wallcycle_t_t;
90
91 /* Each name should not exceed 19 characters */
92 static const char *wcn[ewcNR] =
93 {
94     "Run", "Step", "PP during PME", "Domain decomp.", "DD comm. load",
95     "DD comm. bounds", "Vsite constr.", "Send X to PME", "Neighbor search", "Launch GPU ops.",
96     "Comm. coord.", "Born radii", "Force", "Wait + Comm. F", "PME mesh",
97     "PME redist. X/F", "PME spread/gather", "PME 3D-FFT", "PME 3D-FFT Comm.", "PME solve",
98     "PME wait for PP", "Wait + Recv. PME F", "Wait GPU nonlocal", "Wait GPU local", "NB X/F buffer ops.",
99     "Vsite spread", "Write traj.", "Update", "Constraints", "Comm. energies",
100     "Enforced rotation", "Add rot. forces", "Test"
101 };
102
103 static const char *wcsn[ewcsNR] =
104 {
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 nthreads_tot,
509                          int nnodes, int nthreads,
510                          int n, double c, double tot)
511 {
512     char   num[11];
513     char   thstr[6];
514     double wallt;
515
516     if (c > 0)
517     {
518         if (n > 0)
519         {
520             snprintf(num, sizeof(num), "%10d", n);
521             if (nthreads < 0)
522             {
523                 snprintf(thstr, sizeof(thstr), "N/A");
524             }
525             else
526             {
527                 snprintf(thstr, sizeof(thstr), "%4d", nthreads);
528             }
529         }
530         else
531         {
532             sprintf(num, "          ");
533             sprintf(thstr, "    ");
534         }
535         /* Convert the cycle count to wallclock time for this task */
536         if (nthreads > 0)
537         {
538             /* Cycle count has been multiplied by the thread count,
539              * correct for the number of threads used.
540              */
541             wallt = c*c2t*nthreads_tot/(double)(nnodes*nthreads);
542         }
543         else
544         {
545             /* nthreads=-1 signals total run time, no correction required */
546             wallt = c*c2t;
547         }
548         fprintf(fplog, " %-19s %4d %4s %10s  %10.3f %12.3f   %5.1f\n",
549                 name, nnodes, thstr, num, wallt, c*1e-9, 100*c/tot);
550     }
551 }
552
553 static void print_gputimes(FILE *fplog, const char *name,
554                            int n, double t, double tot_t)
555 {
556     char num[11];
557     char avg_perf[11];
558
559     if (n > 0)
560     {
561         snprintf(num, sizeof(num), "%10d", n);
562         snprintf(avg_perf, sizeof(avg_perf), "%10.3f", t/n);
563     }
564     else
565     {
566         sprintf(num, "          ");
567         sprintf(avg_perf, "          ");
568     }
569     if (t != tot_t)
570     {
571         fprintf(fplog, " %-29s %10s%12.3f   %s   %5.1f\n",
572                 name, num, t/1000, avg_perf, 100 * t/tot_t);
573     }
574     else
575     {
576         fprintf(fplog, " %-29s %10s%12.3f   %s   %5.1f\n",
577                 name, "", t/1000, avg_perf, 100.0);
578     }
579 }
580
581 void wallcycle_print(FILE *fplog, int nnodes, int npme, double realtime,
582                      gmx_wallcycle_t wc, wallclock_gpu_t *gpu_t)
583 {
584     double     *cycles;
585     double      c2t, tot, tot_gpu, tot_cpu_overlap, gpu_cpu_ratio, sum, tot_k;
586     int         i, j, npp, nth_pp, nth_pme, nth_tot;
587     char        buf[STRLEN];
588     const char *hline = "-----------------------------------------------------------------------------";
589
590     if (wc == NULL)
591     {
592         return;
593     }
594
595     nth_pp  = wc->nthreads_pp;
596     nth_pme = wc->nthreads_pme;
597
598     cycles = wc->cycles_sum;
599
600     if (npme > 0)
601     {
602         npp = nnodes - npme;
603     }
604     else
605     {
606         npp  = nnodes;
607         npme = nnodes;
608     }
609     nth_tot = npp*nth_pp + npme*nth_pme;
610
611     tot = cycles[ewcRUN];
612
613     /* Conversion factor from cycles to seconds */
614     if (tot > 0)
615     {
616         c2t = realtime/tot;
617     }
618     else
619     {
620         c2t = 0;
621     }
622
623     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");
624
625     fprintf(fplog, " Computing:         Nodes   Th.     Count  Wall t (s)     G-Cycles       %c\n", '%');
626     fprintf(fplog, "%s\n", hline);
627     sum = 0;
628     for (i = ewcPPDURINGPME+1; i < ewcNR; i++)
629     {
630         if (!is_pme_subcounter(i))
631         {
632             print_cycles(fplog, c2t, wcn[i], nth_tot,
633                          is_pme_counter(i) ? npme : npp,
634                          is_pme_counter(i) ? nth_pme : nth_pp,
635                          wc->wcc[i].n, cycles[i], tot);
636             sum += cycles[i];
637         }
638     }
639     if (wc->wcc_all != NULL)
640     {
641         for (i = 0; i < ewcNR; i++)
642         {
643             for (j = 0; j < ewcNR; j++)
644             {
645                 snprintf(buf, 9, "%-9s", wcn[i]);
646                 buf[9] = ' ';
647                 snprintf(buf+10, 9, "%-9s", wcn[j]);
648                 buf[19] = '\0';
649                 print_cycles(fplog, c2t, buf, nth_tot,
650                              is_pme_counter(i) ? npme : npp,
651                              is_pme_counter(i) ? nth_pme : nth_pp,
652                              wc->wcc_all[i*ewcNR+j].n,
653                              wc->wcc_all[i*ewcNR+j].c,
654                              tot);
655             }
656         }
657     }
658     print_cycles(fplog, c2t, "Rest", nth_tot, npp, -1, 0, tot-sum, tot);
659     fprintf(fplog, "%s\n", hline);
660     print_cycles(fplog, c2t, "Total", nth_tot, nnodes, -1, 0, tot, tot);
661     fprintf(fplog, "%s\n", hline);
662
663     if (wc->wcc[ewcPMEMESH].n > 0)
664     {
665         fprintf(fplog, "%s\n", hline);
666         for (i = ewcPPDURINGPME+1; i < ewcNR; i++)
667         {
668             if (is_pme_subcounter(i))
669             {
670                 print_cycles(fplog, c2t, wcn[i], nth_tot,
671                              is_pme_counter(i) ? npme : npp,
672                              is_pme_counter(i) ? nth_pme : nth_pp,
673                              wc->wcc[i].n, cycles[i], tot);
674             }
675         }
676         fprintf(fplog, "%s\n", hline);
677     }
678
679 #ifdef GMX_CYCLE_SUBCOUNTERS
680     fprintf(fplog, "%s\n", hline);
681     for (i = 0; i < ewcsNR; i++)
682     {
683         print_cycles(fplog, c2t, wcsn[i], nth_tot, npp, nth_pp,
684                      wc->wcsc[i].n, cycles[ewcNR+i], tot);
685     }
686     fprintf(fplog, "%s\n", hline);
687 #endif
688
689     /* print GPU timing summary */
690     if (gpu_t)
691     {
692         const char *k_log_str[2][2] = {
693             {"Nonbonded F kernel", "Nonbonded F+ene k."},
694             {"Nonbonded F+prune k.", "Nonbonded F+ene+prune k."}
695         };
696
697         tot_gpu = gpu_t->pl_h2d_t + gpu_t->nb_h2d_t + gpu_t->nb_d2h_t;
698
699         /* add up the kernel timings */
700         tot_k = 0.0;
701         for (i = 0; i < 2; i++)
702         {
703             for (j = 0; j < 2; j++)
704             {
705                 tot_k += gpu_t->ktime[i][j].t;
706             }
707         }
708         tot_gpu += tot_k;
709
710         tot_cpu_overlap = wc->wcc[ewcFORCE].c;
711         if (wc->wcc[ewcPMEMESH].n > 0)
712         {
713             tot_cpu_overlap += wc->wcc[ewcPMEMESH].c;
714         }
715         tot_cpu_overlap *= c2t * 1000; /* convert s to ms */
716
717         fprintf(fplog, "\n GPU timings\n%s\n", hline);
718         fprintf(fplog, " Computing:                         Count  Wall t (s)      ms/step       %c\n", '%');
719         fprintf(fplog, "%s\n", hline);
720         print_gputimes(fplog, "Pair list H2D",
721                        gpu_t->pl_h2d_c, gpu_t->pl_h2d_t, tot_gpu);
722         print_gputimes(fplog, "X / q H2D",
723                        gpu_t->nb_c, gpu_t->nb_h2d_t, tot_gpu);
724
725         for (i = 0; i < 2; i++)
726         {
727             for (j = 0; j < 2; j++)
728             {
729                 if (gpu_t->ktime[i][j].c)
730                 {
731                     print_gputimes(fplog, k_log_str[i][j],
732                                    gpu_t->ktime[i][j].c, gpu_t->ktime[i][j].t, tot_gpu);
733                 }
734             }
735         }
736
737         print_gputimes(fplog, "F D2H",  gpu_t->nb_c, gpu_t->nb_d2h_t, tot_gpu);
738         fprintf(fplog, "%s\n", hline);
739         print_gputimes(fplog, "Total ", gpu_t->nb_c, tot_gpu, tot_gpu);
740         fprintf(fplog, "%s\n", hline);
741
742         gpu_cpu_ratio = tot_gpu/tot_cpu_overlap;
743         fprintf(fplog, "\nForce evaluation time GPU/CPU: %.3f ms/%.3f ms = %.3f\n",
744                 tot_gpu/gpu_t->nb_c, tot_cpu_overlap/wc->wcc[ewcFORCE].n,
745                 gpu_cpu_ratio);
746
747         /* only print notes related to CPU-GPU load balance with PME */
748         if (wc->wcc[ewcPMEMESH].n > 0)
749         {
750             fprintf(fplog, "For optimal performance this ratio should be close to 1!\n");
751
752             /* print note if the imbalance is high with PME case in which
753              * CPU-GPU load balancing is possible */
754             if (gpu_cpu_ratio < 0.75 || gpu_cpu_ratio > 1.2)
755             {
756                 /* Only the sim master calls this function, so always print to stderr */
757                 if (gpu_cpu_ratio < 0.75)
758                 {
759                     if (npp > 1)
760                     {
761                         /* The user could have used -notunepme,
762                          * but we currently can't check that here.
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. Maybe the domain decomposition limits the PME tuning.\n"
767                                       "      In that case, try setting the DD grid manually (-dd) or lowering -dds.");
768                     }
769                     else
770                     {
771                         /* We should not end up here, unless the box is
772                          * too small for increasing the cut-off for PME tuning.
773                          */
774                         md_print_warn(NULL, fplog,
775                                       "\nNOTE: The GPU has >25%% less load than the CPU. This imbalance causes\n"
776                                       "      performance loss.");
777                     }
778                 }
779                 if (gpu_cpu_ratio > 1.2)
780                 {
781                     md_print_warn(NULL, fplog,
782                                   "\nNOTE: The GPU has >20%% more load than the CPU. This imbalance causes\n"
783                                   "      performance loss, consider using a shorter cut-off and a finer PME grid.");
784                 }
785             }
786         }
787     }
788
789     if (wc->wcc[ewcNB_XF_BUF_OPS].n > 0 &&
790         (cycles[ewcDOMDEC] > tot*0.1 ||
791          cycles[ewcNS] > tot*0.1))
792     {
793         /* Only the sim master calls this function, so always print to stderr */
794         if (wc->wcc[ewcDOMDEC].n == 0)
795         {
796             md_print_warn(NULL, fplog,
797                           "NOTE: %d %% of the run time was spent in pair search,\n"
798                           "      you might want to increase nstlist (this has no effect on accuracy)\n",
799                           (int)(100*cycles[ewcNS]/tot+0.5));
800         }
801         else
802         {
803             md_print_warn(NULL, fplog,
804                           "NOTE: %d %% of the run time was spent in domain decomposition,\n"
805                           "      %d %% of the run time was spent in pair search,\n"
806                           "      you might want to increase nstlist (this has no effect on accuracy)\n",
807                           (int)(100*cycles[ewcDOMDEC]/tot+0.5),
808                           (int)(100*cycles[ewcNS]/tot+0.5));
809         }
810     }
811
812     if (cycles[ewcMoveE] > tot*0.05)
813     {
814         /* Only the sim master calls this function, so always print to stderr */
815         md_print_warn(NULL, fplog,
816                       "NOTE: %d %% of the run time was spent communicating energies,\n"
817                       "      you might want to use the -gcom option of mdrun\n",
818                       (int)(100*cycles[ewcMoveE]/tot+0.5));
819     }
820 }
821
822 extern gmx_large_int_t wcycle_get_reset_counters(gmx_wallcycle_t wc)
823 {
824     if (wc == NULL)
825     {
826         return -1;
827     }
828
829     return wc->reset_counters;
830 }
831
832 extern void wcycle_set_reset_counters(gmx_wallcycle_t wc, gmx_large_int_t reset_counters)
833 {
834     if (wc == NULL)
835     {
836         return;
837     }
838
839     wc->reset_counters = reset_counters;
840 }
841
842 #ifdef GMX_CYCLE_SUBCOUNTERS
843
844 void wallcycle_sub_start(gmx_wallcycle_t wc, int ewcs)
845 {
846     if (wc != NULL)
847     {
848         wc->wcsc[ewcs].start = gmx_cycles_read();
849     }
850 }
851
852 void wallcycle_sub_stop(gmx_wallcycle_t wc, int ewcs)
853 {
854     if (wc != NULL)
855     {
856         wc->wcsc[ewcs].c += gmx_cycles_read() - wc->wcsc[ewcs].start;
857         wc->wcsc[ewcs].n++;
858     }
859 }
860
861 #endif /* GMX_CYCLE_SUBCOUNTERS */