machines.cpp 27.4 KB
Newer Older
Millian Poquet's avatar
Millian Poquet committed
1 2 3 4 5
/**
 * @file machines.cpp
 * @brief Contains machine-related classes
 */

Millian Poquet's avatar
Millian Poquet committed
6 7 8 9
#include "machines.hpp"

#include <algorithm>

10 11
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/join.hpp>
12
#include <boost/lexical_cast.hpp>
13

14 15
#include <simgrid/plugins/energy.h>

16
#include "batsim.hpp"
17
#include "context.hpp"
18
#include "export.hpp"
19
#include "jobs.hpp"
20
#include "permissions.hpp"
21

Millian Poquet's avatar
Millian Poquet committed
22
using namespace std;
23
using namespace roles;
Millian Poquet's avatar
Millian Poquet committed
24

Millian Poquet's avatar
Millian Poquet committed
25
XBT_LOG_NEW_DEFAULT_CATEGORY(machines, "machines"); //!< Logging
26

Millian Poquet's avatar
Millian Poquet committed
27 28
Machines::Machines()
{
29 30 31 32 33
    const vector<MachineState> machine_states = {MachineState::SLEEPING, MachineState::IDLE,
                                                 MachineState::COMPUTING,
                                                 MachineState::TRANSITING_FROM_SLEEPING_TO_COMPUTING,
                                                 MachineState::TRANSITING_FROM_COMPUTING_TO_SLEEPING};
    for (const MachineState & state : machine_states)
34
    {
35
        _nb_machines_in_each_state[state] = 0;
36
    }
Millian Poquet's avatar
Millian Poquet committed
37 38 39 40
}

Machines::~Machines()
{
41
    for (Machine * machine : _machines)
42
    {
43
        delete machine;
44
    }
Millian Poquet's avatar
Millian Poquet committed
45 46 47
    _machines.clear();
}

48 49
void Machines::create_machines(xbt_dynar_t hosts,
                               const BatsimContext *context,
50
                               const std::map<string, string> & role_map,
51
                               int limit_machine_count)
Millian Poquet's avatar
Millian Poquet committed
52 53 54 55
{
    xbt_assert(_machines.size() == 0, "Bad call to Machines::createMachines(): machines already created");

    msg_host_t host;
56
    unsigned int i = 0;
Millian Poquet's avatar
Millian Poquet committed
57 58
    xbt_dynar_foreach(hosts, i, host)
    {
59
        Machine * machine = new Machine(this);
Millian Poquet's avatar
Millian Poquet committed
60

61
        machine->name = sg_host_get_name(host);
62 63 64
        machine->host = host;
        machine->jobs_being_computed = {};

65 66 67 68
        auto properties_dict = MSG_host_get_properties(machine->host);
        xbt_dict_cursor_t cursor = nullptr;
        char *prop_key = nullptr;
        char *prop_value = nullptr;
69 70 71 72 73 74 75 76
        xbt_dict_foreach(properties_dict, cursor, prop_key, prop_value)
        {
            if (string(prop_value) != "")
            {
                machine->properties[string(prop_key)] = string(prop_value);
            }
        }

77 78
        // set role properties on hosts if it has CLI defined role
        if (role_map.count(machine->name))
79
        {
80 81
            // there is role to set
            if (machine->properties.count("role"))
82
            {
83
                // there is an existing role property
84
                machine->properties["role"] = machine->properties["role"] + "," + role_map.at(machine->name);
85 86 87
            }
            else
            {
88
                machine->properties["role"] = role_map.at(machine->name);
89 90 91
            }
        }

92 93
        // set the permissions using the role
        machine->permissions = permissions_from_role(machine->properties["role"]);
94

95 96 97 98
        if (context->energy_used)
        {
            int nb_pstates = MSG_host_get_nb_pstates(machine->host);
            const char * sleep_states_cstr = MSG_host_get_property_value(machine->host, "sleep_pstates");
Millian Poquet's avatar
Millian Poquet committed
99
            bool contains_sleep_pstates = (sleep_states_cstr != NULL);
100 101 102 103 104 105 106 107 108

            // Let the sleep_pstates property be traversed in order to find the sleep and virtual transition pstates
            if (contains_sleep_pstates)
            {
                string sleep_states_str = sleep_states_cstr;

                vector<string> sleep_pstate_triplets;
                boost::split(sleep_pstate_triplets, sleep_states_str, boost::is_any_of(","), boost::token_compress_on);

109
                for (const string & triplet : sleep_pstate_triplets)
110 111 112 113 114 115 116 117 118 119 120 121
                {
                    vector<string> pstates;
                    boost::split(pstates, triplet, boost::is_any_of(":"), boost::token_compress_on);
                    xbt_assert(pstates.size() == 3, "Invalid platform file '%s': host '%s' has an invalid 'sleep_pstates' property:"
                               " each comma-separated part must be composed of three pstates of three colon-separated pstates, whereas"
                               " '%s' is not valid. Each comma-separated part represents one sleep pstate sleep_ps and its virtual pstates"
                               " on_ps and off_ps used to simulate the switch ON and switch OFF mechanisms."
                               " Example of a valid comma-separated part: 0:1:3, where sleep_ps=0, on_ps=1 and off_ps=3",
                               context->platform_filename.c_str(), machine->name.c_str(), triplet.c_str());

                    int sleep_ps, on_ps, off_ps;
                    bool conversion_succeeded = true;
Millian Poquet's avatar
Millian Poquet committed
122
                    (void) conversion_succeeded; // Avoids a warning if assertions are ignored
123 124
                    try
                    {
125 126 127 128
                        boost::trim(pstates[0]);
                        boost::trim(pstates[1]);
                        boost::trim(pstates[2]);

129
                        sleep_ps = boost::lexical_cast<unsigned int>(pstates[0]);
130 131
                        off_ps = boost::lexical_cast<unsigned int>(pstates[1]);
                        on_ps = boost::lexical_cast<unsigned int>(pstates[2]);
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
                    }
                    catch(boost::bad_lexical_cast& e)
                    {
                        conversion_succeeded = false;
                    }

                    xbt_assert(conversion_succeeded, "Invalid platform file '%s': host '%s' has an invalid 'sleep_pstates' property:"
                               " the pstates of the comma-separated sleep pstate '%s' are invalid: impossible to convert the pstates to"
                               " unsigned integers", context->platform_filename.c_str(), machine->name.c_str(), triplet.c_str());

                    xbt_assert(sleep_ps >= 0 && sleep_ps < nb_pstates, "Invalid platform file '%s': host '%s' has an invalid 'sleep_pstates' property:"
                               " the pstates of the comma-separated sleep pstate '%s' are invalid: the pstate %d does not exist",
                               context->platform_filename.c_str(), machine->name.c_str(), triplet.c_str(), sleep_ps);
                    xbt_assert(on_ps >= 0 && on_ps < nb_pstates, "Invalid platform file '%s': host '%s' has an invalid 'sleep_pstates' property:"
                               " the pstates of the comma-separated sleep pstate '%s' are invalid: the pstate %d does not exist",
                               context->platform_filename.c_str(), machine->name.c_str(), triplet.c_str(), on_ps);
                    xbt_assert(off_ps >= 0 && off_ps < nb_pstates, "Invalid platform file '%s': host '%s' has an invalid 'sleep_pstates' property:"
                               " the pstates of the comma-separated sleep pstate '%s' are invalid: the sleep pstate %d does not exist",
                               context->platform_filename.c_str(), machine->name.c_str(), triplet.c_str(), off_ps);

                    if (machine->has_pstate(sleep_ps))
                    {
                        if (machine->pstates[sleep_ps] == PStateType::SLEEP_PSTATE)
                        {
                            XBT_ERROR("Invalid platform file '%s': host '%s' has an invalid 'sleep_pstates' property:"
                                      " the pstate %d is defined several times, which is forbidden.",
                                      context->platform_filename.c_str(), machine->name.c_str(), sleep_ps);
                        }
                        else if (machine->pstates[sleep_ps] == PStateType::TRANSITION_VIRTUAL_PSTATE)
                        {
                            XBT_ERROR("Invalid platform file '%s': host '%s' has an invalid 'sleep_pstates' property:"
                                      " the pstate %d is defined as a sleep pstate and as a virtual transition pstate."
                                      " A pstate can either be a computation one, a sleeping one or a virtual transition one, but combinations are forbidden.",
                                      context->platform_filename.c_str(), machine->name.c_str(), sleep_ps);
                        }
                        else
                        {
                            XBT_ERROR("Invalid platform file '%s': host '%s' has an invalid 'sleep_pstates' property:"
                                      " the pstate %d is defined as a sleep pstate and as another type of pstate."
                                      " A pstate can either be a computation one, a sleeping one or a virtual transition one, but combinations are forbidden.",
                                      context->platform_filename.c_str(), machine->name.c_str(), sleep_ps);
                        }
                    }

                    if (machine->has_pstate(on_ps))
                    {
                        xbt_assert(machine->pstates[on_ps] == PStateType::TRANSITION_VIRTUAL_PSTATE,
                                   "Invalid platform file '%s': host '%s' has an invalid 'sleep_pstates' property:"
                                   " a pstate can either be a computation one, a sleeping one or a virtual transition one, but combinations are forbidden."
                                   " Pstate %d is defined as a virtual transition pstate but also as another type of pstate.",
                                   context->platform_filename.c_str(), machine->name.c_str(), on_ps);
                    }

                    if (machine->has_pstate(off_ps))
                    {
                        xbt_assert(machine->pstates[off_ps] == PStateType::TRANSITION_VIRTUAL_PSTATE,
                                   "Invalid platform file '%s': host '%s' has an invalid 'sleep_pstates' property:"
                                   " a pstate can either be a computation one, a sleeping one or a virtual transition one, but combinations are forbidden."
                                   " Pstate %d is defined as a virtual transition pstate but also as another type of pstate.",
                                   context->platform_filename.c_str(), machine->name.c_str(), off_ps);
                    }

                    SleepPState * sleep_pstate = new SleepPState;
                    sleep_pstate->sleep_pstate = sleep_ps;
                    sleep_pstate->switch_on_virtual_pstate = on_ps;
                    sleep_pstate->switch_off_virtual_pstate = off_ps;

                    machine->sleep_pstates[sleep_ps] = sleep_pstate;
                    machine->pstates[sleep_ps] = PStateType::SLEEP_PSTATE;
                    machine->pstates[on_ps] = PStateType::TRANSITION_VIRTUAL_PSTATE;
                    machine->pstates[off_ps] = PStateType::TRANSITION_VIRTUAL_PSTATE;
                }
            }

            // Let the computation pstates be defined by those who are not sleep pstates nor virtual transition pstates
            for (int ps = 0; ps < nb_pstates; ++ps)
            {
                if (!machine->has_pstate(ps))
                {
                    // TODO: check that the pstate computational power is not null
                    machine->pstates[ps] = PStateType::COMPUTATION_PSTATE;
                }
            }
        }
216

217 218 219 220 221 222 223 224 225 226 227
        if (context->submission_sched_enabled)
        {
            // Because of one pernicious bug (https://github.com/oar-team/batsim/issues/21),
            // let's check that the machine contains no energy information if dynamic submissions
            // are enabled.
            const char * sleep_states_cstr = MSG_host_get_property_value(machine->host, "sleep_pstates");
            bool contains_sleep_pstates = (sleep_states_cstr != NULL);
            xbt_assert(!contains_sleep_pstates,
                       "Using dynamic job submissions AND plaforms with energy information "
                       "is currently forbidden (https://github.com/oar-team/batsim/issues/21).");
        }
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
        
        // Store machines in different place depending on the role
        if (machine->permissions == Permissions::COMPUTE_NODE)
        {
            _compute_nodes.push_back(machine);
        }
        else if (machine->permissions == Permissions::STORAGE)
        {
            // wait to give ids until we know how many compute node there is
            _storage_nodes.push_back(machine);
        }
        else if (machine->permissions == Permissions::MASTER)
        {
            xbt_assert(_master_machine == nullptr, "There are two master hosts...");
            machine->id = -1;
            _master_machine = machine;
        }
245 246 247
    }

    // sort the machines by name
248
    sort_machines_by_ascending_name(_compute_nodes);
249

250 251 252 253 254 255 256 257 258 259 260 261
    // Let's limit the number of machines
    if (limit_machine_count != 0)
    {
        int nb_machines_without_limitation = (int)_compute_nodes.size();
        xbt_assert(limit_machine_count > 0);
        xbt_assert(limit_machine_count <= nb_machines_without_limitation,
                   "Impossible to compute on M=%d machines: "
                   "only %d machines are described in the PLATFORM_FILE.",
                   limit_machine_count,
                   nb_machines_without_limitation);

        for (int machine_id = limit_machine_count; machine_id < nb_machines_without_limitation; ++machine_id)
262
        {
263
            delete _compute_nodes[machine_id];
264
        }
265 266 267 268

        _compute_nodes.resize(limit_machine_count);
    }

269 270 271
    // Now add machines to global list and add ids
    unsigned int id = 0;
    for (auto& machine : _compute_nodes)
272
    {
273 274 275 276 277 278 279 280 281 282 283
        machine->id = id;
        ++id;
        _machines.push_back(machine);
    }   
    
    // then attibute ids to storage machines
    for (auto& machine : _storage_nodes)
    {
        machine->id = id;
        ++id;
        _machines.push_back(machine);
Millian Poquet's avatar
Millian Poquet committed
284
    }
285

Millian Poquet's avatar
Millian Poquet committed
286
    xbt_assert(_master_machine != nullptr,
287
               "Cannot find the \"master\" role in the platform file");
Millian Poquet's avatar
Millian Poquet committed
288

289
    _nb_machines_in_each_state[MachineState::IDLE] = (int)_compute_nodes.size();
Millian Poquet's avatar
Millian Poquet committed
290 291
}

292
const Machine * Machines::operator[](int machineID) const
Millian Poquet's avatar
Millian Poquet committed
293
{
294
    xbt_assert(exists(machineID), "Cannot get machine %d: it does not exist", machineID);
Millian Poquet's avatar
Millian Poquet committed
295 296 297
    return _machines[machineID];
}

298
Machine * Machines::operator[](int machineID)
Millian Poquet's avatar
Millian Poquet committed
299
{
300
    xbt_assert(exists(machineID), "Cannot get machine %d: it does not exist", machineID);
Millian Poquet's avatar
Millian Poquet committed
301 302 303
    return _machines[machineID];
}

304 305 306 307 308
bool Machines::exists(int machineID) const
{
    return machineID >= 0 && machineID < (int)_machines.size();
}

309
void Machines::display_debug() const
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
{
    // Let us traverse machines to display some information about them
    vector<string> machinesVector;
    for (const Machine * m : _machines)
    {
        machinesVector.push_back(m->name + "(" + to_string(m->id) + ")");
    }

    // Let us create the string that will be sent to XBT_INFO
    string s = "Machines debug information:\n";

    s += "There are " + to_string(_machines.size()) + " machines.\n";
    s += "Mobs : [" + boost::algorithm::join(machinesVector, ", ") + "]";

    // Let us display the string which has been built
    XBT_INFO("%s", s.c_str());
}

328 329 330 331 332
const std::vector<Machine *> &Machines::machines() const
{
    return _machines;
}

333
const std::vector<Machine *> &Machines::compute_machines() const
Olivier Richard's avatar
Olivier Richard committed
334
{
335
    return _compute_nodes;
Olivier Richard's avatar
Olivier Richard committed
336 337
}

338
const std::vector<Machine *> &Machines::storage_machines() const
339
{
340
    return _storage_nodes;
341 342
}

343
const Machine *Machines::master_machine() const
344
{
345 346 347
    xbt_assert(_master_machine != nullptr,
               "Trying to access the master machine, which does not exist.");
    return _master_machine;
348
}
Olivier Richard's avatar
Olivier Richard committed
349

Millian Poquet's avatar
Millian Poquet committed
350
long double Machines::total_consumed_energy(const BatsimContext *context) const
351 352 353 354 355 356
{
    long double total_consumed_energy = 0;

    if (context->energy_used)
    {
        for (const Machine * m : _machines)
357
        {
358
            total_consumed_energy += sg_host_get_consumed_energy(m->host);
359
        }
360 361
    }
    else
362
    {
363
        total_consumed_energy = -1;
364
    }
365 366 367 368

    return total_consumed_energy;
}

369 370 371 372 373 374 375
long double Machines::total_wattmin(const BatsimContext *context) const
{
    long double total_wattmin = 0;

    if (context->energy_used)
    {
        for (const Machine * m : _machines)
376
        {
377
            total_wattmin += sg_host_get_wattmin_at(m->host, sg_host_get_pstate(m->host));
378
        }
379 380
    }
    else
381
    {
382
        total_wattmin = -1;
383
    }
384 385 386 387

    return total_wattmin;
}

388 389 390 391 392
int Machines::nb_machines() const
{
    return _machines.size();
}

393 394 395 396 397 398 399 400 401 402
int Machines::nb_compute_machines() const
{
    return _compute_nodes.size();
}

int Machines::nb_storage_machines() const
{
    return _storage_nodes.size();
}

403 404 405 406 407 408 409 410 411 412 413
void Machines::update_nb_machines_in_each_state(MachineState old_state, MachineState new_state)
{
    _nb_machines_in_each_state[old_state]--;
    _nb_machines_in_each_state[new_state]++;
}

const std::map<MachineState, int> &Machines::nb_machines_in_each_state() const
{
    return _nb_machines_in_each_state;
}

414 415 416
void Machines::update_machines_on_job_run(const Job * job,
                                          const MachineRange & used_machines,
                                          BatsimContext * context)
Millian Poquet's avatar
Millian Poquet committed
417
{
418
    for (auto it = used_machines.elements_begin(); it != used_machines.elements_end(); ++it)
Millian Poquet's avatar
Millian Poquet committed
419
    {
420 421
        int machine_id = *it;
        Machine * machine = _machines[machine_id];
422
        machine->update_machine_state(MachineState::COMPUTING);
Millian Poquet's avatar
Millian Poquet committed
423

424
        const Job * previous_top_job = nullptr;
425
        if (!machine->jobs_being_computed.empty())
426
        {
427
            previous_top_job = *machine->jobs_being_computed.begin();
428
        }
429

430
        machine->jobs_being_computed.insert(job);
431

432
        if (previous_top_job == nullptr || previous_top_job != *machine->jobs_being_computed.begin())
433
        {
434
            if (_tracer != nullptr)
435
            {
436 437 438
                _tracer->set_machine_as_computing_job(machine->id,
                                                      *machine->jobs_being_computed.begin(),
                                                      MSG_get_clock());
439
            }
440
        }
Millian Poquet's avatar
Millian Poquet committed
441
    }
442 443

    if (context->trace_machine_states)
444
    {
445
        context->machine_state_tracer.write_machine_states(MSG_get_clock());
446
    }
Millian Poquet's avatar
Millian Poquet committed
447 448
}

449 450 451
void Machines::update_machines_on_job_end(const Job * job,
                                          const MachineRange & used_machines,
                                          BatsimContext * context)
Millian Poquet's avatar
Millian Poquet committed
452
{
453
    for (auto it = used_machines.elements_begin(); it != used_machines.elements_end(); ++it)
Millian Poquet's avatar
Millian Poquet committed
454
    {
455 456
        int machine_id = *it;
        Machine * machine = _machines[machine_id];
Millian Poquet's avatar
Millian Poquet committed
457

458
        xbt_assert(!machine->jobs_being_computed.empty());
459
        const Job * previous_top_job = *machine->jobs_being_computed.begin();
460 461

        // Let's erase jobID in the jobs_being_computed data structure
462
        int ret = machine->jobs_being_computed.erase(job);
463
        (void) ret; // Avoids a warning if assertions are ignored
Millian Poquet's avatar
Millian Poquet committed
464 465
        xbt_assert(ret == 1);

466
        if (machine->jobs_being_computed.empty())
Millian Poquet's avatar
Millian Poquet committed
467
        {
468
            machine->update_machine_state(MachineState::IDLE);
469
            if (_tracer != nullptr)
470
            {
471
                _tracer->set_machine_idle(machine->id, MSG_get_clock());
472
            }
Millian Poquet's avatar
Millian Poquet committed
473
        }
474 475
        else if (*machine->jobs_being_computed.begin() != previous_top_job)
        {
476
            if (_tracer != nullptr)
477
            {
478 479 480
                _tracer->set_machine_as_computing_job(machine->id,
                                                      *machine->jobs_being_computed.begin(),
                                                      MSG_get_clock());
481
            }
482
        }
Millian Poquet's avatar
Millian Poquet committed
483
    }
484 485

    if (context->trace_machine_states)
486
    {
487
        context->machine_state_tracer.write_machine_states(MSG_get_clock());
488
    }
Millian Poquet's avatar
Millian Poquet committed
489 490
}

491
void Machines::set_tracer(PajeTracer *tracer)
492 493 494 495
{
    _tracer = tracer;
}

496
string machine_state_to_string(MachineState state)
497
{
498 499 500
    string s;

    switch (state)
501
    {
502 503 504 505 506 507 508 509 510 511
    case MachineState::SLEEPING:
        s = "sleeping";
        break;
    case MachineState::IDLE:
        s = "idle";
        break;
    case MachineState::COMPUTING:
        s = "computing";
        break;
    case MachineState::TRANSITING_FROM_SLEEPING_TO_COMPUTING:
512
        s = "switching_on";
513 514
        break;
    case MachineState::TRANSITING_FROM_COMPUTING_TO_SLEEPING:
515
        s = "switching_off";
516 517
        break;
    }
518

519
    return s;
520
}
521

522 523
Machine::Machine(Machines *machines) :
    machines(machines)
524
{
525
    xbt_assert(this->machines != nullptr);
526 527 528 529 530
    const vector<MachineState> machine_states = {MachineState::SLEEPING, MachineState::IDLE,
                                                 MachineState::COMPUTING,
                                                 MachineState::TRANSITING_FROM_SLEEPING_TO_COMPUTING,
                                                 MachineState::TRANSITING_FROM_COMPUTING_TO_SLEEPING};
    for (const MachineState & state : machine_states)
531
    {
532
        time_spent_in_each_state[state] = 0;
533
    }
534 535
}

536 537 538 539 540 541 542 543
Machine::~Machine()
{
    for (auto mit : sleep_pstates)
    {
        delete mit.second;
    }

    sleep_pstates.clear();
544 545 546 547 548 549
    properties.clear();
}

bool Machine::has_role(Permissions role)
{
    return (role | this->permissions) == role;
550 551 552 553 554 555 556 557 558 559
}

bool Machine::has_pstate(int pstate) const
{
    return pstates.find(pstate) != pstates.end();
}

void Machine::display_machine(bool is_energy_used) const
{
    // Let us traverse jobs_being_computed to display some information about them
560 561
    vector<string> jobs_vector;
    for (auto & job : jobs_being_computed)
562
    {
563
        jobs_vector.push_back(job->id.to_string());
564 565 566 567 568
    }

    string str = "Machine\n";
    str += "  id = " + to_string(id) + "\n";
    str += "  name = '" + name + "'\n";
569
    str += "  state = " + machine_state_to_string(state) + "\n";
570
    str += "  jobs_being_computed = [" + boost::algorithm::join(jobs_vector, ", ") + "]\n";
571 572 573 574 575 576 577 578 579 580 581 582

    if (is_energy_used)
    {
        vector<string> pstates_vector;
        vector<string> comp_pstates_vector;
        vector<string> sleep_pstates_vector;
        vector<string> vt_pstates_vector;

        for (auto & mit : pstates)
        {
            pstates_vector.push_back(to_string(mit.first));
            if (mit.second == PStateType::COMPUTATION_PSTATE)
583
            {
584
                comp_pstates_vector.push_back(to_string(mit.first));
585
            }
586
            else if (mit.second == PStateType::SLEEP_PSTATE)
587
            {
588
                sleep_pstates_vector.push_back(to_string(mit.first));
589
            }
590
            else if (mit.second == PStateType::TRANSITION_VIRTUAL_PSTATE)
591
            {
592
                vt_pstates_vector.push_back(to_string(mit.first));
593
            }
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610
        }

        str += "  pstates  = [\n" + boost::algorithm::join(pstates_vector, ", ") + "]\n";
        str += "  computation pstates  = [\n" + boost::algorithm::join(comp_pstates_vector, ", ") + "]\n";
        str += "  sleep pstates  = [\n" + boost::algorithm::join(sleep_pstates_vector, ", ") + "]\n";
        str += "  virtual transition pstates  = [\n" + boost::algorithm::join(vt_pstates_vector, ", ") + "]\n";

        for (auto & mit : sleep_pstates)
        {
            str += "    sleep_ps=" + to_string(mit.second->sleep_pstate) +
                   ", on_ps=" + to_string(mit.second->switch_on_virtual_pstate) +
                   ", off_ps=" + to_string(mit.second->switch_off_virtual_pstate) + "\n";
        }
    }

    XBT_INFO("%s", str.c_str());
}
611 612 613 614 615

string Machine::jobs_being_computed_as_string() const
{
    vector<string> jobs_strings;

616
    for (auto & job : jobs_being_computed)
617
    {
618
        jobs_strings.push_back(job->id.to_string());
619
    }
620 621 622

    return boost::algorithm::join(jobs_strings, ", ");
}
623

624 625
void Machine::update_machine_state(MachineState new_state)
{
626
    Rational current_date = MSG_get_clock();
627

628
    Rational delta_time = current_date - last_state_change_date;
629 630 631 632
    xbt_assert(delta_time >= 0);

    time_spent_in_each_state[state] += delta_time;

633
    machines->update_nb_machines_in_each_state(state, new_state);
634
    state = new_state;
635
    last_state_change_date = current_date;
636 637
}

638
int string_numeric_comparator(const std::string & s1, const std::string & s2)
639
{
640 641 642
    // Const C strings for s1 and s2
    const char * const_c_s1 = s1.c_str();
    const char * const_c_s2 = s2.c_str();
643

644 645 646
    // Iterators
    char * p1 = const_cast<char *>(const_c_s1);
    char * p2 = const_cast<char *>(const_c_s2);
647

648 649 650 651 652 653
    // End bounds (precompute for the sake of performance)
    const char * s1_end = p1 + s1.size();
    const char * s2_end = p2 + s2.size();

    // Traverse the two strings until at least one of them is at end
    for ( ; p1 < s1_end && p2 < s2_end; )
654
    {
655 656
        // Digits are encountered. The integer value is used as a symbol instead of the character.
        if (isdigit(*p1) && isdigit(*p2))
657
        {
658 659 660 661 662 663 664 665 666 667 668
            // The numeric value is read via strtol, which tells where it stopped its reading
            char * end_p1;
            char * end_p2;

            long int int1 = strtol(p1, &end_p1, 10);
            long int int2 = strtol(p2, &end_p2, 10);

            xbt_assert(int1 != LONG_MIN && int1 != LONG_MAX,
                       "Numeric overflow while reading %s", const_c_s1);
            xbt_assert(int2 != LONG_MIN && int1 != LONG_MAX,
                       "Numeric overflow while reading %s", const_c_s2);
669 670

            if (int1 != int2)
671
            {
672
                return int1 - int2;
673
            }
674

675 676 677
            // The string traversal is pursued where strtol stopped
            p1 = end_p1;
            p2 = end_p2;
678 679 680
        }
        else
        {
681 682
            // Classical character by character comparison
            if (*p1 != *p2)
683
            {
684
                return *p1 - *p2;
685
            }
686

687 688 689
            // The string traversal is pursued on the next characters
            ++p1;
            ++p2;
690 691 692
        }
    }

693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
    if (p1 < s1_end)
    {
        // S1 is not at end (but S2 is since the loop condition is false)
        return 1;
    }
    else if (p2 < s2_end)
    {
        // S2 is not at end (but S1 is since the loop condition is false)
        return -1;
    }
    else
    {
        // Both strings are at end. They are equal.
        return 0;
    }
708 709
}

710 711
bool machine_comparator_name(const Machine *m1, const Machine *m2)
{
712 713 714 715 716 717 718
    int cmp_ret = string_numeric_comparator(m1->name, m2->name);

    // If the strings are identical in the numeric meaning, they are compared lexicographically
    if (cmp_ret == 0)
        cmp_ret = strcmp(m1->name.c_str(), m2->name.c_str());

    return cmp_ret <= 0;
719
}
720

721 722 723 724 725 726 727 728 729
void sort_machines_by_ascending_name(std::vector<Machine *> machines_vect)
{
    std::sort(machines_vect.begin(), machines_vect.end(), machine_comparator_name);

    for (unsigned int i = 0; i < machines_vect.size(); ++i)
    {
        machines_vect[i]->id = i;
    }
}
730

731 732 733
void create_machines(const MainArguments & main_args,
                     BatsimContext * context,
                     int max_nb_machines_to_use)
734
{
735
    XBT_INFO("Creating the machines from platform file '%s'...", main_args.platform_filename.c_str());
736 737
    MSG_create_environment(main_args.platform_filename.c_str());

Millian Poquet's avatar
Millian Poquet committed
738 739
    XBT_INFO("Looking for master host '%s'", main_args.master_host_name.c_str());

740
    xbt_dynar_t hosts = MSG_hosts_as_dynar();
741 742 743
    context->machines.create_machines(hosts,
                                      context,
                                      main_args.hosts_roles_map,
744
                                      max_nb_machines_to_use);
745 746
    xbt_dynar_free(&hosts);

747
    XBT_INFO("The machines have been created successfully. There are %d computing machines.",
748
             context->machines.nb_compute_machines());
749
}
750 751 752 753 754

long double consumed_energy_on_machines(BatsimContext * context,
                                        const MachineRange & machines)
{
    if (!context->energy_used)
755
    {
756
        return 0;
757
    }
758 759 760 761 762 763 764 765 766 767 768

    long double consumed_energy = 0;
    for (auto it = machines.elements_begin(); it != machines.elements_end(); ++it)
    {
        int machine_id = *it;
        Machine * machine = context->machines[machine_id];
        consumed_energy += sg_host_get_consumed_energy(machine->host);
    }

    return consumed_energy;
}