Convert gmx_ffparams_t to C++
[alexxy/gromacs.git] / src / gromacs / mdlib / perf_est.cpp
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) 2012,2014,2015,2016,2017,2018, 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 "perf_est.h"
40
41 #include <cmath>
42
43 #include "gromacs/math/functions.h"
44 #include "gromacs/math/utilities.h"
45 #include "gromacs/math/vec.h"
46 #include "gromacs/mdlib/nbnxn_consts.h"
47 #include "gromacs/mdlib/nbnxn_search.h"
48 #include "gromacs/mdtypes/commrec.h"
49 #include "gromacs/mdtypes/inputrec.h"
50 #include "gromacs/mdtypes/md_enums.h"
51 #include "gromacs/simd/simd.h"
52 #include "gromacs/topology/ifunc.h"
53 #include "gromacs/topology/topology.h"
54 #include "gromacs/utility/fatalerror.h"
55
56 /* Computational cost of bonded, non-bonded and PME calculations.
57  * This will be machine dependent.
58  * The numbers are only used for estimating the relative cost of PME vs PP,
59  * so only relative numbers matter.
60  * The numbers here are accurate cycle counts for Haswell in single precision
61  * compiled with gcc5.2. A correction factor for other architectures is given
62  * by simd_cycle_factor().
63  * In double precision PME mesh is slightly cheaper, although not so much
64  * that the numbers need to be adjusted.
65  */
66
67 /* Cost of a pair interaction in the "group" cut-off scheme */
68 static const double c_group_fq        = 18.0;
69 static const double c_group_qlj_cut   = 18.0;
70 static const double c_group_qlj_tab   = 24.0;
71 static const double c_group_lj_cut    = 12.0;
72 static const double c_group_lj_tab    = 21.0;
73 /* Cost of 1 water with one Q/LJ atom */
74 static const double c_group_qljw_cut  = 24.0;
75 static const double c_group_qljw_tab  = 27.0;
76 /* Cost of 1 water with one Q atom or with 1/3 water (LJ negligible) */
77 static const double c_group_qw        = 21.0;
78
79 /* Cost of a pair interaction in the "Verlet" cut-off scheme, QEXP is Ewald */
80 static const double c_nbnxn_lj        =  2.5;
81 static const double c_nbnxn_qrf_lj    =  2.9;
82 static const double c_nbnxn_qrf       =  2.4;
83 static const double c_nbnxn_qexp_lj   =  4.2;
84 static const double c_nbnxn_qexp      =  3.8;
85 /* Extra cost for expensive LJ interaction, e.g. pot-switch or LJ-PME */
86 static const double c_nbnxn_ljexp_add =  1.0;
87
88 /* Cost of the different components of PME. */
89 /* Cost of particle reordering and redistribution (no SIMD correction).
90  * This will be zero without MPI and can be very high with load imbalance.
91  * Thus we use an approximate value for medium parallelization.
92  */
93 static const double c_pme_redist = 100.0;
94 /* Cost of q spreading and force interpolation per charge. This part almost
95  * doesn't accelerate with SIMD, so we don't use SIMD correction.
96  */
97 static const double c_pme_spread =   5.0;
98 /* Cost of fft's, will be multiplied with 2 N log2(N) (no SIMD correction)
99  * Without MPI the number is 2-3, depending on grid factors and thread count.
100  * We take the high limit to be on the safe side and account for some MPI
101  * communication cost, which will dominate at high parallelization.
102  */
103 static const double c_pme_fft    =   3.0;
104 /* Cost of pme_solve, will be multiplied with N */
105 static const double c_pme_solve  =   9.0;
106
107 /* Cost of a bonded interaction divided by the number of distances calculations
108  * required in one interaction. The actual cost is nearly propotional to this.
109  */
110 static const double c_bond       =  25.0;
111
112
113 #if GMX_SIMD_HAVE_REAL
114 static const gmx_bool bHaveSIMD = TRUE;
115 #else
116 static const gmx_bool bHaveSIMD = FALSE;
117 #endif
118
119 /* Gives a correction factor for the currently compiled SIMD implementations
120  * versus the reference used for the coefficients above (8-wide SIMD with FMA).
121  * bUseSIMD sets if we asking for plain-C (FALSE) or SIMD (TRUE) code.
122  */
123 static double simd_cycle_factor(gmx_bool bUseSIMD)
124 {
125     /* The (average) ratio of the time taken by plain-C force calculations
126      * relative to SIMD versions, for the reference platform Haswell:
127      * 8-wide SIMD with FMA, factor: sqrt(2*8)*1.25 = 5.
128      * This factor is used for normalization in simd_cycle_factor().
129      */
130     const double simd_cycle_no_simd = 5.0;
131     double       speedup;
132
133 #if GMX_SIMD_HAVE_REAL
134     if (bUseSIMD)
135     {
136         /* We never get full speed-up of a factor GMX_SIMD_REAL_WIDTH.
137          * The actual speed-up depends very much on gather+scatter overhead,
138          * which is different for different bonded and non-bonded kernels.
139          * As a rough, but actually not bad, approximation we use a sqrt
140          * dependence on the width which gives a factor 4 for width=8.
141          */
142         speedup = std::sqrt(2.0*GMX_SIMD_REAL_WIDTH);
143 #if GMX_SIMD_HAVE_FMA
144         /* FMA tends to give a bit more speedup */
145         speedup *= 1.25;
146 #endif
147     }
148     else
149     {
150         speedup  = 1.0;
151     }
152 #else
153     if (bUseSIMD)
154     {
155         gmx_incons("gmx_cycle_factor() compiled without SIMD called with bUseSIMD=TRUE");
156     }
157     /* No SIMD, no speedup */
158     speedup      = 1.0;
159 #endif
160
161     /* Return speed compared to the reference (Haswell).
162      * For x86 SIMD, the nbnxn kernels are relatively much slower on
163      * Sandy/Ivy Bridge than Haswell, but that only leads to a too high
164      * PME load estimate on SB/IB, which is erring on the safe side.
165      */
166     return simd_cycle_no_simd/speedup;
167 }
168
169 void count_bonded_distances(const gmx_mtop_t *mtop, const t_inputrec *ir,
170                             double *ndistance_c, double *ndistance_simd)
171 {
172     gmx_bool       bExcl;
173     double         nonsimd_step_frac;
174     int            ftype;
175     double         ndtot_c, ndtot_simd;
176 #if GMX_SIMD_HAVE_REAL
177     gmx_bool       bSimdBondeds = TRUE;
178 #else
179     gmx_bool       bSimdBondeds = FALSE;
180 #endif
181
182     bExcl = (ir->cutoff_scheme == ecutsGROUP && inputrecExclForces(ir)
183              && !EEL_FULL(ir->coulombtype));
184
185     if (bSimdBondeds)
186     {
187         /* We only have SIMD versions of these bondeds without energy and
188          * without shift-forces, we take that into account here.
189          */
190         if (ir->nstcalcenergy > 0)
191         {
192             nonsimd_step_frac = 1.0/ir->nstcalcenergy;
193         }
194         else
195         {
196             nonsimd_step_frac = 0;
197         }
198         if (ir->epc != epcNO && 1.0/ir->nstpcouple > nonsimd_step_frac)
199         {
200             nonsimd_step_frac = 1.0/ir->nstpcouple;
201         }
202     }
203     else
204     {
205         nonsimd_step_frac = 1;
206     }
207
208     /* Count the number of pbc_rvec_sub calls required for bonded interactions.
209      * This number is also roughly proportional to the computational cost.
210      */
211     ndtot_c    = 0;
212     ndtot_simd = 0;
213     for (const gmx_molblock_t &molb : mtop->molblock)
214     {
215         const gmx_moltype_t *molt = &mtop->moltype[molb.type];
216         for (ftype = 0; ftype < F_NRE; ftype++)
217         {
218             int nbonds;
219
220             if (interaction_function[ftype].flags & IF_BOND)
221             {
222                 double nd_c, nd_simd;
223
224                 nd_c    = 0;
225                 nd_simd = 0;
226                 /* For all interactions, except for the three exceptions
227                  * in the switch below, #distances = #atoms - 1.
228                  */
229                 switch (ftype)
230                 {
231                     case F_POSRES:
232                     case F_FBPOSRES:
233                         nd_c    = 1;
234                         break;
235                     case F_CONNBONDS:
236                         break;
237                     /* These bonded potentially use SIMD */
238                     case F_ANGLES:
239                     case F_PDIHS:
240                     case F_RBDIHS:
241                     case F_LJ14:
242                         nd_c    =      nonsimd_step_frac *(NRAL(ftype) - 1);
243                         nd_simd = (1 - nonsimd_step_frac)*(NRAL(ftype) - 1);
244                         break;
245                     default:
246                         nd_c    = NRAL(ftype) - 1;
247                         break;
248                 }
249                 nbonds      = molb.nmol*molt->ilist[ftype].size()/(1 + NRAL(ftype));
250                 ndtot_c    += nbonds*nd_c;
251                 ndtot_simd += nbonds*nd_simd;
252             }
253         }
254         if (bExcl)
255         {
256             ndtot_c += molb.nmol*(molt->excls.nra - molt->atoms.nr)/2.;
257         }
258     }
259
260     if (debug)
261     {
262         fprintf(debug, "nr. of distance calculations in bondeds: C %.1f SIMD %.1f\n", ndtot_c, ndtot_simd);
263     }
264
265     if (ndistance_c    != nullptr)
266     {
267         *ndistance_c    = ndtot_c;
268     }
269     if (ndistance_simd != nullptr)
270     {
271         *ndistance_simd = ndtot_simd;
272     }
273 }
274
275 static void pp_group_load(const gmx_mtop_t *mtop, const t_inputrec *ir,
276                           const matrix box,
277                           int *nq_tot, int *nlj_tot,
278                           double *cost_pp,
279                           gmx_bool *bChargePerturbed, gmx_bool *bTypePerturbed)
280 {
281     int            atnr, cg, a0, ncqlj, ncq, nclj;
282     gmx_bool       bBHAM, bLJcut, bWater, bQ, bLJ;
283     int            nw, nqlj, nq, nlj;
284     double         fq, fqlj, flj, fqljw, fqw;
285
286     bBHAM = (mtop->ffparams.functype[0] == F_BHAM);
287
288     bLJcut = ((ir->vdwtype == evdwCUT) && !bBHAM);
289
290     /* Computational cost of bonded, non-bonded and PME calculations.
291      * This will be machine dependent.
292      * The numbers here are accurate for Intel Core2 and AMD Athlon 64
293      * in single precision. In double precision PME mesh is slightly cheaper,
294      * although not so much that the numbers need to be adjusted.
295      */
296     fq    = c_group_fq;
297     fqlj  = (bLJcut ? c_group_qlj_cut : c_group_qlj_tab);
298     flj   = (bLJcut ? c_group_lj_cut  : c_group_lj_tab);
299     /* Cost of 1 water with one Q/LJ atom */
300     fqljw = (bLJcut ? c_group_qljw_cut : c_group_qljw_tab);
301     /* Cost of 1 water with one Q atom or with 1/3 water (LJ negligible) */
302     fqw   = c_group_qw;
303
304     gmx::ArrayRef<const t_iparams> iparams = mtop->ffparams.iparams;
305     atnr              = mtop->ffparams.atnr;
306     nw                = 0;
307     nqlj              = 0;
308     nq                = 0;
309     nlj               = 0;
310     *bChargePerturbed = FALSE;
311     *bTypePerturbed   = FALSE;
312     for (const gmx_molblock_t &molb : mtop->molblock)
313     {
314         const gmx_moltype_t *molt = &mtop->moltype[molb.type];
315         const t_atom        *atom = molt->atoms.atom;
316         int                  a    = 0;
317         for (cg = 0; cg < molt->cgs.nr; cg++)
318         {
319             bWater = !bBHAM;
320             ncqlj  = 0;
321             ncq    = 0;
322             nclj   = 0;
323             a0     = a;
324             while (a < molt->cgs.index[cg+1])
325             {
326                 bQ  = (atom[a].q != 0 || atom[a].qB != 0);
327                 bLJ = (iparams[(atnr+1)*atom[a].type].lj.c6  != 0 ||
328                        iparams[(atnr+1)*atom[a].type].lj.c12 != 0);
329                 if (atom[a].q != atom[a].qB)
330                 {
331                     *bChargePerturbed = TRUE;
332                 }
333                 if (atom[a].type != atom[a].typeB)
334                 {
335                     *bTypePerturbed = TRUE;
336                 }
337                 /* This if this atom fits into water optimization */
338                 if (!((a == a0   &&  bQ &&  bLJ) ||
339                       (a == a0+1 &&  bQ && !bLJ) ||
340                       (a == a0+2 &&  bQ && !bLJ && atom[a].q == atom[a-1].q) ||
341                       (a == a0+3 && !bQ &&  bLJ)))
342                 {
343                     bWater = FALSE;
344                 }
345                 if (bQ && bLJ)
346                 {
347                     ncqlj++;
348                 }
349                 else
350                 {
351                     if (bQ)
352                     {
353                         ncq++;
354                     }
355                     if (bLJ)
356                     {
357                         nclj++;
358                     }
359                 }
360                 a++;
361             }
362             if (bWater)
363             {
364                 nw   += molb.nmol;
365             }
366             else
367             {
368                 nqlj += molb.nmol*ncqlj;
369                 nq   += molb.nmol*ncq;
370                 nlj  += molb.nmol*nclj;
371             }
372         }
373     }
374
375     *nq_tot  = nq  + nqlj + nw*3;
376     *nlj_tot = nlj + nqlj + nw;
377
378     if (debug)
379     {
380         fprintf(debug, "nw %d nqlj %d nq %d nlj %d\n", nw, nqlj, nq, nlj);
381     }
382
383     /* For the PP non-bonded cost it is (unrealistically) assumed
384      * that all atoms are distributed homogeneously in space.
385      * Factor 3 is used because a water molecule has 3 atoms
386      * (and TIP4P effectively has 3 interactions with (water) atoms)).
387      */
388     *cost_pp = 0.5*(fqljw*nw*nqlj +
389                     fqw  *nw*(3*nw + nq) +
390                     fqlj *nqlj*nqlj +
391                     fq   *nq*(3*nw + nqlj + nq) +
392                     flj  *nlj*(nw + nqlj + nlj))
393         *4/3*M_PI*ir->rlist*ir->rlist*ir->rlist/det(box);
394
395     *cost_pp *= simd_cycle_factor(bHaveSIMD);
396 }
397
398 static void pp_verlet_load(const gmx_mtop_t *mtop, const t_inputrec *ir,
399                            const matrix box,
400                            int *nq_tot, int *nlj_tot,
401                            double *cost_pp,
402                            gmx_bool *bChargePerturbed, gmx_bool *bTypePerturbed)
403 {
404     int            atnr, a, nqlj, nq, nlj;
405     gmx_bool       bQRF;
406     real           r_eff;
407     double         c_qlj, c_q, c_lj;
408     double         nppa;
409     int            j_cluster_size;
410     /* Conversion factor for reference vs SIMD kernel performance.
411      * The factor is about right for SSE2/4, but should be 2 higher for AVX256.
412      */
413 #if GMX_DOUBLE
414     const real     nbnxn_refkernel_fac = 4.0;
415 #else
416     const real     nbnxn_refkernel_fac = 8.0;
417 #endif
418
419     bQRF = (EEL_RF(ir->coulombtype) || ir->coulombtype == eelCUT);
420
421     gmx::ArrayRef<const t_iparams> iparams = mtop->ffparams.iparams;
422     atnr              = mtop->ffparams.atnr;
423     nqlj              = 0;
424     nq                = 0;
425     *bChargePerturbed = FALSE;
426     *bTypePerturbed   = FALSE;
427     for (const gmx_molblock_t &molb : mtop->molblock)
428     {
429         const gmx_moltype_t *molt = &mtop->moltype[molb.type];
430         const t_atom        *atom = molt->atoms.atom;
431         for (a = 0; a < molt->atoms.nr; a++)
432         {
433             if (atom[a].q != 0 || atom[a].qB != 0)
434             {
435                 if (iparams[(atnr+1)*atom[a].type].lj.c6  != 0 ||
436                     iparams[(atnr+1)*atom[a].type].lj.c12 != 0)
437                 {
438                     nqlj += molb.nmol;
439                 }
440                 else
441                 {
442                     nq += molb.nmol;
443                 }
444             }
445             if (atom[a].q != atom[a].qB)
446             {
447                 *bChargePerturbed = TRUE;
448             }
449             if (atom[a].type != atom[a].typeB)
450             {
451                 *bTypePerturbed = TRUE;
452             }
453         }
454     }
455
456     nlj = mtop->natoms - nqlj - nq;
457
458     *nq_tot  = nqlj + nq;
459     *nlj_tot = nqlj + nlj;
460
461     /* Effective cut-off for cluster pair list of 4x4 or 4x8 atoms.
462      * This choice should match the one of pick_nbnxn_kernel_cpu().
463      * TODO: Make this function use pick_nbnxn_kernel_cpu().
464      */
465 #if GMX_SIMD_HAVE_REAL && ((GMX_SIMD_REAL_WIDTH == 8 && defined GMX_SIMD_HAVE_FMA) || GMX_SIMD_REAL_WIDTH > 8)
466     j_cluster_size = 8;
467 #else
468     j_cluster_size = 4;
469 #endif
470     r_eff = ir->rlist + nbnxn_get_rlist_effective_inc(j_cluster_size, mtop->natoms/det(box));
471
472     /* The average number of pairs per atom */
473     nppa  = 0.5*4/3*M_PI*r_eff*r_eff*r_eff*mtop->natoms/det(box);
474
475     if (debug)
476     {
477         fprintf(debug, "nqlj %d nq %d nlj %d rlist %.3f r_eff %.3f pairs per atom %.1f\n",
478                 nqlj, nq, nlj, ir->rlist, r_eff, nppa);
479     }
480
481     /* Determine the cost per pair interaction */
482     c_qlj = (bQRF ? c_nbnxn_qrf_lj : c_nbnxn_qexp_lj);
483     c_q   = (bQRF ? c_nbnxn_qrf    : c_nbnxn_qexp);
484     c_lj  = c_nbnxn_lj;
485     if (ir->vdw_modifier == eintmodPOTSWITCH || EVDW_PME(ir->vdwtype))
486     {
487         c_qlj += c_nbnxn_ljexp_add;
488         c_lj  += c_nbnxn_ljexp_add;
489     }
490     if (EVDW_PME(ir->vdwtype) && ir->ljpme_combination_rule == eljpmeLB)
491     {
492         /* We don't have LJ-PME LB comb. rule kernels, we use slow kernels */
493         c_qlj *= nbnxn_refkernel_fac;
494         c_q   *= nbnxn_refkernel_fac;
495         c_lj  *= nbnxn_refkernel_fac;
496     }
497
498     /* For the PP non-bonded cost it is (unrealistically) assumed
499      * that all atoms are distributed homogeneously in space.
500      */
501     *cost_pp = (nqlj*c_qlj + nq*c_q + nlj*c_lj)*nppa;
502
503     *cost_pp *= simd_cycle_factor(bHaveSIMD);
504 }
505
506 float pme_load_estimate(const gmx_mtop_t *mtop, const t_inputrec *ir,
507                         const matrix box)
508 {
509     int            nq_tot, nlj_tot;
510     gmx_bool       bChargePerturbed, bTypePerturbed;
511     double         ndistance_c, ndistance_simd;
512     double         cost_bond, cost_pp, cost_redist, cost_spread, cost_fft, cost_solve, cost_pme;
513     float          ratio;
514
515     /* Computational cost of bonded, non-bonded and PME calculations.
516      * This will be machine dependent.
517      * The numbers here are accurate for Intel Core2 and AMD Athlon 64
518      * in single precision. In double precision PME mesh is slightly cheaper,
519      * although not so much that the numbers need to be adjusted.
520      */
521
522     count_bonded_distances(mtop, ir, &ndistance_c, &ndistance_simd);
523     /* C_BOND is the cost for bonded interactions with SIMD implementations,
524      * so we need to scale the number of bonded interactions for which there
525      * are only C implementations to the number of SIMD equivalents.
526      */
527     cost_bond = c_bond*(ndistance_c   *simd_cycle_factor(FALSE) +
528                         ndistance_simd*simd_cycle_factor(bHaveSIMD));
529
530     if (ir->cutoff_scheme == ecutsGROUP)
531     {
532         pp_group_load(mtop, ir, box,
533                       &nq_tot, &nlj_tot, &cost_pp,
534                       &bChargePerturbed, &bTypePerturbed);
535     }
536     else
537     {
538         pp_verlet_load(mtop, ir, box,
539                        &nq_tot, &nlj_tot, &cost_pp,
540                        &bChargePerturbed, &bTypePerturbed);
541     }
542
543     cost_redist = 0;
544     cost_spread = 0;
545     cost_fft    = 0;
546     cost_solve  = 0;
547
548     int gridNkzFactor = int{
549         (ir->nkz + 1)/2
550     };
551     if (EEL_PME(ir->coulombtype))
552     {
553         double grid = ir->nkx*ir->nky*gridNkzFactor;
554
555         int    f     = ((ir->efep != efepNO && bChargePerturbed) ? 2 : 1);
556         cost_redist +=   c_pme_redist*nq_tot;
557         cost_spread += f*c_pme_spread*nq_tot*gmx::power3(ir->pme_order);
558         cost_fft    += f*c_pme_fft*grid*std::log(grid)/std::log(2.0);
559         cost_solve  += f*c_pme_solve*grid*simd_cycle_factor(bHaveSIMD);
560     }
561
562     if (EVDW_PME(ir->vdwtype))
563     {
564         double grid = ir->nkx*ir->nky*gridNkzFactor;
565
566         int    f     = ((ir->efep != efepNO && bTypePerturbed) ? 2 : 1);
567         if (ir->ljpme_combination_rule == eljpmeLB)
568         {
569             /* LB combination rule: we have 7 mesh terms */
570             f       *= 7;
571         }
572         cost_redist +=   c_pme_redist*nlj_tot;
573         cost_spread += f*c_pme_spread*nlj_tot*gmx::power3(ir->pme_order);
574         cost_fft    += f*c_pme_fft*2*grid*std::log(grid)/std::log(2.0);
575         cost_solve  += f*c_pme_solve*grid*simd_cycle_factor(bHaveSIMD);
576     }
577
578     cost_pme = cost_redist + cost_spread + cost_fft + cost_solve;
579
580     ratio = cost_pme/(cost_bond + cost_pp + cost_pme);
581
582     if (debug)
583     {
584         fprintf(debug,
585                 "cost_bond   %f\n"
586                 "cost_pp     %f\n"
587                 "cost_redist %f\n"
588                 "cost_spread %f\n"
589                 "cost_fft    %f\n"
590                 "cost_solve  %f\n",
591                 cost_bond, cost_pp, cost_redist, cost_spread, cost_fft, cost_solve);
592
593         fprintf(debug, "Estimate for relative PME load: %.3f\n", ratio);
594     }
595
596     return ratio;
597 }