• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

STEllAR-GROUP / hpx / #856

28 Dec 2022 02:00AM UTC coverage: 86.602% (+0.05%) from 86.55%
#856

push

StellarBot
Merge #6119

6119: Update CMakeLists.txt r=hkaiser a=khuck

updating the default APEX version


Co-authored-by: Kevin Huck <khuck@cs.uoregon.edu>

174566 of 201573 relevant lines covered (86.6%)

1876093.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.5
/libs/core/batch_environments/src/slurm_environment.cpp
1
//  Copyright (c) 2007-2022 Hartmut Kaiser
2
//  Copyright (c) 2013-2015 Thomas Heller
3
//
4
//  SPDX-License-Identifier: BSL-1.0
5
//  Distributed under the Boost Software License, Version 1.0. (See accompanying
6
//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7

8
#include <hpx/config.hpp>
9
#include <hpx/assert.hpp>
10
#include <hpx/batch_environments/slurm_environment.hpp>
11
#include <hpx/string_util/classification.hpp>
12
#include <hpx/string_util/split.hpp>
13
#include <hpx/util/from_string.hpp>
14

15
#include <boost/fusion/include/vector.hpp>
16
#include <boost/optional.hpp>
17
#include <boost/spirit/home/x3/char.hpp>
18
#include <boost/spirit/home/x3/core.hpp>
19
#include <boost/spirit/home/x3/nonterminal.hpp>
20
#include <boost/spirit/home/x3/numeric.hpp>
21
#include <boost/spirit/home/x3/operator.hpp>
22
#include <boost/spirit/home/x3/string.hpp>
23

24
#include <cstddef>
25
#include <iostream>
26
#include <string>
27
#include <utility>
28
#include <vector>
29

30
namespace {
31

32
    using range_type = std::vector<std::string>;
33

34
    using ranges_type = std::vector<range_type>;
35

36
    using nodename_type =
37
        boost::fusion::vector<std::string, boost::optional<ranges_type>>;
38

39
    void construct_nodelist(
156✔
40
        std::vector<std::string>& nodes, std::vector<nodename_type> const& p)
41
    {
42
        std::vector<std::string> tmp_nodes;
156✔
43

44
        for (nodename_type const& value : p)
312✔
45
        {
46
            std::string const& prefix = boost::fusion::at_c<0>(value);
156✔
47
            boost::optional<ranges_type> const& ranges =
156✔
48
                boost::fusion::at_c<1>(value);
156✔
49
            bool push_now = tmp_nodes.empty();
156✔
50
            if (ranges)
156✔
51
            {
52
                for (range_type const& range : *ranges)
546✔
53
                {
54
                    if (range.size() == 1)
390✔
55
                    {
56
                        std::string s(prefix);
312✔
57
                        s += range[0];
312✔
58
                        if (push_now)
312✔
59
                        {
60
                            tmp_nodes.push_back(s);
312✔
61
                        }
312✔
62
                        else
63
                        {
64
                            for (std::string& node : tmp_nodes)
×
65
                            {
66
                                node += s;
×
67
                            }
68
                        }
69
                    }
312✔
70
                    else
71
                    {
72
                        std::size_t begin =
78✔
73
                            hpx::util::from_string<std::size_t>(range[0]);
78✔
74
                        std::size_t end =
78✔
75
                            hpx::util::from_string<std::size_t>(range[1]);
78✔
76
                        if (begin > end)
78✔
77
                            std::swap(begin, end);
×
78

79
                        std::vector<std::string> vs;
78✔
80

81
                        for (std::size_t i = begin; i <= end; ++i)
234✔
82
                        {
83
                            std::string s(prefix);
156✔
84
                            std::size_t dec = 10;
156✔
85
                            // pad with zeros
86
                            for (std::size_t j = 0; j < range[0].length() - 1;
156✔
87
                                 ++j)
×
88
                            {
89
                                if (i < dec)
×
90
                                {
91
                                    s += "0";
×
92
                                }
×
93
                                dec *= 10;
×
94
                            }
×
95
                            s += std::to_string(i);
156✔
96
                            if (push_now)
156✔
97
                            {
98
                                tmp_nodes.push_back(s);
156✔
99
                            }
156✔
100
                            else
101
                            {
102
                                vs.push_back(s);
×
103
                            }
104
                        }
156✔
105
                        if (!push_now)
78✔
106
                        {
107
                            std::vector<std::string> tmp;
×
108
                            std::swap(tmp, tmp_nodes);
×
109
                            for (std::string s : tmp)
×
110
                            {
111
                                for (std::string const& s2 : vs)
×
112
                                {
113
                                    s += s2;
×
114
                                    tmp_nodes.push_back(s);
×
115
                                }
116
                            }
×
117
                        }
×
118
                    }
78✔
119
                }
120
            }
156✔
121
            else
122
            {
123
                if (push_now)
×
124
                {
125
                    tmp_nodes.push_back(prefix);
×
126
                }
×
127
                else
128
                {
129
                    for (std::string& node : tmp_nodes)
×
130
                    {
131
                        node += prefix;
×
132
                    }
133
                }
134
            }
135
        }
136
        nodes.insert(nodes.end(), tmp_nodes.begin(), tmp_nodes.end());
156✔
137
    }
156✔
138

139
    namespace x3 = boost::spirit::x3;
140

141
    x3::rule<class prefix, std::string> prefix = "prefix";
1,251✔
142
    x3::rule<class range_str, std::string> range_str = "range_str";
1,251✔
143
    x3::rule<class range, range_type> range = "range";
1,251✔
144
    x3::rule<class ranges, ranges_type> ranges = "ranges";
1,251✔
145
    x3::rule<class ranges, nodename_type> nodename = "nodename";
1,251✔
146
    x3::rule<class hostlist, std::vector<std::string>> hostlist = "hostlist";
1,251✔
147
    x3::rule<class nodelist, std::vector<std::string>> nodelist = "nodelist";
1,251✔
148

149
    // grammar definition
150
    auto const prefix_def = +(x3::print - (x3::char_("[") | x3::char_(",")));
1,251✔
151

152
    auto const range_str_def =
1,251✔
153
        +(x3::print - (x3::char_("]") | x3::char_(",") | x3::char_("-")));
1,251✔
154

155
    auto const range_def = range_str % '-';
1,251✔
156

157
    auto const ranges_def = x3::lit("[") >> (range % ',') >> x3::lit("]");
1,251✔
158

159
    auto const nodename_def = prefix >> -ranges;
1,251✔
160

161
    auto const bind_construct_nodelist = [](auto& ctx) {
156✔
162
        auto& nodes = x3::_val(ctx);
156✔
163
        auto const& nodenames = x3::_attr(ctx);
156✔
164
        construct_nodelist(nodes, nodenames);
156✔
165
    };
156✔
166
    auto const hostlist_def = (+nodename)[bind_construct_nodelist];
1,251✔
167

168
    auto const nodelist_def = hostlist % ',';
1,251✔
169

170
    BOOST_SPIRIT_DEFINE(
1,872✔
171
        prefix, range_str, range, ranges, nodename, hostlist, nodelist)
172

173
}    // namespace
174

175
namespace hpx::util::batch_environments {
176

177
    slurm_environment::slurm_environment(
1,273✔
178
        std::vector<std::string>& nodelist, bool debug)
179
      : node_num_(0)
1,273✔
180
      , num_threads_(0)
1,273✔
181
      , num_tasks_(0)
1,273✔
182
      , num_localities_(0)
1,273✔
183
      , valid_(false)
1,273✔
184
    {
185
        char* node_num = std::getenv("SLURM_PROCID");
1,273✔
186
        valid_ = node_num != nullptr;
1,273✔
187
        if (valid_)
1,273✔
188
        {
189
            // Initialize our node number
190
            node_num_ = from_string<std::size_t>(node_num);
78✔
191

192
            // Retrieve number of localities
193
            retrieve_number_of_localities(debug);
78✔
194

195
            // Retrieve number of tasks
196
            retrieve_number_of_tasks(debug);
78✔
197

198
            // Get the list of nodes
199
            if (nodelist.empty())
78✔
200
            {
201
                retrieve_nodelist(nodelist, debug);
78✔
202
            }
78✔
203

204
            // Determine how many threads to use
205
            retrieve_number_of_threads();
78✔
206
        }
78✔
207
    }
1,273✔
208

209
    void slurm_environment::retrieve_number_of_localities(bool debug)
78✔
210
    {
211
        char* total_num_tasks = std::getenv("SLURM_STEP_NUM_TASKS");
78✔
212
        if (total_num_tasks)
78✔
213
        {
214
            num_localities_ = from_string<std::size_t>(total_num_tasks);
78✔
215
        }
78✔
216
        else
217
        {
218
            if (debug)
×
219
            {
220
                std::cerr
221
                    << "SLURM_STEP_NUM_TASKS not found: set num_localities to 1"
×
222
                    << std::endl;
×
223
            }
×
224
            num_localities_ = 1;
×
225
        }
226
    }
78✔
227

228
    void slurm_environment::retrieve_number_of_tasks(bool debug)
78✔
229
    {
230
        char* slurm_step_tasks_per_node =
78✔
231
            std::getenv("SLURM_STEP_TASKS_PER_NODE");
78✔
232
        if (slurm_step_tasks_per_node)
78✔
233
        {
234
            std::vector<std::string> tokens;
78✔
235
            hpx::string_util::split(tokens, slurm_step_tasks_per_node,
78✔
236
                hpx::string_util::is_any_of(","));
78✔
237

238
            char* slurm_node_id = std::getenv("SLURM_NODEID");
78✔
239
            HPX_ASSERT(slurm_node_id != nullptr);
78✔
240
            if (slurm_node_id)
78✔
241
            {
242
                std::size_t node_id = from_string<std::size_t>(slurm_node_id);
78✔
243
                std::size_t task_count = 0;
78✔
244
                for (auto& token : tokens)
138✔
245
                {
246
                    std::size_t paren_pos = token.find_first_of('(');
138✔
247
                    if (paren_pos != std::string::npos)
138✔
248
                    {
249
                        HPX_ASSERT(token[paren_pos + 1] == 'x');
60✔
250
                        HPX_ASSERT(token[token.size() - 1] == ')');
60✔
251
                        std::size_t begin = paren_pos + 2;
60✔
252
                        std::size_t end = token.size() - 1;
60✔
253
                        task_count += from_string<std::size_t>(
60✔
254
                            token.substr(paren_pos + 2, end - begin));
60✔
255
                    }
60✔
256
                    else
257
                    {
258
                        task_count += 1;
78✔
259
                    }
260

261
                    if (task_count > node_id)
138✔
262
                    {
263
                        num_tasks_ = from_string<std::size_t>(
78✔
264
                            token.substr(0, paren_pos));
78✔
265
                        break;
78✔
266
                    }
267
                }
268
                HPX_ASSERT(num_tasks_);
78✔
269
            }
78✔
270
        }
78✔
271
        else
272
        {
273
            if (debug)
×
274
            {
275
                std::cerr
276
                    << "SLURM_STEP_TASKS_PER_NODE not found: set num_tasks to 1"
×
277
                    << std::endl;
×
278
            }
×
279
            num_tasks_ = 1;
×
280
        }
281
    }
78✔
282

283
    void slurm_environment::retrieve_nodelist(
78✔
284
        std::vector<std::string>& nodes, bool debug)
285
    {
286
        char* slurm_nodelist_env = std::getenv("SLURM_STEP_NODELIST");
78✔
287
        if (slurm_nodelist_env)
78✔
288
        {
289
            if (debug)
78✔
290
            {
291
                std::cerr << "SLURM nodelist found (SLURM_STEP_NODELIST): "
×
292
                          << slurm_nodelist_env << std::endl;
×
293
            }
×
294

295
            std::string nodelist_str(slurm_nodelist_env);
78✔
296
            std::string::iterator begin = nodelist_str.begin();
78✔
297
            std::string::iterator end = nodelist_str.end();
78✔
298

299
            if (!x3::parse(begin, end, nodelist, nodes) || begin != end)
78✔
300
            {
301
                if (debug)
×
302
                {
303
                    std::cerr << "failed to parse SLURM nodelist "
×
304
                                 "(SLURM_STEP_NODELIST): "
305
                              << slurm_nodelist_env << std::endl;
×
306
                }
×
307
            }
×
308
        }
78✔
309
    }
78✔
310

311
    void slurm_environment::retrieve_number_of_threads()
78✔
312
    {
313
        char* slurm_cpus_per_task = std::getenv("SLURM_CPUS_PER_TASK");
78✔
314
        if (slurm_cpus_per_task)
78✔
315
            num_threads_ = from_string<std::size_t>(slurm_cpus_per_task);
26✔
316
        else
317
        {
318
            char* slurm_job_cpus_on_node =
52✔
319
                std::getenv("SLURM_JOB_CPUS_PER_NODE");
52✔
320
            HPX_ASSERT(slurm_job_cpus_on_node != nullptr);
52✔
321
            if (slurm_job_cpus_on_node)
52✔
322
            {
323
                std::vector<std::string> tokens;
52✔
324
                hpx::string_util::split(tokens, slurm_job_cpus_on_node,
52✔
325
                    hpx::string_util::is_any_of(","));
52✔
326

327
                char* slurm_node_id = std::getenv("SLURM_NODEID");
52✔
328
                HPX_ASSERT(slurm_node_id != nullptr);
52✔
329
                if (slurm_node_id)
52✔
330
                {
331
                    std::size_t node_id =
52✔
332
                        from_string<std::size_t>(slurm_node_id);
52✔
333
                    std::size_t task_count = 0;
52✔
334
                    for (auto& token : tokens)
154✔
335
                    {
336
                        std::size_t paren_pos = token.find_first_of('(');
154✔
337
                        if (paren_pos != std::string::npos)
154✔
338
                        {
339
                            HPX_ASSERT(token[paren_pos + 1] == 'x');
20✔
340
                            HPX_ASSERT(token[token.size() - 1] == ')');
20✔
341
                            std::size_t begin = paren_pos + 2;
20✔
342
                            std::size_t end = token.size() - 1;
20✔
343
                            task_count += from_string<std::size_t>(
20✔
344
                                token.substr(paren_pos + 2, end - begin));
20✔
345
                        }
20✔
346
                        else
347
                        {
348
                            task_count += 1;
134✔
349
                        }
350
                        if (task_count > node_id)
154✔
351
                        {
352
                            num_threads_ = from_string<std::size_t>(
52✔
353
                                               token.substr(0, paren_pos)) /
104✔
354
                                num_tasks_;
52✔
355
                            break;
52✔
356
                        }
357
                    }
358
                    HPX_ASSERT(num_threads_);
52✔
359
                }
52✔
360
            }
52✔
361
        }
362
    }
78✔
363
}    // namespace hpx::util::batch_environments
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc