fix internal/external OpenMP thread affinity clash
[alexxy/gromacs.git] / src / gmxlib / gmx_omp.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 the Gromacs development team under coordination of
11  * David van der Spoel, Berk Hess, and Erik Lindahl.
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * To help us fund GROMACS development, we humbly ask that you cite
19  * the research papers on the package. Check out http://www.gromacs.org
20  *
21  * And Hey:
22  * Gnomes, ROck Monsters And Chili Sauce
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #ifdef GMX_OPENMP
30 #include <omp.h>
31 #endif
32
33 #include <stdio.h>
34
35 #include "md_logging.h"
36 #include "gmx_fatal.h"
37 #include "statutil.h"
38 #include "string2.h"
39 #include "gmx_omp.h"
40
41 int gmx_omp_get_max_threads(void)
42 {
43 #ifdef GMX_OPENMP
44     return omp_get_max_threads();
45 #else
46     return 1;
47 #endif
48 }
49
50 int gmx_omp_get_num_procs(void)
51 {
52 #ifdef GMX_OPENMP
53     return omp_get_num_procs();
54 #else
55     return 1;
56 #endif
57 }
58
59 int gmx_omp_get_thread_num(void)
60 {
61 #ifdef GMX_OPENMP
62     return omp_get_thread_num();
63 #else
64     return 0;
65 #endif
66 }
67
68 void gmx_omp_set_num_threads(int num_threads)
69 {
70 #ifdef GMX_OPENMP
71     omp_set_num_threads(num_threads);
72 #else
73     return;
74 #endif
75 }
76
77 /*!
78  * Thread affinity set by the OpenMP library can conflict with the GROMACS
79  * internal affinity setting.
80  *
81  * While GNU OpenMP does not set affinity by default, the Intel OpenMP library
82  * does. This conflicts with the internal affinity (especially thread-MPI)
83  * setting, results in incorrectly locked threads, and causes dreadful performance.
84  *
85  * The KMP_AFFINITY environment variable is used by Intel, GOMP_CPU_AFFINITY
86  * by the GNU compilers (Intel also honors it well). If any of the variables
87  * is set, we honor it, disable the internal pinning, and warn the user.
88  * When using Intel OpenMP, we will disable affinity if the user did not set it
89  * anually through one of the aforementioned environment variables.
90  *
91  * Note that the Intel OpenMP affinity disabling iwll only take effect if this
92  * function is called before the OpenMP library gets initialized which happens
93  * when the first call is made into a compilation unit that contains OpenMP
94  * pragmas.
95  */
96 void gmx_omp_check_thread_affinity(FILE *fplog, const t_commrec *cr,
97                                    gmx_hw_opt_t *hw_opt)
98 {
99     gmx_bool bKmpAffinitySet, bGompCpuAffinitySet;
100     char *kmp_env, *gomp_env;
101
102     /* no need to worry if internal thread pinning is turned off */
103     if (!hw_opt->bThreadPinning)
104     {
105         return;
106     }
107
108 #if defined(GMX_OPENMP)
109
110     /* We assume that the affinity setting is available on all platforms
111      * gcc supports. Even if this is not the case (e.g. Mac OS) the user
112      * will only get a warning.*/
113     bGompCpuAffinitySet = FALSE;
114     gomp_env = NULL;
115 #if defined(__GNUC__)
116     gomp_env = getenv("GOMP_CPU_AFFINITY");
117     bGompCpuAffinitySet = (gomp_env != NULL);
118 #endif /* __GNUC__ */
119
120     bKmpAffinitySet = FALSE;
121 #if defined(__INTEL_COMPILER)
122     kmp_env = getenv("KMP_AFFINITY");
123     bKmpAffinitySet = (kmp_env != NULL);
124
125     /* disable Intel OpenMP affinity if neither KMP_AFFINITY nor
126      * GOMP_CPU_AFFINITY is set (Intel uses the GNU env. var as well) */
127     if (!bKmpAffinitySet && !bGompCpuAffinitySet)
128     {
129         int retval;
130
131 #ifdef _MSC_VER
132         /* Windows not POSIX */
133         retval = _putenv_s("KMP_AFFINITY", "disabled");
134 #else
135         /* POSIX */
136         retval = setenv("KMP_AFFINITY", "disabled", 0);
137 #endif /* _MSC_VER */
138
139         if (debug)
140         {
141             fprintf(debug, "Disabling Intel OpenMP affinity by setting the KMP_AFFINITY=disabled env. var.\n");
142         }
143
144         if (retval != 0)
145         {
146             gmx_warning("Disabling Intel OpenMp affinity setting failed!");
147         }
148     }
149
150     /* turn off internal pinning KMP_AFFINITY != "disabled" */
151     if (bKmpAffinitySet && (gmx_strncasecmp(kmp_env, "disabled", 8) != 0))
152     {
153         md_print_warn(cr, fplog, "WARNING: KMP_AFFINITY set, will turn off %s internal affinity\n"
154                       "         setting as the two can conflict and cause performance degradation.\n"
155                       "         To keep using the %s internal affinity setting, set the\n"
156                       "         KMP_AFFINITY=disabled environment variable.",
157                       ShortProgram(), ShortProgram());
158
159         hw_opt->bThreadPinning = FALSE;
160     }
161 #endif /* __INTEL_COMPILER */
162
163 #if defined(__INTEL_COMPILER) || defined(__GNUC__)
164     /* turn off internal pinning f GOMP_CPU_AFFINITY is set & non-empty */
165     if (bGompCpuAffinitySet && gomp_env != NULL && gomp_env != '\0')
166     {
167         md_print_warn(cr, fplog,
168                       "WARNING: GOMP_CPU_AFFINITY set, will turn off %s internal affinity\n"
169                       "         setting as the two can conflict and cause performance degradation.\n"
170                       "         To keep using the %s internal affinity setting, unset the\n"
171                       "         GOMP_CPU_AFFINITY environment variable.",
172                       ShortProgram(), ShortProgram());
173
174         hw_opt->bThreadPinning = FALSE;
175     }
176 #endif /* __INTEL_COMPILER || __GNUC__ */
177
178 #endif /* GMX_OPENMP */
179 }