Make sure frexp() returns correct for argument 0.0
[alexxy/gromacs.git] / src / gromacs / hardware / prepare_detection.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
5  * Copyright (c) 2017,2018,2019,2020, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 /*! \internal \file
37  * \brief Defines routine for activating potentially deactivated cores
38  * so they can be detected.
39  *
40  * The use of std::thread makes for brittle interaction with std
41  * library headers. Its caller also handles GPU detection and
42  * allocation of device-specific data structures. This is more
43  * manageable when separated into two distinct translation units.
44  *
45  * \author Erik Lindahl <erik.lindahl@scilifelab.se>
46  * \author Mark Abraham <mark.j.abraham@gmail.com>
47  * \ingroup module_hardware
48  */
49 #include "gmxpre.h"
50
51 #include "prepare_detection.h"
52
53 #include "config.h"
54
55 #include <cstdio>
56
57 #include <chrono>
58 #include <thread>
59 #include <vector>
60
61 #include "architecture.h"
62
63 #ifdef HAVE_UNISTD_H
64 #    include <unistd.h> // sysconf()
65 #endif
66
67 namespace gmx
68 {
69
70 /*! \brief Utility that does dummy computing for max 2 seconds to spin up cores
71  *
72  *  This routine will check the number of cores configured and online
73  *  (using sysconf), and the spins doing dummy compute operations for up to
74  *  2 seconds, or until all cores have come online. This can be used prior to
75  *  hardware detection for platforms that take unused processors offline.
76  *
77  *  This routine will not throw exceptions. In principle it should be
78  *  declared noexcept, but at least icc 19.1 and 21-beta08 with the
79  *  libstdc++-7.5 has difficulty implementing a std::vector of
80  *  std::thread started with this function when declared noexcept. It
81  *  is not clear whether the problem is the compiler or the standard
82  *  library. Fortunately, this function is not performance sensitive,
83  *  and only runs on platforms other than x86 and POWER (ie ARM),
84  *  so the possible overhead introduced by omitting noexcept is not
85  *  important.
86  */
87 static void spinUpCore()
88 {
89 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) && defined(_SC_NPROCESSORS_ONLN)
90     float dummy           = 0.1;
91     int   countConfigured = sysconf(_SC_NPROCESSORS_CONF);    // noexcept
92     auto  start           = std::chrono::steady_clock::now(); // noexcept
93
94     while (sysconf(_SC_NPROCESSORS_ONLN) < countConfigured
95            && std::chrono::steady_clock::now() - start < std::chrono::seconds(2))
96     {
97         for (int i = 1; i < 10000; i++)
98         {
99             dummy /= i;
100         }
101     }
102
103     if (dummy < 0)
104     {
105         printf("This cannot happen, but prevents loop from being optimized away.");
106     }
107 #endif
108 }
109
110 void hardwareTopologyPrepareDetection()
111 {
112 #if defined(HAVE_SYSCONF) && defined(_SC_NPROCESSORS_CONF) \
113         && (defined(THREAD_PTHREADS) || defined(THREAD_WINDOWS))
114
115     // Modify this conditional when/if x86 or PowerPC starts to sleep some cores
116     if (c_architecture != Architecture::X86 && c_architecture != Architecture::PowerPC)
117     {
118         int                      countConfigured = sysconf(_SC_NPROCESSORS_CONF);
119         std::vector<std::thread> workThreads(countConfigured);
120
121         for (auto& t : workThreads)
122         {
123             t = std::thread(spinUpCore);
124         }
125
126         for (auto& t : workThreads)
127         {
128             t.join();
129         }
130     }
131 #endif
132 }
133
134 } // namespace gmx