Merge branch release-2016
[alexxy/gromacs.git] / src / gromacs / timing / walltime_accounting.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2013, The GROMACS development team.
5  * Copyright (c) 2013,2014,2015,2016,2017, 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 #include "gmxpre.h"
37
38 #include "walltime_accounting.h"
39
40 #include "config.h"
41
42 #include <ctime>
43
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
47 #ifdef HAVE_SYS_TIME_H
48 #include <sys/time.h>
49 #endif
50
51 #include "gromacs/utility/basedefinitions.h"
52 #include "gromacs/utility/smalloc.h"
53
54 /* TODO in future: convert gmx_walltime_accounting to a class,
55  * resolve who should have responsibility for recording the number of
56  * steps done, consider whether parts of finish_time, print_perf,
57  * wallcycle_print belong in this module.
58  *
59  * If/when any kind of task parallelism is implemented (even OpenMP
60  * regions simultaneously assigned to different tasks), consider
61  * whether this data structure (and/or cycle counters) should be
62  * maintained on a per-OpenMP-thread basis.
63  *
64  * Consider also replacing this with std::chrono. */
65
66 /*! \brief Manages caching wall-clock time measurements for
67  * simulations */
68 typedef struct gmx_walltime_accounting {
69     //! Seconds since the epoch recorded at the start of the simulation
70     double          start_time_stamp;
71     //! Seconds since the epoch recorded at the start of the simulation for this thread
72     double          start_time_stamp_per_thread;
73     //! Total seconds elapsed over the simulation
74     double          elapsed_time;
75     //! Total seconds elapsed over the simulation running this thread
76     double          elapsed_time_over_all_threads;
77     /*! \brief Number of OpenMP threads that will be launched by this
78      * MPI rank.
79      *
80      * This is used to scale elapsed_time_over_all_threads so
81      * that any combination of real MPI, thread MPI and OpenMP (even
82      * mdrun -ntomp_pme) processes/threads would (when run at maximum
83      * efficiency) return values such that the sum of
84      * elapsed_time_over_all_threads over all threads was constant
85      * with respect to parallelism implementation. */
86     int             numOpenMPThreads;
87     //! Set by integrators to report the amount of work they did
88     gmx_int64_t     nsteps_done;
89     //! Whether the simulation has finished in a way valid for walltime reporting.
90     bool            isValidFinish;
91 } t_gmx_walltime_accounting;
92
93 /*! \brief Calls system timing routines (e.g. clock_gettime) to get
94  * the (fractional) number of seconds elapsed since the epoch when
95  * this thread was executing.
96  *
97  * This can be used to measure system load. This can be unreliable if
98  * threads migrate between sockets. If thread-specific timers are not
99  * supported by the OS (e.g. if the OS is not POSIX-compliant), this
100  * function is implemented by gmx_gettime. */
101 static double gmx_gettime_per_thread();
102
103 // TODO In principle, all this should get protected by checks that
104 // walltime_accounting is not null. In practice, that NULL condition
105 // does not happen, and future refactoring will likely enforce it by
106 // having the gmx_walltime_accounting_t object be owned by the runner
107 // object. When these become member functions, existence will be
108 // guaranteed.
109
110 gmx_walltime_accounting_t
111 walltime_accounting_init(int numOpenMPThreads)
112 {
113     gmx_walltime_accounting_t walltime_accounting;
114
115     snew(walltime_accounting, 1);
116     walltime_accounting->start_time_stamp            = 0;
117     walltime_accounting->start_time_stamp_per_thread = 0;
118     walltime_accounting->elapsed_time                = 0;
119     walltime_accounting->nsteps_done                 = 0;
120     walltime_accounting->numOpenMPThreads            = numOpenMPThreads;
121     walltime_accounting->isValidFinish               = false;
122
123     return walltime_accounting;
124 }
125
126 void
127 walltime_accounting_destroy(gmx_walltime_accounting_t walltime_accounting)
128 {
129     sfree(walltime_accounting);
130 }
131
132 void
133 walltime_accounting_start(gmx_walltime_accounting_t walltime_accounting)
134 {
135     walltime_accounting->start_time_stamp            = gmx_gettime();
136     walltime_accounting->start_time_stamp_per_thread = gmx_gettime_per_thread();
137     walltime_accounting->elapsed_time                = 0;
138     walltime_accounting->nsteps_done                 = 0;
139 }
140
141 void
142 walltime_accounting_end(gmx_walltime_accounting_t walltime_accounting)
143 {
144     double now, now_per_thread;
145
146     now            = gmx_gettime();
147     now_per_thread = gmx_gettime_per_thread();
148
149     walltime_accounting->elapsed_time                  = now - walltime_accounting->start_time_stamp;
150     walltime_accounting->elapsed_time_over_all_threads = now_per_thread - walltime_accounting->start_time_stamp_per_thread;
151     /* For thread-MPI, the per-thread CPU timer makes this just
152      * work. For OpenMP threads, the per-thread CPU timer measurement
153      * needs to be multiplied by the number of OpenMP threads used,
154      * under the current assumption that all regions ever opened
155      * within a process are of the same size, and each thread should
156      * keep one core busy.
157      */
158     walltime_accounting->elapsed_time_over_all_threads *= walltime_accounting->numOpenMPThreads;
159 }
160
161 double
162 walltime_accounting_get_current_elapsed_time(gmx_walltime_accounting_t walltime_accounting)
163 {
164     return gmx_gettime() - walltime_accounting->start_time_stamp;
165 }
166
167 double
168 walltime_accounting_get_elapsed_time(gmx_walltime_accounting_t walltime_accounting)
169 {
170     return walltime_accounting->elapsed_time;
171 }
172
173 double
174 walltime_accounting_get_elapsed_time_over_all_threads(gmx_walltime_accounting_t walltime_accounting)
175 {
176     return walltime_accounting->elapsed_time_over_all_threads;
177 }
178
179 double
180 walltime_accounting_get_start_time_stamp(gmx_walltime_accounting_t walltime_accounting)
181 {
182     return walltime_accounting->start_time_stamp;
183 }
184
185 gmx_int64_t
186 walltime_accounting_get_nsteps_done(gmx_walltime_accounting_t walltime_accounting)
187 {
188     return walltime_accounting->nsteps_done;
189 }
190
191 void
192 walltime_accounting_set_nsteps_done(gmx_walltime_accounting_t   walltime_accounting,
193                                     gmx_int64_t                 nsteps_done)
194 {
195     walltime_accounting->nsteps_done = nsteps_done;
196 }
197
198 double
199 gmx_gettime()
200 {
201     /* Use clock_gettime only if we know linking the C run-time
202        library will work (which is not trivial on e.g. Crays), and its
203        headers claim sufficient support for POSIX (ie not Mac and
204        Windows), and it isn't BG/Q (whose compute node kernel only
205        supports gettimeofday, and bgclang doesn't provide a fully
206        functional implementation clock_gettime). */
207 #if HAVE_CLOCK_GETTIME && defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && !(defined __bgq__ && defined __clang__)
208     struct timespec t;
209     double          seconds;
210
211     clock_gettime(CLOCK_REALTIME, &t);
212     seconds = static_cast<double>(t.tv_sec) + 1e-9*t.tv_nsec;
213
214     return seconds;
215 #elif HAVE_GETTIMEOFDAY
216     // Note that gettimeofday() is deprecated by POSIX, but since Mac
217     // and Windows do not yet support POSIX, we are still stuck.
218     // Also, this is the only supported API call on Bluegene/Q.
219     struct timeval t;
220     double         seconds;
221
222     gettimeofday(&t, NULL);
223     seconds = static_cast<double>(t.tv_sec) + 1e-6*t.tv_usec;
224
225     return seconds;
226 #else
227     double  seconds;
228
229     seconds = time(NULL);
230
231     return seconds;
232 #endif
233 }
234
235 void
236 walltime_accounting_set_valid_finish(gmx_walltime_accounting_t walltime_accounting)
237 {
238     walltime_accounting->isValidFinish = true;
239 }
240
241 //! Return whether the simulation finished in a way valid for reporting walltime.
242 bool
243 walltime_accounting_get_valid_finish(const gmx_walltime_accounting_t walltime_accounting)
244 {
245     return walltime_accounting->isValidFinish;
246 }
247
248 static double
249 gmx_gettime_per_thread()
250 {
251     /* Use clock_gettime only if we know linking the C run-time
252        library will work (which is not trivial on e.g. Crays), and its
253        headers claim sufficient support for POSIX (ie not Mac and
254        Windows), and it isn't BG/Q (whose compute node kernel only
255        supports gettimeofday, and bgclang doesn't provide a fully
256        functional implementation clock_gettime). */
257 #if HAVE_CLOCK_GETTIME && defined(_POSIX_THREAD_CPUTIME) && _POSIX_THREAD_CPUTIME > 0 && !(defined __bgq__ && defined __clang__)
258     struct timespec t;
259     double          seconds;
260
261     clock_gettime(CLOCK_THREAD_CPUTIME_ID, &t);
262     seconds = static_cast<double>(t.tv_sec) + 1e-9*t.tv_nsec;
263
264     return seconds;
265 #else
266     return gmx_gettime();
267 #endif
268 }