S4U Examples
SimGrid comes with an extensive set of examples, documented on this page. Most of them only demonstrate one single feature, with some larger exemplars listed below.
The C++ examples can be found under examples/cpp while python examples are in examples/python. Each such directory contains the source code (also listed from this page), and the so-called tesh file containing how to call the binary obtained by compiling this example and also the expected output. Tesh files are used to turn each of our examples into an integration test. Some examples also contain other files, on need.
A good way to bootstrap your own project is to copy and combine some of the provided examples to constitute the skeleton of what you plan to simulate.
Actors: the Active Entities
Starting and Stopping Actors
Creating actors
Most actors are started from the deployment XML file because this is a better scientific habit, but you can also create them directly from your code.
You create actors either:
Directly with
simgrid::s4u::Actor::create()
From XML with
simgrid::s4u::Engine::register_actor()
(if your actor is a class) orsimgrid::s4u::Engine::register_function()
(if your actor is a function) and thensimgrid::s4u::Engine::load_deployment()
View examples/cpp/actor-create/s4u-actor-create.cpp
/* Copyright (c) 2006-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This example shows how to declare and start your actors.
*
* The first step is to declare the code of your actors (what they do exactly does not matter to this example) and then
* you ask SimGrid to start your actors. There is three ways of doing so:
* - Directly, by instantiating your actor as parameter to Actor::create()
* - By first registering your actors before instantiating it
* - Through the deployment file.
*
* This example shows all these solutions, even if you obviously should use only one of these solutions to start your
* actors. The most advised solution is to use a deployment file, as it creates a clear separation between your
* application and the settings to test it. This is a better scientific methodology. Actually, starting an actor with
* Actor::create() is mostly useful to start an actor from another actor.
*/
#include <simgrid/s4u.hpp>
#include <string>
namespace sg4 = simgrid::s4u;
// This declares a logging channel so that XBT_INFO can be used later
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_actor_create, "The logging channel used in this example");
/* Our first class of actors is simply implemented with a function, that takes a single string as parameter.
*
* Later, this actor class is instantiated within the simulation.
*/
static void receiver(const std::string& mailbox_name)
{
sg4::Mailbox* mailbox = sg4::Mailbox::by_name(mailbox_name);
XBT_INFO("Hello s4u, I'm ready to get any message you'd want on %s", mailbox->get_cname());
auto msg1 = mailbox->get_unique<std::string>();
auto msg2 = mailbox->get_unique<std::string>();
auto msg3 = mailbox->get_unique<std::string>();
XBT_INFO("I received '%s', '%s' and '%s'", msg1->c_str(), msg2->c_str(), msg3->c_str());
XBT_INFO("I'm done. See you.");
}
/* Our second class of actors is also a function */
static void forwarder(int argc, char** argv)
{
xbt_assert(argc >= 3, "Actor forwarder requires 2 parameters, but got only %d", argc - 1);
sg4::Mailbox* in = sg4::Mailbox::by_name(argv[1]);
sg4::Mailbox* out = sg4::Mailbox::by_name(argv[2]);
auto* msg = in->get<std::string>();
XBT_INFO("Forward '%s'.", msg->c_str());
out->put(msg, msg->size());
}
/* Declares a third class of actors which sends a message to the mailbox 'mb42'.
* The sent message is what was passed as parameter on creation (or 'GaBuZoMeu' by default)
*
* Later, this actor class is instantiated twice in the simulation.
*/
class Sender {
public:
std::string mbox = "mb42";
std::string msg = "GaBuZoMeu";
explicit Sender() = default; /* Sending the default message */
explicit Sender(const std::string& arg) : msg(arg) { /* Sending the specified message */}
explicit Sender(std::vector<std::string> args)
{
/* This constructor is used when we start the actor from the deployment file */
/* args[0] is the actor's name, so the first parameter is args[1] */
xbt_assert(args.size() >= 3, "The sender is expecting 2 parameters from the deployment file but got %zu",
args.size() - 1);
msg = args[1];
mbox = args[2];
}
void operator()() const /* This is the main code of the actor */
{
XBT_INFO("Hello s4u, I have something to send");
sg4::Mailbox* mailbox = sg4::Mailbox::by_name(mbox);
mailbox->put(new std::string(msg), msg.size());
XBT_INFO("I'm done. See you.");
}
};
/* Here comes the main function of your program */
int main(int argc, char** argv)
{
/* When your program starts, you have to first start a new simulation engine, as follows */
sg4::Engine e(&argc, argv);
/* Then you should load a platform file, describing your simulated platform */
e.load_platform(argc > 1 ? argv[1] : "../../platforms/small_platform.xml");
/* And now you have to ask SimGrid to actually start your actors.
*
* The easiest way to do so is to implement the behavior of your actor in a single function,
* as we do here for the receiver actors. This function can take any kind of parameters, as
* long as the last parameters of Actor::create() match what your function expects.
*/
sg4::Actor::create("receiver", e.host_by_name("Fafard"), &receiver, "mb42");
/* If your actor is getting more complex, you probably want to implement it as a class instead,
* as we do here for the sender actors. The main behavior goes into operator()() of the class.
*
* You can then directly start your actor, as follows: */
sg4::Actor::create("sender1", e.host_by_name("Tremblay"), Sender());
/* If you want to pass parameters to your class, that's very easy: just use your constructors */
sg4::Actor::create("sender2", e.host_by_name("Jupiter"), Sender("GloubiBoulga"));
/* But starting actors directly is considered as a bad experimental habit, since it ties the code
* you want to test with the experimental scenario. Starting your actors from an external deployment
* file in XML ensures that you can test your code in several scenarios without changing the code itself.
*
* For that, you first need to register your function or your actor as follows.
* Actor classes must have a (std::vector<std::string>) constructor,
* and actor functions must be of type int(*)(int argc, char**argv). */
e.register_actor<Sender>("sender"); // The sender class is passed as a template parameter here
e.register_function("forwarder", &forwarder);
/* Once actors and functions are registered, just load the deployment file */
e.load_deployment(argc > 2 ? argv[2] : "s4u-actor-create_d.xml");
/* Once every actors are started in the engine, the simulation can start */
e.run();
/* Once the simulation is done, the program is ended */
return 0;
}
You create actors either:
Directly with
simgrid.Actor.create()
From XML with
simgrid.Engine.register_actor()
and thensimgrid.Engine.load_deployment()
View examples/python/actor-create/actor-create.py
# Copyright (c) 2006-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
This example shows how to declare and start your actors.
The first step is to declare the code of your actors (what they do exactly does not matter to this example) and then
you ask SimGrid to start your actors. There is three ways of doing so:
- Directly, by instantiating your actor as parameter to Actor::create()
- By first registering your actors before instantiating it;
- Through the deployment file.
This example shows all these solutions, even if you obviously should use only one of these solutions to start your
actors. The most advised solution is to use a deployment file, as it creates a clear separation between your
application and the settings to test it. This is a better scientific methodology. Actually, starting an actor with
Actor.create() is mostly useful to start an actor from another actor.
"""
import sys
from simgrid import Actor, Engine, Host, Mailbox, this_actor
def receiver(mailbox_name):
"""
Our first class of actors is simply implemented with a function, that takes a single string as parameter.
Later, this actor class is instantiated within the simulation.
"""
mailbox = Mailbox.by_name(mailbox_name)
this_actor.info(
"Hello s4u, I'm ready to get any message you'd want on {:s}".format(mailbox.name))
msg1 = mailbox.get()
msg2 = mailbox.get()
msg3 = mailbox.get()
this_actor.info(
"I received '{:s}', '{:s}' and '{:s}'".format(msg1, msg2, msg3))
this_actor.info("I'm done. See you.")
def forwarder(*args):
"""Our second class of actors is also a function"""
if len(args) < 2:
raise AssertionError(
"Actor forwarder requires 2 parameters, but got only {:d}".format(len(args)))
mb_in = Mailbox.by_name(args[0])
mb_out = Mailbox.by_name(args[1])
msg = mb_in.get()
this_actor.info("Forward '{:s}'.".format(msg))
mb_out.put(msg, len(msg))
class Sender:
"""
Declares a third class of actors which sends a message to the mailbox 'mb42'.
The sent message is what was passed as parameter on creation (or 'GaBuZoMeu' by default)
Later, this actor class is instantiated twice in the simulation.
"""
def __init__(self, msg="GaBuZoMeu", mbox="mb42"):
self.msg = msg
self.mbox = mbox
# Actors that are created as object will execute their __call__ method.
# So, the following constitutes the main function of the Sender actor.
def __call__(self):
this_actor.info("Hello s4u, I have something to send")
mailbox = Mailbox.by_name(self.mbox)
mailbox.put(self.msg, len(self.msg))
this_actor.info("I'm done. See you.")
if __name__ == '__main__':
# Here comes the main function of your program
# When your program starts, you have to first start a new simulation engine, as follows
e = Engine(sys.argv)
# Then you should load a platform file, describing your simulated platform
e.load_platform("../../platforms/small_platform.xml")
# And now you have to ask SimGrid to actually start your actors.
#
# The easiest way to do so is to implement the behavior of your actor in a single function,
# as we do here for the receiver actors. This function can take any kind of parameters, as
# long as the last parameters of Actor::create() match what your function expects.
Actor.create("receiver", Host.by_name("Fafard"), receiver, "mb42")
# If your actor is getting more complex, you probably want to implement it as a class instead,
# as we do here for the sender actors. The main behavior goes into operator()() of the class.
#
# You can then directly start your actor, as follows:
Actor.create("sender1", Host.by_name("Tremblay"), Sender())
# If you want to pass parameters to your class, that's very easy: just use your constructors
Actor.create("sender2", Host.by_name("Jupiter"), Sender("GloubiBoulga"))
# But starting actors directly is considered as a bad experimental habit, since it ties the code
# you want to test with the experimental scenario. Starting your actors from an external deployment
# file in XML ensures that you can test your code in several scenarios without changing the code itself.
#
# For that, you first need to register your function or your actor as follows.
e.register_actor("sender", Sender)
e.register_actor("forwarder", forwarder)
# Once actors and functions are registered, just load the deployment file
e.load_deployment("actor-create_d.xml")
# Once every actors are started in the engine, the simulation can start
e.run()
# Once the simulation is done, the program is ended
You create actors either:
Directly with
sg_actor_create()
followed bysg_actor_start()
.From XML with
simgrid_register_function()
and thensimgrid_load_deployment()
.
View examples/c/actor-create/actor-create.c
/* Copyright (c) 2006-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This example shows how to declare and start your actors.
*
* The first step is to declare the code of your actors (what they do exactly does not matter to this example) and then
* you ask SimGrid to start your actors. There is three ways of doing so:
* - Directly, by instantiating your actor as parameter to Actor::create()
* - By first registering your actors before instantiating it;
* - Through the deployment file.
*
* This example shows all these solutions, even if you obviously should use only one of these solutions to start your
* actors. The most advised solution is to use a deployment file, as it creates a clear separation between your
* application and the settings to test it. This is a better scientific methodology. Actually, starting an actor with
* Actor::create() is mostly useful to start an actor from another actor.
*/
#include "simgrid/forward.h"
#include <simgrid/actor.h>
#include <simgrid/engine.h>
#include <simgrid/host.h>
#include <simgrid/mailbox.h>
#include <xbt/log.h>
#include <xbt/sysdep.h>
// This declares a logging channel so that XBT_INFO can be used later
XBT_LOG_NEW_DEFAULT_CATEGORY(actor_create, "The logging channel used in this example");
/* Our first class of actors is simply implemented with a function.
* It follows a pthread-like prototype with a single void* parameter, and it does not return anything.
*
* One 'receiver' actor is instantiated within the simulation later in this file.
*/
static void receiver(void* mb)
{
sg_mailbox_t mailbox = mb;
XBT_INFO("Hello, I'm ready to get any message you'd want on %s", sg_mailbox_get_name(mailbox));
char* msg1 = sg_mailbox_get(mailbox);
char* msg2 = sg_mailbox_get(mailbox);
char* msg3 = sg_mailbox_get(mailbox);
XBT_INFO("I received '%s', '%s' and '%s'", msg1, msg2, msg3);
xbt_free(msg1);
xbt_free(msg2);
xbt_free(msg3);
XBT_INFO("I'm done. See you.");
}
/* Our second class of actors, in charge of sending stuff.
* It follows a main-like prototype with argc/argv parameters, and it does not return anything.
*/
static void sender(int argc, char** argv)
{
xbt_assert(argc == 3, "Actor 'sender' requires 2 parameters (mailbox and data to send), but got only %d", argc - 1);
XBT_INFO("Hello, I have something to send");
const char* sent_data = argv[1];
sg_mailbox_t mailbox = sg_mailbox_by_name(argv[2]);
sg_mailbox_put(mailbox, xbt_strdup(sent_data), strlen(sent_data));
XBT_INFO("I'm done. See you.");
}
/* Our third class of actors, in charge of forwarding stuff. Also following a main-like prototype. */
static void forwarder(int argc, char** argv)
{
xbt_assert(argc >= 3, "Actor forwarder requires 2 parameters, but got only %d", argc - 1);
sg_mailbox_t mailbox_in = sg_mailbox_by_name(argv[1]);
sg_mailbox_t mailbox_out = sg_mailbox_by_name(argv[2]);
char* msg = sg_mailbox_get(mailbox_in);
XBT_INFO("Forward '%s'.", msg);
sg_mailbox_put(mailbox_out, msg, strlen(msg));
}
/* Here comes the main function of your program */
int main(int argc, char** argv)
{
/* When your program starts, you have to first start a new simulation engine, as follows */
simgrid_init(&argc, argv);
/* Then you should load a platform file, describing your simulated platform */
simgrid_load_platform("../../platforms/small_platform.xml");
/* And now you have to ask SimGrid to actually start your actors.
*
* The easiest way to do so is to implement the behavior of your actor in a single function, as we do here for the
* receiver actors. In C, you can either use a main-like prototype with argc/argv, as the sender in this example, or a
* pthread-like prototype with void*, as the receiver in this example.
*
* If you go for the argc/argv prototype, you can either create your actor in one shot with sg_actor_create_() or
* sg_actor_create() (depending on the const-ness of your parameters), or split it into sg_actor_init() and then
* sg_actor_start().
*
* If you go for the void* prototype, you must call sg_actor_init() and then sg_actor_start_voidp().
*/
sg_actor_t recv_actor = sg_actor_init("receiver", sg_host_by_name("Fafard"));
sg_actor_start_voidp(recv_actor, &receiver, sg_mailbox_by_name("mb42"));
int sender1_argc = 3;
const char* sender1_argv[] = {"sender", "GaBuZoMeu", "mb42", NULL};
sg_actor_create_("sender1", sg_host_by_name("Tremblay"), &sender, sender1_argc, sender1_argv);
int sender2_argc = 3;
const char* sender2_argv[] = {"sender", "GloubiBoulga", "mb42", NULL};
sg_actor_t sender2 = sg_actor_init("sender2", sg_host_by_name("Jupiter"));
sg_actor_start_(sender2, &sender, sender2_argc, sender2_argv);
/* But starting actors directly is considered as a bad experimental habit, since it ties the code
* you want to test with the experimental scenario. Starting your actors from an external deployment
* file in XML ensures that you can test your code in several scenarios without changing the code itself.
*
* For that, you first need to register your function or your actor as follows.
* actor functions must be of type void(*)(int argc, char**argv). */
simgrid_register_function("sender", sender);
simgrid_register_function("forwarder", forwarder);
/* Once actors and functions are registered, just load the deployment file */
simgrid_load_deployment("actor-create_d.xml");
/* Once every actors are started in the engine, the simulation can start */
simgrid_run();
/* Once the simulation is done, the program is ended */
return 0;
}
The following file is used in both C++ and Python.
View examples/python/actor-create/actor-create_d.xml
<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
<platform version="4.1">
<!-- This a weird deployment file: we only start one actor from here and the others from the main().
-
- This is only for the example, but don't do that at home.
- Instead, you want to start all your actors from the deployment file.
-->
<actor host="Fafard" function="sender">
<argument value="PopPop!"/> <!-- msg as argv[1] -->
<argument value="other mb"/> <!-- mbox as argv[2] -->
</actor>
<actor host="Ginette" function="forwarder">
<argument value="other mb"/>
<argument value="mb42"/>
</actor>
</platform>
Reacting to actors’ end
You can attach callbacks to the end of actors. There are several ways of doing so, depending on whether you want to attach your callback to a given actor and on how you define the end of a given actor. User code probably wants to react to the termination of an actor while some plugins want to react to the destruction (memory collection) of actors.
This example shows how to attach a callback to:
the end of a specific actor:
simgrid::s4u::Actor::on_exit()
the end of any actor:
simgrid::s4u::Actor::on_termination_cb()
the destruction of any actor:
simgrid::s4u::Actor::on_destruction_cb()
View examples/cpp/actor-exiting/s4u-actor-exiting.cpp
Download s4u-actor-exiting.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* There is two very different ways of being informed when an actor exits.
*
* The this_actor::on_exit() function allows one to register a function to be
* executed when this very actor exits. The registered function will run
* when this actor terminates (either because its main function returns, or
* because it's killed in any way). No simcall are allowed here: your actor
* is dead already, so it cannot interact with its environment in any way
* (network, executions, disks, etc).
*
* Usually, the functions registered in this_actor::on_exit() are in charge
* of releasing any memory allocated by the actor during its execution.
*
* The other way of getting informed when an actor terminates is to connect a
* function in the Actor::on_termination signal, that is shared between
* all actors. Callbacks to this signal are executed for each terminating
* actors, no matter what. This is useful in many cases, in particular
* when developing SimGrid plugins.
*
* Finally, you can attach callbacks to the Actor::on_destruction signal.
* It is also shared between all actors, and gets fired when the actors
* are destroyed. A delay is possible between the termination of an actor
* (ie, when it terminates executing its code) and its destruction (ie,
* when it is not referenced anywhere in the simulation and can be collected).
*
* In both cases, you can stack more than one callback in the signal.
* They will all be executed in the registration order.
*/
#include <simgrid/s4u.hpp>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_actor_exiting, "Messages specific for this s4u example");
static void actor_a()
{
// Register a lambda function to be executed once it stops
sg4::this_actor::on_exit([](bool /*failed*/) { XBT_INFO("I stop now"); });
sg4::this_actor::sleep_for(1);
}
static void actor_b()
{
sg4::this_actor::sleep_for(2);
}
static void actor_c()
{
// Register a lambda function to be executed once it stops
sg4::this_actor::on_exit([](bool failed) {
if (failed) {
XBT_INFO("I was killed!");
if (xbt_log_no_loc)
XBT_INFO("The backtrace would be displayed here if --log=no_loc would not have been passed");
else
xbt_backtrace_display_current();
} else
XBT_INFO("Exiting gracefully.");
});
sg4::this_actor::sleep_for(3);
XBT_INFO("And now, induce a deadlock by waiting for a message that will never come\n\n");
sg4::Mailbox::by_name("nobody")->get<void>();
xbt_die("Receiving is not supposed to succeed when nobody is sending");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s ../platforms/small_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]); /* - Load the platform description */
/* Register a callback in the Actor::on_termination signal. It will be called for every terminated actors */
sg4::Actor::on_termination_cb(
[](sg4::Actor const& actor) { XBT_INFO("Actor %s terminates now", actor.get_cname()); });
/* Register a callback in the Actor::on_destruction signal. It will be called for every destructed actors */
sg4::Actor::on_destruction_cb(
[](sg4::Actor const& actor) { XBT_INFO("Actor %s gets destroyed now", actor.get_cname()); });
/* Create some actors */
sg4::Actor::create("A", e.host_by_name("Tremblay"), actor_a);
sg4::Actor::create("B", e.host_by_name("Fafard"), actor_b);
sg4::Actor::create("C", e.host_by_name("Ginette"), actor_c);
e.run(); /* - Run the simulation */
return 0;
}
This example shows how to attach a callback to the end of a specific actor with
sg_actor_on_exit()
.
View examples/c/actor-exiting/actor-exiting.c
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* In C, there is a single way of being informed when an actor exits.
*
* The sg_actor_on_exit() function allows one to register a function to be
* executed when this very actor exits. The registered function will run
* when this actor terminates (either because its main function returns, or
* because it's killed in any way). No simcall are allowed here: your actor
* is dead already, so it cannot interact with its environment in any way
* (network, executions, disks, etc).
*
* Usually, the functions registered in sg_actor_on_exit() are in charge
* of releasing any memory allocated by the actor during its execution.
*/
#include <simgrid/actor.h>
#include <simgrid/engine.h>
#include <simgrid/host.h>
#include <simgrid/mailbox.h>
#include <xbt/asserts.h>
#include <xbt/log.h>
XBT_LOG_NEW_DEFAULT_CATEGORY(actor_exiting, "Messages specific for this example");
static void A_on_exit(int ignored1, void* ignored2)
{
XBT_INFO("I stop now");
}
static void actorA_fun(int argc, char* argv[])
{
// Register a lambda function to be executed once it stops
sg_actor_on_exit(&A_on_exit, NULL);
sg_actor_sleep_for(1);
}
static void actorB_fun(int argc, char* argv[])
{
sg_actor_sleep_for(2);
}
static void C_on_exit(int failed, void* ignored2)
{
if (failed) {
XBT_INFO("I was killed!");
if (xbt_log_no_loc)
XBT_INFO("The backtrace would be displayed here if --log=no_loc would not have been passed");
else
xbt_backtrace_display_current();
} else
XBT_INFO("Exiting gracefully.");
}
static void actorC_fun(int argc, char* argv[])
{
// Register a lambda function to be executed once it stops
sg_actor_on_exit(&C_on_exit, NULL);
sg_actor_sleep_for(3);
XBT_INFO("And now, induce a deadlock by waiting for a message that will never come\n\n");
sg_mailbox_get(sg_mailbox_by_name("nobody"));
xbt_die("Receiving is not supposed to succeed when nobody is sending");
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s ../platforms/small_platform.xml\n", argv[0], argv[0]);
simgrid_load_platform(argv[1]); /* - Load the platform description */
sg_actor_create("A", sg_host_by_name("Tremblay"), &actorA_fun, 0, NULL);
sg_actor_create("B", sg_host_by_name("Fafard"), &actorB_fun, 0, NULL);
sg_actor_create("C", sg_host_by_name("Ginette"), &actorC_fun, 0, NULL);
simgrid_run();
return 0;
}
Killing actors
Actors can forcefully stop other actors.
See also void simgrid::s4u::Actor::kill(void)
, void simgrid::s4u::Actor::kill_all()
,
simgrid::s4u::this_actor::exit()
, simgrid::s4u::Actor::on_exit()
.
View examples/cpp/actor-kill/s4u-actor-kill.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <simgrid/s4u.hpp>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_actor_kill, "Messages specific for this s4u example");
static void victimA_fun()
{
sg4::this_actor::on_exit([](bool /*failed*/) { XBT_INFO("I have been killed!"); });
XBT_INFO("Hello!");
XBT_INFO("Suspending myself");
sg4::this_actor::suspend(); /* - Start by suspending itself */
XBT_INFO("OK, OK. Let's work"); /* - Then is resumed and start to execute some flops */
sg4::this_actor::execute(1e9);
XBT_INFO("Bye!"); /* - But will never reach the end of it */
}
static void victimB_fun()
{
XBT_INFO("Terminate before being killed");
}
static void killer()
{
XBT_INFO("Hello!"); /* - First start a victim actor */
sg4::ActorPtr victimA = sg4::Actor::create("victim A", sg4::Host::by_name("Fafard"), victimA_fun);
sg4::ActorPtr victimB = sg4::Actor::create("victim B", sg4::Host::by_name("Jupiter"), victimB_fun);
sg4::this_actor::sleep_for(10); /* - Wait for 10 seconds */
XBT_INFO("Resume the victim A"); /* - Resume it from its suspended state */
victimA->resume();
sg4::this_actor::sleep_for(2);
XBT_INFO("Kill the victim A"); /* - and then kill it */
sg4::Actor::by_pid(victimA->get_pid())->kill(); // You can retrieve an actor from its PID (and then kill it)
sg4::this_actor::sleep_for(1);
XBT_INFO("Kill victimB, even if it's already dead"); /* that's a no-op, there is no zombies in SimGrid */
victimB->kill(); // the actor is automatically garbage-collected after this last reference
sg4::this_actor::sleep_for(1);
XBT_INFO("Start a new actor, and kill it right away");
sg4::ActorPtr victimC = sg4::Actor::create("victim C", sg4::Host::by_name("Jupiter"), victimA_fun);
victimC->kill();
sg4::this_actor::sleep_for(1);
XBT_INFO("Killing everybody but myself");
sg4::Actor::kill_all();
XBT_INFO("OK, goodbye now. I commit a suicide.");
sg4::this_actor::exit();
XBT_INFO("This line never gets displayed: I'm already dead since the previous line.");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s ../platforms/small_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]); /* - Load the platform description */
/* - Create and deploy killer actor, that will create the victim actors */
sg4::Actor::create("killer", e.host_by_name("Tremblay"), killer);
e.run(); /* - Run the simulation */
return 0;
}
See also simgrid.Actor.kill()
, simgrid.Actor.kill_all()
, simgrid.this_actor.exit()
,
simgrid.this_actor.on_exit()
.
View examples/python/actor-kill/actor-kill.py
# Copyright (c) 2017-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
Usage: actor-kill.py platform_file [other parameters]
"""
import sys
from simgrid import Actor, Engine, Host, this_actor
def victim_a_fun():
this_actor.on_exit(lambda forcefully: this_actor.info("I have been killed!" if forcefully else "I finish now."))
this_actor.info("Hello!")
this_actor.info("Suspending myself")
this_actor.suspend() # - Start by suspending itself
# - Then is resumed and start to execute a task
this_actor.info("OK, OK. Let's work")
this_actor.execute(1e9)
# - But will never reach the end of it
this_actor.info("Bye!")
def victim_b_fun():
this_actor.info("Terminate before being killed")
def killer():
this_actor.info("Hello!") # - First start a victim actor
victim_a = Actor.create("victim A", Host.by_name("Fafard"), victim_a_fun)
victim_b = Actor.create("victim B", Host.by_name("Jupiter"), victim_b_fun)
this_actor.sleep_for(10) # - Wait for 10 seconds
# - Resume it from its suspended state
this_actor.info("Resume the victim A")
victim_a.resume()
this_actor.sleep_for(2)
this_actor.info("Kill the victim A") # - and then kill it
Actor.by_pid(victim_a.pid).kill() # You can retrieve an actor from its PID (and then kill it)
this_actor.sleep_for(1)
# that's a no-op, there is no zombies in SimGrid
this_actor.info("Kill victim B, even if it's already dead")
victim_b.kill()
this_actor.sleep_for(1)
this_actor.info("Start a new actor, and kill it right away")
victim_c = Actor.create("victim C", Host.by_name("Jupiter"), victim_a_fun)
victim_c.kill()
this_actor.sleep_for(1)
this_actor.info("Killing everybody but myself")
Actor.kill_all()
this_actor.info("OK, goodbye now. I commit a suicide.")
this_actor.exit()
this_actor.info(
"This line never gets displayed: I'm already dead since the previous line.")
if __name__ == '__main__':
e = Engine(sys.argv)
if len(sys.argv) < 2:
raise AssertionError(
"Usage: actor-kill.py platform_file [other parameters]")
e.load_platform(sys.argv[1]) # Load the platform description
# Create and deploy killer actor, that will create the victim actors
Actor.create("killer", Host.by_name("Tremblay"), killer)
e.run()
See also sg_actor_kill()
, sg_actor_kill_all()
, sg_actor_exit()
, sg_actor_on_exit()
.
View examples/c/actor-kill/actor-kill.c
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "xbt/log.h"
#include "xbt/sysdep.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(actor_kill, "Messages specific for this example");
static void victim_on_exit(int ignored1, void* ignored2)
{
XBT_INFO("I have been killed!");
}
static void victimA_fun(int argc, char* argv[])
{
sg_actor_on_exit(&victim_on_exit, NULL);
XBT_INFO("Hello!");
XBT_INFO("Suspending myself");
sg_actor_suspend(sg_actor_self()); /* - First suspend itself */
XBT_INFO("OK, OK. Let's work"); /* - Then is resumed and start to execute a task */
sg_actor_execute(1e9);
XBT_INFO("Bye!"); /* - But will never reach the end of it */
}
static void victimB_fun(int argc, char* argv[])
{
XBT_INFO("Terminate before being killed");
}
static void killer_fun(int argc, char* argv[])
{
XBT_INFO("Hello!"); /* - First start a victim actor */
sg_actor_t victimA = sg_actor_create("victim A", sg_host_by_name("Fafard"), &victimA_fun, 0, NULL);
sg_actor_t victimB = sg_actor_create("victim B", sg_host_by_name("Jupiter"), &victimB_fun, 0, NULL);
sg_actor_ref(victimB); // We have to take that ref because victimB will end before we try to kill it
sg_actor_sleep_for(10.0);
XBT_INFO("Resume the victim A"); /* - Resume it from its suspended state */
sg_actor_resume(victimA);
sg_actor_sleep_for(2.0);
XBT_INFO("Kill the victim A"); /* - and then kill it */
sg_actor_kill(victimA);
sg_actor_sleep_for(1.0);
XBT_INFO("Kill victimB, even if it's already dead"); /* that's a no-op, there is no zombies in SimGrid */
sg_actor_kill(victimB); // the actor is automatically garbage-collected after this last reference
sg_actor_unref(victimB); // Release the ref taken on victimB to avoid to leak memory
sg_actor_sleep_for(1.0);
XBT_INFO("Start a new actor, and kill it right away");
sg_actor_t victimC = sg_actor_create("victim C", sg_host_by_name("Jupiter"), &victimA_fun, 0, NULL);
sg_actor_kill(victimC);
sg_actor_sleep_for(1.0);
XBT_INFO("Killing everybody but myself");
sg_actor_kill_all();
XBT_INFO("OK, goodbye now. I commit a suicide.");
sg_actor_exit();
XBT_INFO("This line will never get displayed: I'm already dead since the previous line.");
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s platform.xml\n", argv[0], argv[0]);
simgrid_load_platform(argv[1]);
/* - Create and deploy killer actor, that will create the victim actor */
sg_actor_create("killer", sg_host_by_name("Tremblay"), &killer_fun, 0, NULL);
simgrid_run();
return 0;
}
Actors’ life cycle from XML_reference
You can specify a start time and a kill time in the deployment file.
This file is not really interesting: the important matter is in the XML file.
View examples/cpp/actor-lifetime/s4u-actor-lifetime.cpp
Download s4u-actor-lifetime.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This C++ file acts as the foil to the corresponding XML file, where the
action takes place: Actors are started and stopped at predefined time. */
#include "simgrid/s4u.hpp"
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(test, "Messages specific for this s4u example");
/* This actor just sleeps until termination */
class sleeper {
public:
explicit sleeper(std::vector<std::string> /*args*/)
{
sg4::this_actor::on_exit([](bool /*failed*/) {
/* Executed on actor termination, to display a message helping to understand the output */
XBT_INFO("Exiting now (done sleeping or got killed).");
});
}
void operator()() const
{
XBT_INFO("Hello! I go to sleep.");
sg4::this_actor::sleep_for(10);
XBT_INFO("Done sleeping.");
}
};
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 2,
"Usage: %s platform_file deployment_file\n"
"\tExample: %s ../platforms/cluster_backbone.xml ./s4u_actor_lifetime_d.xml\n",
argv[0], argv[0]);
e.load_platform(argv[1]); /* Load the platform description */
e.register_actor<sleeper>("sleeper");
e.load_deployment(argv[2]); /* Deploy the sleeper actors with explicit start/kill times */
e.run(); /* - Run the simulation */
return 0;
}
This demonstrates the start_time
and kill_time
attribute of the <actor> tag.
View examples/cpp/actor-lifetime/s4u-actor-lifetime_d.xml
Download s4u-actor-lifetime_d.xml
<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
<!-- This shows how to use the start_time and kill_time attributes of <actors> -->
<platform version="4.1">
<actor host="node-0.simgrid.org" function="sleeper" />
<actor host="node-0.simgrid.org" function="sleeper" start_time="2" />
<actor host="node-1.simgrid.org" function="sleeper" kill_time="3" />
<actor host="node-2.simgrid.org" function="sleeper" start_time="4" kill_time="7" />
</platform>
This file is not really interesting: the important matter is in the XML file.
View examples/python/actor-lifetime/actor-lifetime.py
# Copyright (c) 2007-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
This Python file acts as the foil to the corresponding XML file, where the
action takes place: Actors are started and stopped at predefined time
"""
import sys
from simgrid import Engine, this_actor
class Sleeper:
"""This actor just sleeps until termination"""
def __init__(self):
this_actor.on_exit(lambda killed: this_actor.info("Exiting now (killed)." if killed else "Exiting now (finishing)."))
def __call__(self):
this_actor.info("Hello! I go to sleep.")
this_actor.sleep_for(10)
this_actor.info("Done sleeping.")
if __name__ == '__main__':
e = Engine(sys.argv)
if len(sys.argv) < 2:
raise AssertionError(
"Usage: actor-lifetime.py platform_file actor-lifetime_d.xml [other parameters]")
e.load_platform(sys.argv[1]) # Load the platform description
e.register_actor("sleeper", Sleeper)
# Deploy the sleeper actors with explicit start/kill times
e.load_deployment(sys.argv[2])
e.run()
This file is not really interesting: the important matter is in the XML file.
View examples/c/actor-lifetime/actor-lifetime.c
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "xbt/asserts.h"
#include "xbt/log.h"
#include <stdio.h> /* snprintf */
XBT_LOG_NEW_DEFAULT_CATEGORY(test, "Messages specific for this example");
/* Executed on actor termination*/
static void my_onexit(int ignored1, void* ignored2)
{
XBT_INFO("Exiting now (done sleeping or got killed)."); /* - Just display an informative message (see tesh file) */
}
/* Just sleep until termination */
static void sleeper(int argc, char* argv[])
{
sg_actor_on_exit(my_onexit, NULL);
XBT_INFO("Hello! I go to sleep.");
sg_actor_sleep_for(10);
XBT_INFO("Done sleeping.");
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 2,
"Usage: %s platform_file deployment_file\n"
"\tExample: %s platform.xml deployment.xml\n",
argv[0], argv[0]);
simgrid_load_platform(argv[1]); /* - Load the platform description */
simgrid_register_function("sleeper", sleeper);
simgrid_load_deployment(argv[2]); /* - Deploy the sleeper actors with explicit start/kill times */
simgrid_run(); /* - Run the simulation */
return 0;
}
Daemon actors
Some actors may be intended to simulate daemons that run in the background. This example shows how to transform a regular actor into a daemon that will be automatically killed once the simulation is over.
See also simgrid::s4u::Actor::daemonize()
and simgrid::s4u::Actor::is_daemon()
.
View examples/cpp/actor-daemon/s4u-actor-daemon.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_actor_daemon, "Messages specific for this s4u example");
/* The worker actor, working for a while before leaving */
static void worker()
{
XBT_INFO("Let's do some work (for 10 sec on Boivin).");
sg4::this_actor::execute(980.95e6);
XBT_INFO("I'm done now. I leave even if it makes the daemon die.");
}
/* The daemon, displaying a message every 3 seconds until all other actors stop */
static void my_daemon()
{
sg4::Actor::self()->daemonize();
while (sg4::this_actor::get_host()->is_on()) {
XBT_INFO("Hello from the infinite loop");
sg4::this_actor::sleep_for(3.0);
}
XBT_INFO("I will never reach that point: daemons are killed when regular actors are done");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("worker", e.host_by_name("Boivin"), worker);
sg4::Actor::create("daemon", e.host_by_name("Tremblay"), my_daemon);
e.run();
return 0;
}
See also simgrid.Actor.daemonize()
and simgrid.Actor.is_daemon()
.
View examples/python/actor-daemon/actor-daemon.py
# Copyright (c) 2017-2024. The SimGrid Team. All rights reserved.
#
# This program is free software you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
Usage: actor-daemon.py platform_file [other parameters]
"""
import sys
from simgrid import Actor, Engine, Host, this_actor
def worker():
"""The worker actor, working for a while before leaving"""
this_actor.info("Let's do some work (for 10 sec on Boivin).")
this_actor.execute(980.95e6)
this_actor.info("I'm done now. I leave even if it makes the daemon die.")
def my_daemon():
"""The daemon, displaying a message every 3 seconds until all other actors stop"""
Actor.self().daemonize()
while True:
this_actor.info("Hello from the infinite loop")
this_actor.sleep_for(3.0)
this_actor.info(
"I will never reach that point: daemons are killed when regular actors are done")
if __name__ == '__main__':
e = Engine(sys.argv)
if len(sys.argv) < 2:
raise AssertionError(
"Usage: actor-daemon.py platform_file [other parameters]")
e.load_platform(sys.argv[1])
Actor.create("worker", Host.by_name("Boivin"), worker)
Actor.create("daemon", Host.by_name("Tremblay"), my_daemon)
e.run()
See also sg_actor_daemonize()
and sg_actor_is_daemon()
.
View examples/c/actor-daemon/actor-daemon.c
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "xbt/asserts.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(actor_daemon, "Messages specific for this example");
/* The worker actor, working for a while before leaving */
static void worker(int argc, char* argv[])
{
XBT_INFO("Let's do some work (for 10 sec on Boivin).");
sg_actor_execute(980.95e6);
XBT_INFO("I'm done now. I leave even if it makes the daemon die.");
}
/* The daemon, displaying a message every 3 seconds until all other actors stop */
static void my_daemon(int argc, char* argv[])
{
sg_actor_daemonize(sg_actor_self());
while (1) {
XBT_INFO("Hello from the infinite loop");
sg_actor_sleep_for(3.0);
}
XBT_INFO("I will never reach that point: daemons are killed when regular actors are done");
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform.xml\n", argv[0]);
simgrid_load_platform(argv[1]);
sg_actor_create("worker", sg_host_by_name("Boivin"), &worker, 0, NULL);
sg_actor_create("daemon", sg_host_by_name("Tremblay"), &my_daemon, 0, NULL);
simgrid_run();
}
Specifying the stack size
The stack size can be specified by default on the command line,
globally by changing the configuration with simgrid::s4u::Engine::set_config()
,
or for a specific actor using simgrid::s4u::Actor::set_stacksize()
before its start.
View examples/cpp/actor-stacksize/s4u-actor-stacksize.cpp
Download s4u-actor-stacksize.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This code tests that we can change the stack-size between the actors creation. */
#include <simgrid/s4u.hpp>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
static void actor()
{
XBT_INFO("Hello");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
// If you don't specify anything, you get the default size (8Mb) or the one passed on the command line
sg4::Actor::create("actor", e.host_by_name("Tremblay"), actor);
// You can use set_config(string) to pass a size that will be parsed. That value will be used for any subsequent
// actors
sg4::Engine::set_config("contexts/stack-size:16384");
sg4::Actor::create("actor", e.host_by_name("Tremblay"), actor);
sg4::Actor::create("actor", e.host_by_name("Tremblay"), actor);
// You can use set_config(key, value) for the same effect.
sg4::Engine::set_config("contexts/stack-size", 32 * 1024);
sg4::Actor::create("actor", e.host_by_name("Tremblay"), actor);
sg4::Actor::create("actor", e.host_by_name("Tremblay"), actor);
// Or you can use set_stacksize() before starting the actor to modify only this one
sg4::Actor::init("actor", e.host_by_name("Tremblay"))->set_stacksize(64 * 1024)->start(actor);
sg4::Actor::create("actor", e.host_by_name("Tremblay"), actor);
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
View examples/c/actor-stacksize/actor-stacksize.c
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This code tests that we can change the stack-size between the actors creation. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "xbt/config.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(actor_stacksize, "Messages specific for this example");
static void actor(int argc, char* argv[])
{
XBT_INFO("Hello");
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
simgrid_load_platform(argv[1]);
// If you don't specify anything, you get the default size (8Mb) or the one passed on the command line
sg_actor_create("actor", sg_host_by_name("Tremblay"), &actor, 0, NULL);
// You can use sg_cfg_set_int(key, value) to pass a size that will be parsed. That value will be used for any
// subsequent actors
sg_cfg_set_int("contexts/stack-size", 16384);
sg_actor_create("actor", sg_host_by_name("Tremblay"), &actor, 0, NULL);
sg_actor_create("actor", sg_host_by_name("Tremblay"), &actor, 0, NULL);
sg_cfg_set_int("contexts/stack-size", 32 * 1024);
sg_actor_create("actor", sg_host_by_name("Tremblay"), &actor, 0, NULL);
sg_actor_create("actor", sg_host_by_name("Tremblay"), &actor, 0, NULL);
// Or you can use set_stacksize() before starting the actor to modify only this one
sg_actor_t a = sg_actor_init("actor", sg_host_by_name("Tremblay"));
sg_actor_set_stacksize(a, 64 * 1024);
sg_actor_start(a, actor, 0, NULL);
sg_actor_create("actor", sg_host_by_name("Tremblay"), &actor, 0, NULL);
simgrid_run();
XBT_INFO("Simulation time %g", simgrid_get_clock());
return 0;
}
Inter-Actors Interactions
See also the examples on inter-actors communications and the ones on classical synchronization objects.
Suspending/resuming Actors
Actors can be suspended and resumed during their executions.
See also simgrid::s4u::this_actor::suspend()
,
simgrid::s4u::Actor::suspend()
, simgrid::s4u::Actor::resume()
, and
simgrid::s4u::Actor::is_suspended()
.
View examples/cpp/actor-suspend/s4u-actor-suspend.cpp
Download s4u-actor-suspend.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <simgrid/s4u.hpp>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_actor_suspend, "Messages specific for this s4u example");
/* The Lazy guy only wants to sleep, but can be awaken by the dream_master actor. */
static void lazy_guy()
{
XBT_INFO("Nobody's watching me ? Let's go to sleep.");
sg4::this_actor::suspend(); /* - Start by suspending itself */
XBT_INFO("Uuuh ? Did somebody call me ?");
XBT_INFO("Going to sleep..."); /* - Then repetitively go to sleep, but got awaken */
sg4::this_actor::sleep_for(10);
XBT_INFO("Mmm... waking up.");
XBT_INFO("Going to sleep one more time (for 10 sec)...");
sg4::this_actor::sleep_for(10);
XBT_INFO("Waking up once for all!");
XBT_INFO("Ok, let's do some work, then (for 10 sec on Boivin).");
sg4::this_actor::execute(980.95e6);
XBT_INFO("Mmmh, I'm done now. Goodbye.");
}
/* The Dream master: */
static void dream_master()
{
XBT_INFO("Let's create a lazy guy."); /* - Create a lazy_guy actor */
sg4::ActorPtr lazy = sg4::Actor::create("Lazy", sg4::this_actor::get_host(), lazy_guy);
XBT_INFO("Let's wait a little bit...");
sg4::this_actor::sleep_for(10); /* - Wait for 10 seconds */
XBT_INFO("Let's wake the lazy guy up! >:) BOOOOOUUUHHH!!!!");
if (lazy->is_suspended())
lazy->resume(); /* - Then wake up the lazy_guy */
else
XBT_ERROR("I was thinking that the lazy guy would be suspended now");
sg4::this_actor::sleep_for(5); /* Repeat two times: */
XBT_INFO("Suspend the lazy guy while he's sleeping...");
lazy->suspend(); /* - Suspend the lazy_guy while he's asleep */
XBT_INFO("Let him finish his siesta.");
sg4::this_actor::sleep_for(10); /* - Wait for 10 seconds */
XBT_INFO("Wake up, lazy guy!");
lazy->resume(); /* - Then wake up the lazy_guy again */
sg4::this_actor::sleep_for(5);
XBT_INFO("Suspend again the lazy guy while he's sleeping...");
lazy->suspend();
XBT_INFO("This time, don't let him finish his siesta.");
sg4::this_actor::sleep_for(2);
XBT_INFO("Wake up, lazy guy!");
lazy->resume();
sg4::this_actor::sleep_for(5);
XBT_INFO("Give a 2 seconds break to the lazy guy while he's working...");
lazy->suspend();
sg4::this_actor::sleep_for(2);
XBT_INFO("Back to work, lazy guy!");
lazy->resume();
XBT_INFO("OK, I'm done here.");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s ../platforms/small_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]); /* - Load the platform description */
std::vector<sg4::Host*> list = e.get_all_hosts();
sg4::Actor::create("dream_master", list.front(), dream_master);
e.run(); /* - Run the simulation */
return 0;
}
See also simgrid.this_actor.suspend()
,
simgrid.Actor.suspend()
, simgrid.Actor.resume()
, and
simgrid.Actor.is_suspended()
.
View examples/python/actor-suspend/actor-suspend.py
# Copyright (c) 2017-2024. The SimGrid Team. All rights reserved.
#
# This program is free software you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
Usage: actor-suspend.py platform_file [other parameters]
"""
import sys
from simgrid import Actor, Engine, this_actor
def lazy_guy():
"""The Lazy guy only wants to sleep, but can be awaken by the dream_master actor"""
this_actor.info("Nobody's watching me ? Let's go to sleep.")
this_actor.suspend() # - Start by suspending itself
this_actor.info("Uuuh ? Did somebody call me ?")
# - Then repetitively go to sleep, but get awaken
this_actor.info("Going to sleep...")
this_actor.sleep_for(10)
this_actor.info("Mmm... waking up.")
this_actor.info("Going to sleep one more time (for 10 sec)...")
this_actor.sleep_for(10)
this_actor.info("Waking up once for all!")
this_actor.info("Ok, let's do some work, then (for 10 sec on Boivin).")
this_actor.execute(980.95e6)
this_actor.info("Mmmh, I'm done now. Goodbye.")
def dream_master():
"""The Dream master"""
this_actor.info("Let's create a lazy guy.") # Create a lazy_guy actor
lazy = Actor.create("Lazy", this_actor.get_host(), lazy_guy)
this_actor.info("Let's wait a little bit...")
this_actor.sleep_for(10) # Wait for 10 seconds
this_actor.info("Let's wake the lazy guy up! >:) BOOOOOUUUHHH!!!!")
if lazy.is_suspended():
lazy.resume() # Then wake up the lazy_guy
else:
this_actor.error(
"I was thinking that the lazy guy would be suspended now")
this_actor.sleep_for(5) # Repeat two times:
this_actor.info("Suspend the lazy guy while he's sleeping...")
lazy.suspend() # Suspend the lazy_guy while he's asleep
this_actor.info("Let him finish his siesta.")
this_actor.sleep_for(10) # Wait for 10 seconds
this_actor.info("Wake up, lazy guy!")
lazy.resume() # Then wake up the lazy_guy again
this_actor.sleep_for(5)
this_actor.info("Suspend again the lazy guy while he's sleeping...")
lazy.suspend()
this_actor.info("This time, don't let him finish his siesta.")
this_actor.sleep_for(2)
this_actor.info("Wake up, lazy guy!")
lazy.resume()
this_actor.sleep_for(5)
this_actor.info(
"Give a 2 seconds break to the lazy guy while he's working...")
lazy.suspend()
this_actor.sleep_for(2)
this_actor.info("Back to work, lazy guy!")
lazy.resume()
this_actor.info("OK, I'm done here.")
if __name__ == '__main__':
e = Engine(sys.argv)
if len(sys.argv) < 2:
raise AssertionError(
"Usage: actor-suspend.py platform_file [other parameters]")
e.load_platform(sys.argv[1]) # Load the platform description
hosts = e.all_hosts
Actor.create("dream_master", hosts[0], dream_master)
e.run() # Run the simulation
See also sg_actor_suspend()
, sg_actor_resume()
, and
sg_actor_is_suspended()
.
View examples/c/actor-suspend/actor-suspend.c
/* Copyright (c) 2007-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "xbt/asserts.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(actor_suspend, "Messages specific for this example");
/* The Lazy guy only wants to sleep, but can be awaken by the dream_master actor. */
static void lazy_guy(int argc, char* argv[])
{
XBT_INFO("Nobody's watching me ? Let's go to sleep.");
sg_actor_suspend(sg_actor_self()); /* - Start by suspending itself */
XBT_INFO("Uuuh ? Did somebody call me ?");
XBT_INFO("Going to sleep..."); /* - Then repetitively go to sleep, but got awaken */
sg_actor_sleep_for(10.0);
XBT_INFO("Mmm... waking up.");
XBT_INFO("Going to sleep one more time (for 10 sec)...");
sg_actor_sleep_for(10.0);
XBT_INFO("Waking up once for all!");
XBT_INFO("Ok, let's do some work, then (for 10 sec on Boivin).");
sg_actor_execute(980.95e6);
XBT_INFO("Mmmh, I'm done now. Goodbye.");
}
/* The Dream master: */
static void dream_master(int argc, char* argv[])
{
XBT_INFO("Let's create a lazy guy."); /* - Create a lazy_guy actor */
sg_actor_t lazy = sg_actor_create("Lazy", sg_host_self(), &lazy_guy, 0, NULL);
XBT_INFO("Let's wait a little bit...");
sg_actor_sleep_for(10.0); /* - Wait for 10 seconds */
XBT_INFO("Let's wake the lazy guy up! >:) BOOOOOUUUHHH!!!!");
sg_actor_resume(lazy); /* - Then wake up the lazy_guy */
sg_actor_sleep_for(5.0); /* Repeat two times: */
XBT_INFO("Suspend the lazy guy while he's sleeping...");
sg_actor_suspend(lazy); /* - Suspend the lazy_guy while he's asleep */
XBT_INFO("Let him finish his siesta.");
sg_actor_sleep_for(10.0); /* - Wait for 10 seconds */
XBT_INFO("Wake up, lazy guy!");
sg_actor_resume(lazy); /* - Then wake up the lazy_guy again */
sg_actor_sleep_for(5.0);
XBT_INFO("Suspend again the lazy guy while he's sleeping...");
sg_actor_suspend(lazy);
XBT_INFO("This time, don't let him finish his siesta.");
sg_actor_sleep_for(2.0);
XBT_INFO("Wake up, lazy guy!");
sg_actor_resume(lazy);
sg_actor_sleep_for(5.0);
XBT_INFO("Give a 2 seconds break to the lazy guy while he's working...");
sg_actor_suspend(lazy);
sg_actor_sleep_for(2.0);
XBT_INFO("Back to work, lazy guy!");
sg_actor_resume(lazy);
XBT_INFO("OK, I'm done here.");
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform_file\n\tExample: %s platform.xml\n", argv[0], argv[0]);
simgrid_load_platform(argv[1]);
simgrid_register_function("dream_master", dream_master);
sg_actor_create("dream_master", sg_host_by_name("Boivin"), &dream_master, 0, NULL);
simgrid_run();
return 0;
}
Migrating Actors
Actors can move or be moved from a host to another very easily. It amounts to setting them on a new host.
See also simgrid::s4u::this_actor::set_host()
and simgrid::s4u::Actor::set_host()
.
View examples/cpp/actor-migrate/s4u-actor-migrate.cpp
Download s4u-actor-migrate.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This example demonstrate the actor migrations.
*
* The worker actor first move by itself, and then start an execution.
* During that execution, the monitor migrates the worker, that wakes up on another host.
* The execution was of the right amount of flops to take exactly 5 seconds on the first host
* and 5 other seconds on the second one, so it stops after 10 seconds.
*
* Then another migration is done by the monitor while the worker is suspended.
*
* Note that worker() takes an uncommon set of parameters,
* and that this is perfectly accepted by create().
*/
#include <simgrid/s4u.hpp>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_actor_migration, "Messages specific for this s4u example");
static void worker(sg4::Host* first, const sg4::Host* second)
{
double flopAmount = first->get_speed() * 5 + second->get_speed() * 5;
XBT_INFO("Let's move to %s to execute %.2f Mflops (5sec on %s and 5sec on %s)", first->get_cname(), flopAmount / 1e6,
first->get_cname(), second->get_cname());
sg4::this_actor::set_host(first);
sg4::this_actor::execute(flopAmount);
XBT_INFO("I wake up on %s. Let's suspend a bit", sg4::this_actor::get_host()->get_cname());
sg4::this_actor::suspend();
XBT_INFO("I wake up on %s", sg4::this_actor::get_host()->get_cname());
XBT_INFO("Done");
}
static void monitor()
{
sg4::Host* boivin = sg4::Host::by_name("Boivin");
sg4::Host* jacquelin = sg4::Host::by_name("Jacquelin");
sg4::Host* fafard = sg4::Host::by_name("Fafard");
sg4::ActorPtr actor = sg4::Actor::create("worker", fafard, worker, boivin, jacquelin);
sg4::this_actor::sleep_for(5);
XBT_INFO("After 5 seconds, move the actor to %s", jacquelin->get_cname());
actor->set_host(jacquelin);
sg4::this_actor::sleep_until(15);
XBT_INFO("At t=15, move the actor to %s and resume it.", fafard->get_cname());
actor->set_host(fafard);
actor->resume();
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s ../platforms/small_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
sg4::Actor::create("monitor", e.host_by_name("Boivin"), monitor);
e.run();
return 0;
}
See also simgrid.Actor.host
.
View examples/python/actor-migrate/actor-migrate.py
# Copyright (c) 2017-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
This example demonstrate the actor migrations.
The worker actor first move by itself, and then start an execution.
During that execution, the monitor migrates the worker, that wakes up on another host.
The execution was of the right amount of flops to take exactly 5 seconds on the first host
and 5 other seconds on the second one, so it stops after 10 seconds.
Then another migration is done by the monitor while the worker is suspended.
Note that worker() takes an uncommon set of parameters,
and that this is perfectly accepted by create().
"""
import sys
from simgrid import Actor, Engine, Host, this_actor
def worker(first_host, second_host):
flop_amount = first_host.speed * 5 + second_host.speed * 5
this_actor.info("Let's move to {:s} to execute {:.2f} Mflops (5sec on {:s} and 5sec on {:s})".format(
first_host.name, flop_amount / 1e6, first_host.name, second_host.name))
this_actor.set_host(first_host)
this_actor.execute(flop_amount)
this_actor.info("I wake up on {:s}. Let's suspend a bit".format(
this_actor.get_host().name))
this_actor.suspend()
this_actor.info("I wake up on {:s}".format(this_actor.get_host().name))
this_actor.info("Done")
def monitor():
boivin = Host.by_name("Boivin")
jacquelin = Host.by_name("Jacquelin")
fafard = Host.by_name("Fafard")
actor = Actor.create("worker", fafard, worker, boivin, jacquelin)
this_actor.sleep_for(5)
this_actor.info(
"After 5 seconds, move the actor to {:s}".format(jacquelin.name))
actor.host = jacquelin
this_actor.sleep_until(15)
this_actor.info(
"At t=15, move the actor to {:s} and resume it.".format(fafard.name))
actor.host = fafard
actor.resume()
if __name__ == '__main__':
e = Engine(sys.argv)
if len(sys.argv) < 2:
raise AssertionError(
"Usage: actor-migration.py platform_file [other parameters]")
e.load_platform(sys.argv[1])
Actor.create("monitor", Host.by_name("Boivin"), monitor)
e.run()
See also sg_actor_set_host()
.
View examples/c/actor-migrate/actor-migrate.c
/* Copyright (c) 2009-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/barrier.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "xbt/asserts.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(actor_migrate, "Messages specific for this example");
static void worker(int argc, char* argv[])
{
xbt_assert(argc > 2);
sg_host_t first = sg_host_by_name(argv[1]);
const_sg_host_t second = sg_host_by_name(argv[2]);
double flopAmount = sg_host_get_speed(first) * 5 + sg_host_get_speed(second) * 5;
XBT_INFO("Let's move to %s to execute %.2f Mflops (5sec on %s and 5sec on %s)", argv[1], flopAmount / 1e6, argv[1],
argv[2]);
sg_actor_set_host(sg_actor_self(), first);
sg_actor_execute(flopAmount);
XBT_INFO("I wake up on %s. Let's suspend a bit", sg_host_get_name(sg_host_self()));
sg_actor_suspend(sg_actor_self());
XBT_INFO("I wake up on %s", sg_host_get_name(sg_host_self()));
XBT_INFO("Done");
}
static void monitor(int argc, char* argv[])
{
sg_host_t jacquelin = sg_host_by_name("Jacquelin");
sg_host_t fafard = sg_host_by_name("Fafard");
int actor_argc = 3;
const char* actor_argv[] = {"worker", "Boivin", "Jacquelin", NULL};
sg_actor_t actor = sg_actor_create_("worker", sg_host_by_name("Fafard"), &worker, actor_argc, actor_argv);
sg_actor_sleep_for(5);
XBT_INFO("After 5 seconds, move the actor to %s", sg_host_get_name(jacquelin));
sg_actor_set_host(actor, jacquelin);
sg_actor_sleep_until(15);
XBT_INFO("At t=15, move the actor to %s and resume it.", sg_host_get_name(fafard));
sg_actor_set_host(actor, fafard);
sg_actor_resume(actor);
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s platform.xml\n", argv[0], argv[0]);
simgrid_load_platform(argv[1]); /* - Load the platform description */
sg_actor_create("monitor", sg_host_by_name("Boivin"), &monitor, 0, NULL);
simgrid_run();
return 0;
}
Waiting for the termination of an actor (joining on it)
You can block the current actor until the end of another actor.
See also simgrid::s4u::Actor::join()
.
View examples/cpp/actor-join/s4u-actor-join.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
static void sleeper()
{
XBT_INFO("Sleeper started");
sg4::this_actor::sleep_for(3);
XBT_INFO("I'm done. See you!");
}
static void master()
{
sg4::ActorPtr actor;
XBT_INFO("Start sleeper");
actor = sg4::Actor::create("sleeper from master", sg4::Host::current(), sleeper);
XBT_INFO("Join the sleeper (timeout 2)");
actor->join(2);
XBT_INFO("Start sleeper");
actor = sg4::Actor::create("sleeper from master", sg4::Host::current(), sleeper);
XBT_INFO("Join the sleeper (timeout 4)");
actor->join(4);
XBT_INFO("Start sleeper");
actor = sg4::Actor::create("sleeper from master", sg4::Host::current(), sleeper);
XBT_INFO("Join the sleeper (timeout 2)");
actor->join(2);
XBT_INFO("Start sleeper");
actor = sg4::Actor::create("sleeper from master", sg4::Host::current(), sleeper);
XBT_INFO("Waiting 4");
sg4::this_actor::sleep_for(4);
XBT_INFO("Join the sleeper after its end (timeout 1)");
actor->join(1);
XBT_INFO("Goodbye now!");
sg4::this_actor::sleep_for(1);
XBT_INFO("Goodbye now!");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s ../platforms/small_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
sg4::Actor::create("master", e.host_by_name("Tremblay"), master);
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
See also simgrid.Actor.join()
.
View examples/python/actor-join/actor-join.py
# Copyright (c) 2017-2024. The SimGrid Team. All rights reserved.
#
# This program is free software you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
Usage: actor-join.py platform_file [other parameters]
"""
import sys
from simgrid import Actor, Engine, Host, this_actor
def sleeper():
this_actor.info("Sleeper started")
this_actor.sleep_for(3)
this_actor.info("I'm done. See you!")
def master():
this_actor.info("Start 1st sleeper")
actor = Actor.create("1st sleeper from master", Host.current(), sleeper)
this_actor.info("Join the 1st sleeper (timeout 2)")
actor.join(2)
this_actor.info("Start 2nd sleeper")
actor = Actor.create("2nd sleeper from master", Host.current(), sleeper)
this_actor.info("Join the 2nd sleeper (timeout 4)")
actor.join(4)
this_actor.info("Start 3rd sleeper")
actor = Actor.create("3rd sleeper from master", Host.current(), sleeper)
this_actor.info("Join the 3rd sleeper (timeout 2)")
actor.join(2)
this_actor.info("Start 4th sleeper")
actor = Actor.create("4th sleeper from master", Host.current(), sleeper)
this_actor.info("Waiting 4")
this_actor.sleep_for(4)
this_actor.info("Join the 4th sleeper after its end (timeout 1)")
actor.join(1)
this_actor.info("Goodbye now!")
this_actor.sleep_for(1)
this_actor.info("Goodbye now!")
if __name__ == '__main__':
e = Engine(sys.argv)
if len(sys.argv) < 2:
raise AssertionError(
"Usage: actor-join.py platform_file [other parameters]")
e.load_platform(sys.argv[1])
Actor.create("master", Host.by_name("Tremblay"), master)
e.run()
this_actor.info("Simulation time {}".format(e.clock))
See also sg_actor_join()
.
View examples/c/actor-join/actor-join.c
/* Copyright (c) 2010-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "xbt/log.h"
#include "xbt/sysdep.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(actor_join, "Messages specific for this example");
static void sleeper(int argc, char* argv[])
{
XBT_INFO("Sleeper started");
sg_actor_sleep_for(3);
XBT_INFO("I'm done. See you!");
}
static void master(int argc, char* argv[])
{
const_sg_actor_t actor;
XBT_INFO("Start sleeper");
actor = sg_actor_create("sleeper from master", sg_host_self(), &sleeper, 0, NULL);
XBT_INFO("Join the sleeper (timeout 2)");
sg_actor_join(actor, 2);
XBT_INFO("Start sleeper");
actor = sg_actor_create("sleeper from master", sg_host_self(), &sleeper, 0, NULL);
XBT_INFO("Join the sleeper (timeout 4)");
sg_actor_join(actor, 4);
XBT_INFO("Start sleeper");
actor = sg_actor_create("sleeper from master", sg_host_self(), &sleeper, 0, NULL);
XBT_INFO("Join the sleeper (timeout 2)");
sg_actor_join(actor, 2);
XBT_INFO("Start sleeper");
actor = sg_actor_create("sleeper from master", sg_host_self(), &sleeper, 0, NULL);
sg_actor_ref(actor); // We have to take that ref because the actor will stop before we join it
XBT_INFO("Waiting 4");
sg_actor_sleep_for(4);
XBT_INFO("Join the sleeper after its end (timeout 1)");
sg_actor_join(actor, 1);
sg_actor_unref(actor); // Avoid to leak memory
XBT_INFO("Goodbye now!");
sg_actor_sleep_for(1);
XBT_INFO("Goodbye now!");
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s platform.xml\n", argv[0], argv[0]);
simgrid_load_platform(argv[1]);
sg_actor_create("master", sg_host_by_name("Tremblay"), &master, 0, NULL);
simgrid_run();
XBT_INFO("Simulation time %g", simgrid_get_clock());
return 0;
}
Yielding to other actors
The `yield()`
function interrupts the execution of the current
actor, leaving a chance to the other actors that are ready to run
at this timestamp.
See also simgrid::s4u::this_actor::yield()
.
View examples/cpp/actor-yield/s4u-actor-yield.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <simgrid/s4u.hpp>
namespace sg4 = simgrid::s4u;
/* This example does not much: It just spans over-polite actor that yield a large amount
* of time before ending.
*
* This serves as an example for the sg4::this_actor::yield() function, with which an actor can request
* to be rescheduled after the other actor that are ready at the current timestamp.
*
* It can also be used to benchmark our context-switching mechanism.
*/
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_actor_yield, "Messages specific for this s4u example");
static void yielder(long number_of_yields)
{
for (int i = 0; i < number_of_yields; i++)
sg4::this_actor::yield();
XBT_INFO("I yielded %ld times. Goodbye now!", number_of_yields);
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]); /* Load the platform description */
sg4::Actor::create("yielder", e.host_by_name("Tremblay"), yielder, 10);
sg4::Actor::create("yielder", e.host_by_name("Ruby"), yielder, 15);
e.run(); /* - Run the simulation */
return 0;
}
See also simgrid.this_actor.yield_()
.
View examples/python/actor-yield/actor-yield.py
# Copyright (c) 2017-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
This example does not much: It just spans over-polite actor that yield a large amount
of time before ending.
This serves as an example for the simgrid.yield() function, with which an actor can request
to be rescheduled after the other actor that are ready at the current timestamp.
It can also be used to benchmark our context-switching mechanism.
"""
import sys
from simgrid import Actor, Engine, Host, this_actor
def yielder(number_of_yields):
for _ in range(number_of_yields):
this_actor.yield_()
this_actor.info("I yielded {:d} times. Goodbye now!".format(number_of_yields))
if __name__ == '__main__':
e = Engine(sys.argv)
e.load_platform(sys.argv[1]) # Load the platform description
Actor.create("yielder", Host.by_name("Tremblay"), yielder, 10)
Actor.create("yielder", Host.by_name("Ruby"), yielder, 15)
e.run() # - Run the simulation
See also sg_actor_yield()
.
View examples/c/actor-yield/actor-yield.c
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "xbt/asserts.h"
#include "xbt/log.h"
#include "xbt/str.h"
/* This example does not much: It just spans over-polite actors that yield a large amount
* of time before ending.
*
* This serves as an example for the sg_actor_yield() function, with which an actor can request
* to be rescheduled after the other actors that are ready at the current timestamp.
*
* It can also be used to benchmark our context-switching mechanism.
*/
XBT_LOG_NEW_DEFAULT_CATEGORY(actor_yield, "Messages specific for this example");
/* Main function of the Yielder actor */
static void yielder(int argc, char* argv[])
{
xbt_assert(argc == 2, "The sender function expects 1 arguments from the XML deployment file");
long number_of_yields = xbt_str_parse_int(argv[1], "Invalid amount of yields");
for (int i = 0; i < number_of_yields; i++)
sg_actor_yield();
XBT_INFO("I yielded %ld times. Goodbye now!", number_of_yields);
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 2,
"Usage: %s platform_file deployment_file\n"
"\tExample: %s platform.xml deployment.xml\n",
argv[0], argv[0]);
simgrid_load_platform(argv[1]); /* - Load the platform description */
simgrid_register_function("yielder", yielder);
simgrid_load_deployment(argv[2]); /* - Deploy the sender and receiver actors */
simgrid_run();
return 0;
}
Traces Replay as a Workload
This section details how to run trace-driven simulations. It is very handy when you want to test an algorithm or protocol that only reacts to external events. For example, many P2P protocols react to user requests, but do nothing if there is no such event.
In such situations, you should write your protocol in C++, and separate
the workload that you want to play onto your protocol in a separate
text file. Declare a function handling each type of the events in your
trace, register them using xbt_replay_action_register()
in
your main, and then run the simulation.
Then, you can either have one trace file containing all your events, or a file per simulated process: the former may be easier to work with, but the second is more efficient on very large traces. Check also the tesh files in the example directories for details.
Communication replay
Presents a set of event handlers reproducing classical communication primitives (asynchronous send/receive at the moment).
View examples/cpp/replay-comm/s4u-replay-comm.cpp
/* Copyright (c) 2009-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include "xbt/replay.hpp"
#include "xbt/str.h"
#include <boost/algorithm/string/join.hpp>
#include <cinttypes>
#include <string>
XBT_LOG_NEW_DEFAULT_CATEGORY(replay_comm, "Messages specific for this example");
namespace sg4 = simgrid::s4u;
#define ACT_DEBUG(...) \
if (XBT_LOG_ISENABLED(replay_comm, xbt_log_priority_verbose)) { \
std::string NAME = boost::algorithm::join(action, " "); \
XBT_DEBUG(__VA_ARGS__); \
} else \
((void)0)
static void log_action(const simgrid::xbt::ReplayAction& action, double date)
{
if (XBT_LOG_ISENABLED(replay_comm, xbt_log_priority_verbose)) {
std::string s = boost::algorithm::join(action, " ");
XBT_VERB("%s %f", s.c_str(), date);
}
}
class Replayer {
public:
explicit Replayer(std::vector<std::string> args)
{
const char* actor_name = args.at(0).c_str();
if (args.size() > 1) { // split mode, the trace file was provided in the deployment file
const char* trace_filename = args[1].c_str();
simgrid::xbt::replay_runner(actor_name, trace_filename);
} else { // Merged mode
simgrid::xbt::replay_runner(actor_name);
}
}
void operator()() const
{
// Nothing to do here
}
/* My actions */
static void compute(simgrid::xbt::ReplayAction& action)
{
double amount = std::stod(action[2]);
double clock = sg4::Engine::get_clock();
ACT_DEBUG("Entering %s", NAME.c_str());
sg4::this_actor::execute(amount);
log_action(action, sg4::Engine::get_clock() - clock);
}
static void send(simgrid::xbt::ReplayAction& action)
{
auto size = static_cast<uint64_t>(std::stod(action[3]));
auto* payload = new std::string(action[3]);
double clock = sg4::Engine::get_clock();
sg4::Mailbox* to = sg4::Mailbox::by_name(sg4::this_actor::get_name() + "_" + action[2]);
ACT_DEBUG("Entering Send: %s (size: %" PRIu64 ") -- Actor %s on mailbox %s", NAME.c_str(), size,
sg4::this_actor::get_cname(), to->get_cname());
to->put(payload, size);
log_action(action, sg4::Engine::get_clock() - clock);
}
static void recv(simgrid::xbt::ReplayAction& action)
{
double clock = sg4::Engine::get_clock();
sg4::Mailbox* from = sg4::Mailbox::by_name(action[2] + "_" + sg4::this_actor::get_name());
ACT_DEBUG("Receiving: %s -- Actor %s on mailbox %s", NAME.c_str(), sg4::this_actor::get_cname(), from->get_cname());
from->get_unique<std::string>();
log_action(action, sg4::Engine::get_clock() - clock);
}
};
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 2,
"Usage: %s platform_file deployment_file [action_files]\n"
"\t# if all actions are in the same file\n"
"\tExample: %s platform.xml deployment.xml actions\n"
"\t# if actions are in separate files, specified in deployment\n"
"\tExample: %s platform.xml deployment.xml ",
argv[0], argv[0], argv[0]);
e.load_platform(argv[1]);
e.register_actor<Replayer>("p0");
e.register_actor<Replayer>("p1");
e.load_deployment(argv[2]);
if (argv[3] != nullptr)
xbt_replay_set_tracefile(argv[3]);
/* Action registration */
xbt_replay_action_register("compute", Replayer::compute);
xbt_replay_action_register("send", Replayer::send);
xbt_replay_action_register("recv", Replayer::recv);
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
I/O replay
Presents a set of event handlers reproducing classical I/O primitives (open, read, close).
View examples/cpp/replay-io/s4u-replay-io.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <simgrid/plugins/file_system.h>
#include <simgrid/s4u.hpp>
#include <xbt/replay.hpp>
#include <xbt/str.h>
#include <boost/algorithm/string/join.hpp>
XBT_LOG_NEW_DEFAULT_CATEGORY(replay_io, "Messages specific for this example");
namespace sg4 = simgrid::s4u;
#define ACT_DEBUG(...) \
if (XBT_LOG_ISENABLED(replay_io, xbt_log_priority_verbose)) { \
std::string NAME = boost::algorithm::join(action, " "); \
XBT_DEBUG(__VA_ARGS__); \
} else \
((void)0)
class Replayer {
static std::unordered_map<std::string, sg4::File*> opened_files;
static void log_action(const simgrid::xbt::ReplayAction& action, double date)
{
if (XBT_LOG_ISENABLED(replay_io, xbt_log_priority_verbose)) {
std::string s = boost::algorithm::join(action, " ");
XBT_VERB("%s %f", s.c_str(), date);
}
}
static sg4::File* get_file_descriptor(const std::string& file_name)
{
std::string full_name = sg4::this_actor::get_name() + ":" + file_name;
return opened_files.at(full_name);
}
public:
explicit Replayer(std::vector<std::string> args)
{
const char* actor_name = args[0].c_str();
if (args.size() > 1) { // split mode, the trace file was provided in the deployment file
const char* trace_filename = args[1].c_str();
simgrid::xbt::replay_runner(actor_name, trace_filename);
} else { // Merged mode
simgrid::xbt::replay_runner(actor_name);
}
}
void operator()() const
{
// Nothing to do here
}
/* My actions */
static void open(simgrid::xbt::ReplayAction& action)
{
std::string file_name = action[2];
double clock = sg4::Engine::get_clock();
std::string full_name = sg4::this_actor::get_name() + ":" + file_name;
ACT_DEBUG("Entering Open: %s (filename: %s)", NAME.c_str(), file_name.c_str());
auto* file = sg4::File::open(file_name, nullptr);
opened_files.try_emplace(full_name, file);
log_action(action, sg4::Engine::get_clock() - clock);
}
static void read(simgrid::xbt::ReplayAction& action)
{
std::string file_name = action[2];
sg_size_t size = std::stoul(action[3]);
double clock = sg4::Engine::get_clock();
sg4::File* file = get_file_descriptor(file_name);
ACT_DEBUG("Entering Read: %s (size: %llu)", NAME.c_str(), size);
file->read(size);
log_action(action, sg4::Engine::get_clock() - clock);
}
static void close(simgrid::xbt::ReplayAction& action)
{
std::string file_name = action[2];
std::string full_name = sg4::this_actor::get_name() + ":" + file_name;
double clock = sg4::Engine::get_clock();
ACT_DEBUG("Entering Close: %s (filename: %s)", NAME.c_str(), file_name.c_str());
auto entry = opened_files.find(full_name);
xbt_assert(entry != opened_files.end(), "File not found in opened files: %s", full_name.c_str());
entry->second->close();
opened_files.erase(entry);
log_action(action, sg4::Engine::get_clock() - clock);
}
};
std::unordered_map<std::string, sg4::File*> Replayer::opened_files;
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
sg_storage_file_system_init();
xbt_assert(argc > 3,
"Usage: %s platform_file deployment_file [action_files]\n"
"\texample: %s platform.xml deployment.xml actions # if all actions are in the same file\n"
"\t# if actions are in separate files, specified in deployment\n"
"\texample: %s platform.xml deployment.xml",
argv[0], argv[0], argv[0]);
e.load_platform(argv[1]);
e.register_actor<Replayer>("p0");
e.load_deployment(argv[2]);
if (argv[3] != nullptr)
xbt_replay_set_tracefile(argv[3]);
/* Action registration */
xbt_replay_action_register("open", Replayer::open);
xbt_replay_action_register("read", Replayer::read);
xbt_replay_action_register("close", Replayer::close);
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
Activities: what Actors do
Communications on the Network
Basic communications
This simple example just sends one message back and forth. The tesh file laying in the directory shows how to start the simulator binary, highlighting how to pass options to the simulators (as detailed in Section Configuring SimGrid).
View examples/cpp/comm-pingpong/s4u-comm-pingpong.cpp
Download s4u-comm-pingpong.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <simgrid/s4u.hpp>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_app_pingpong, "Messages specific for this s4u example");
static void pinger(sg4::Mailbox* mailbox_in, sg4::Mailbox* mailbox_out)
{
XBT_INFO("Ping from mailbox %s to mailbox %s", mailbox_in->get_name().c_str(), mailbox_out->get_name().c_str());
/* - Do the ping with a 1-Byte payload (latency bound) ... */
auto* payload = new double(sg4::Engine::get_clock());
mailbox_out->put(payload, 1);
/* - ... then wait for the (large) pong */
auto sender_time = mailbox_in->get_unique<double>();
double communication_time = sg4::Engine::get_clock() - *sender_time;
XBT_INFO("Payload received : large communication (bandwidth bound)");
XBT_INFO("Pong time (bandwidth bound): %.3f", communication_time);
}
static void ponger(sg4::Mailbox* mailbox_in, sg4::Mailbox* mailbox_out)
{
XBT_INFO("Pong from mailbox %s to mailbox %s", mailbox_in->get_name().c_str(), mailbox_out->get_name().c_str());
/* - Receive the (small) ping first ....*/
auto sender_time = mailbox_in->get_unique<double>();
double communication_time = sg4::Engine::get_clock() - *sender_time;
XBT_INFO("Payload received : small communication (latency bound)");
XBT_INFO("Ping time (latency bound) %f", communication_time);
/* - ... Then send a 1GB pong back (bandwidth bound) */
auto* payload = new double(sg4::Engine::get_clock());
XBT_INFO("payload = %.3f", *payload);
mailbox_out->put(payload, 1e9);
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Mailbox* mb1 = e.mailbox_by_name_or_create("Mailbox 1");
sg4::Mailbox* mb2 = e.mailbox_by_name_or_create("Mailbox 2");
sg4::Actor::create("pinger", e.host_by_name("Tremblay"), pinger, mb1, mb2);
sg4::Actor::create("ponger", e.host_by_name("Jupiter"), ponger, mb2, mb1);
e.run();
XBT_INFO("Total simulation time: %.3f", sg4::Engine::get_clock());
return 0;
}
View examples/python/comm-pingpong/comm-pingpong.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
from argparse import ArgumentParser
import sys
from simgrid import Engine, Actor, Mailbox, this_actor
def create_parser() -> ArgumentParser:
parser = ArgumentParser()
parser.add_argument(
'--platform',
type=str,
required=True,
help='path to the platform description'
)
return parser
def pinger(mailbox_in: Mailbox, mailbox_out: Mailbox):
this_actor.info(f"Ping from mailbox {mailbox_in.name} to mailbox {mailbox_out.name}")
# Do the ping with a 1-Byte payload (latency bound) ...
payload = Engine.clock
mailbox_out.put(payload, 1)
# ... then wait for the (large) pong
sender_time: float = mailbox_in.get()
communication_time = Engine.clock - sender_time
this_actor.info("Payload received : large communication (bandwidth bound)")
this_actor.info(f"Pong time (bandwidth bound): {communication_time:.3f}")
def ponger(mailbox_in: Mailbox, mailbox_out: Mailbox):
this_actor.info(f"Pong from mailbox {mailbox_in.name} to mailbox {mailbox_out.name}")
# Receive the (small) ping first ...
sender_time: float = mailbox_in.get()
communication_time = Engine.clock - sender_time
this_actor.info("Payload received : small communication (latency bound)")
this_actor.info(f"Ping time (latency bound) {communication_time:.3f}")
# ... Then send a 1GB pong back (bandwidth bound)
payload = Engine.clock
this_actor.info(f"payload = {payload:.3f}")
mailbox_out.put(payload, int(1e9))
def main():
settings = create_parser().parse_known_args()[0]
e = Engine(sys.argv)
e.load_platform(settings.platform)
mb1: Mailbox = e.mailbox_by_name_or_create("Mailbox 1")
mb2: Mailbox = e.mailbox_by_name_or_create("Mailbox 2")
Actor.create("pinger", e.host_by_name("Tremblay"), pinger, mb1, mb2)
Actor.create("ponger", e.host_by_name("Jupiter"), ponger, mb2, mb1)
e.run()
this_actor.info(f"Total simulation time: {e.clock:.3f}")
if __name__ == "__main__":
main()
View examples/c/comm-pingpong/comm-pingpong.c
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/comm.h"
#include "simgrid/engine.h"
#include "simgrid/forward.h"
#include "simgrid/mailbox.h"
#include "xbt/log.h"
#include "xbt/str.h"
#include "xbt/sysdep.h"
#include <stdio.h> /* snprintf */
XBT_LOG_NEW_DEFAULT_CATEGORY(app_pingpong, "Messages specific for this example");
static void pinger(int argc, char* argv[])
{
sg_mailbox_t mailbox_in = sg_mailbox_by_name("Mailbox 1");
sg_mailbox_t mailbox_out = sg_mailbox_by_name("Mailbox 2");
XBT_INFO("Ping from mailbox %s to mailbox %s", sg_mailbox_get_name(mailbox_in), sg_mailbox_get_name(mailbox_out));
/* - Do the ping with a 1-Byte task (latency bound) ... */
double* now = (double*)xbt_malloc(sizeof(double));
*now = simgrid_get_clock();
sg_mailbox_put(mailbox_out, now, 1);
/* - ... then wait for the (large) pong */
double* sender_time = (double*)sg_mailbox_get(mailbox_in);
double communication_time = simgrid_get_clock() - *sender_time;
XBT_INFO("Task received : large communication (bandwidth bound)");
XBT_INFO("Pong time (bandwidth bound): %.3f", communication_time);
xbt_free(sender_time);
}
static void ponger(int argc, char* argv[])
{
sg_mailbox_t mailbox_in = sg_mailbox_by_name("Mailbox 2");
sg_mailbox_t mailbox_out = sg_mailbox_by_name("Mailbox 1");
XBT_INFO("Pong from mailbox %s to mailbox %s", sg_mailbox_get_name(mailbox_in), sg_mailbox_get_name(mailbox_out));
/* - Receive the (small) ping first ....*/
double* sender_time = (double*)sg_mailbox_get(mailbox_in);
double communication_time = simgrid_get_clock() - *sender_time;
XBT_INFO("Task received : small communication (latency bound)");
XBT_INFO(" Ping time (latency bound) %f", communication_time);
xbt_free(sender_time);
/* - ... Then send a 1GB pong back (bandwidth bound) */
double* payload = (double*)xbt_malloc(sizeof(double));
*payload = simgrid_get_clock();
XBT_INFO("task_bw->data = %.3f", *payload);
sg_mailbox_put(mailbox_out, payload, 1e9);
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 2,
"Usage: %s platform_file deployment_file\n"
"\tExample: %s ../../platforms/small_platform.xml app-pingpong_d.xml\n",
argv[0], argv[0]);
simgrid_load_platform(argv[1]);
simgrid_register_function("pinger", pinger);
simgrid_register_function("ponger", ponger);
simgrid_load_deployment(argv[2]);
simgrid_run();
XBT_INFO("Total simulation time: %.3f", simgrid_get_clock());
return 0;
}
Basic asynchronous communications
Illustrates how to have non-blocking communications, that are communications running in the background leaving the process free to do something else during their completion.
See also simgrid::s4u::Mailbox::put_async()
and simgrid::s4u::Comm::wait()
.
View examples/cpp/comm-wait/s4u-comm-wait.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This example shows how to use simgrid::s4u::this_actor::wait() to wait for a given communication.
*
* As for the other asynchronous examples, the sender initiate all the messages it wants to send and
* pack the resulting simgrid::s4u::CommPtr objects in a vector. All messages thus occurs concurrently.
*
* The sender then loops until there is no ongoing communication.
*/
#include "simgrid/s4u.hpp"
#include <cstdlib>
#include <iostream>
#include <string>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_comm_wait, "Messages specific for this s4u example");
static void sender(int messages_count, size_t payload_size)
{
double sleep_start_time = 5.0;
double sleep_test_time = 0;
sg4::Mailbox* mbox = sg4::Mailbox::by_name("receiver");
XBT_INFO("sleep_start_time : %f , sleep_test_time : %f", sleep_start_time, sleep_test_time);
sg4::this_actor::sleep_for(sleep_start_time);
for (int i = 0; i < messages_count; i++) {
std::string msg_content = "Message " + std::to_string(i);
// Copy the data we send: the 'msg_content' variable is not a stable storage location.
// It will be destroyed when this actor leaves the loop, ie before the receiver gets the data
auto* payload = new std::string(msg_content);
/* Create a communication representing the ongoing communication and then */
sg4::CommPtr comm = mbox->put_async(payload, payload_size);
XBT_INFO("Send '%s' to '%s'", msg_content.c_str(), mbox->get_cname());
if (sleep_test_time > 0) { /* - "test_time" is set to 0, wait */
while (not comm->test()) { /* - Call test() every "sleep_test_time" otherwise */
sg4::this_actor::sleep_for(sleep_test_time);
}
} else {
comm->wait();
}
}
/* Send message to let the receiver know that it should stop */
XBT_INFO("Send 'finalize' to 'receiver'");
mbox->put(new std::string("finalize"), 0);
}
/* Receiver actor expects 1 argument: its ID */
static void receiver()
{
double sleep_start_time = 1.0;
double sleep_test_time = 0.1;
sg4::Mailbox* mbox = sg4::Mailbox::by_name("receiver");
XBT_INFO("sleep_start_time : %f , sleep_test_time : %f", sleep_start_time, sleep_test_time);
sg4::this_actor::sleep_for(sleep_start_time);
XBT_INFO("Wait for my first message");
for (bool cont = true; cont;) {
std::string* received;
sg4::CommPtr comm = mbox->get_async<std::string>(&received);
if (sleep_test_time > 0) { /* - "test_time" is set to 0, wait */
while (not comm->test()) { /* - Call test() every "sleep_test_time" otherwise */
sg4::this_actor::sleep_for(sleep_test_time);
}
} else {
comm->wait();
}
XBT_INFO("I got a '%s'.", received->c_str());
if (*received == "finalize")
cont = false; // If it's a finalize message, we're done.
delete received;
}
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("sender", e.host_by_name("Tremblay"), sender, 3, 482117300);
sg4::Actor::create("receiver", e.host_by_name("Ruby"), receiver);
e.run();
return 0;
}
See also simgrid.Mailbox.put_async()
and simgrid.Comm.wait()
.
View examples/python/comm-wait/comm-wait.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
This example shows how to use simgrid::s4u::this_actor::wait() to wait for a given communication.
As for the other asynchronous examples, the sender initiate all the messages it wants to send and
pack the resulting simgrid::s4u::CommPtr objects in a vector. All messages thus occurs concurrently.
The sender then loops until there is no ongoing communication.
"""
import sys
from simgrid import Actor, Engine, Host, Mailbox, this_actor
def sender(messages_count, msg_size, receivers_count):
# List in which we store all ongoing communications
pending_comms = []
# Vector of the used mailboxes
mboxes = [Mailbox.by_name("receiver-{:d}".format(i)) for i in range(0, receivers_count)]
# Start dispatching all messages to receivers, in a round robin fashion
for i in range(0, messages_count):
content = "Message {:d}".format(i)
mbox = mboxes[i % receivers_count]
this_actor.info("Send '{:s}' to '{:s}'".format(content, str(mbox)))
# Create a communication representing the ongoing communication, and store it in pending_comms
comm = mbox.put_async(content, msg_size)
pending_comms.append(comm)
# Start sending messages to let the workers know that they should stop
for i in range(0, receivers_count):
mbox = mboxes[i]
this_actor.info("Send 'finalize' to '{:s}'".format(str(mbox)))
comm = mbox.put_async("finalize", 0)
pending_comms.append(comm)
this_actor.info("Done dispatching all messages")
# Now that all message exchanges were initiated, wait for their completion, in order of creation.
for comm in pending_comms:
comm.wait()
this_actor.info("Goodbye now!")
def receiver(my_id):
mbox = Mailbox.by_name("receiver-{:d}".format(my_id))
this_actor.info("Wait for my first message")
while True:
received = mbox.get()
this_actor.info("I got a '{:s}'.".format(received))
if received == "finalize":
break # If it's a finalize message, we're done.
if __name__ == '__main__':
e = Engine(sys.argv)
e.load_platform(sys.argv[1]) # Load the platform description
Actor.create("sender", Host.by_name("Tremblay"), sender, 3, 50000000, 1)
Actor.create("receiver", Host.by_name("Ruby"), receiver, 0)
e.run()
See also sg_mailbox_put_async()
and sg_comm_wait()
.
View examples/c/comm-wait/comm-wait.c
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/comm.h"
#include "simgrid/engine.h"
#include "simgrid/mailbox.h"
#include "xbt/log.h"
#include "xbt/str.h"
#include "xbt/sysdep.h"
#include <stdio.h>
XBT_LOG_NEW_DEFAULT_CATEGORY(comm_wait, "Messages specific for this example");
/* Main function of the Sender actor */
static void sender(int argc, char* argv[])
{
xbt_assert(argc == 5, "The sender function expects 4 arguments from the XML deployment file");
long messages_count = xbt_str_parse_int(argv[1], "Invalid amount of messages"); /* - number of messages */
long message_size = xbt_str_parse_int(argv[2], "Invalid message size"); /* - communication cost */
double sleep_start_time = xbt_str_parse_double(argv[3], "Invalid sleep start time"); /* - start time */
double sleep_test_time = xbt_str_parse_double(argv[4], "Invalid test time"); /* - test time */
XBT_INFO("sleep_start_time : %f , sleep_test_time : %f", sleep_start_time, sleep_test_time);
sg_mailbox_t mailbox = sg_mailbox_by_name("receiver");
sg_actor_sleep_for(sleep_start_time);
for (int i = 0; i < messages_count; i++) {
char* payload = bprintf("Message %d", i);
/* This actor first sends a message asynchronously with @ref sg_mailbox_put_async. Then, if: */
sg_comm_t comm = sg_mailbox_put_async(mailbox, payload, message_size);
XBT_INFO("Send '%s' to 'receiver'", payload);
if (sleep_test_time > 0) { /* - "test_time" is set to 0, wait on @ref sg_comm_wait */
while (sg_comm_test(comm) == 0) { /* - Call @ref sg_comm_test every "sleep_test_time" otherwise */
sg_actor_sleep_for(sleep_test_time);
}
} else {
sg_comm_wait(comm);
}
}
XBT_INFO("Send 'finalize' to 'receiver'");
sg_mailbox_put(mailbox, xbt_strdup("finalize"), 0);
}
/* Receiver actor expects 3 arguments: */
static void receiver(int argc, char* argv[])
{
xbt_assert(argc == 3, "The relay_runner function does not accept any parameter from the XML deployment file");
double sleep_start_time = xbt_str_parse_double(argv[1], "Invalid sleep start parameter"); /* - start time */
double sleep_test_time = xbt_str_parse_double(argv[2], "Invalid sleep test parameter"); /* - test time */
XBT_INFO("sleep_start_time : %f , sleep_test_time : %f", sleep_start_time, sleep_test_time);
sg_actor_sleep_for(sleep_start_time); /* This actor first sleeps for "start time" seconds. */
sg_mailbox_t mailbox = sg_mailbox_by_name("receiver");
void* received = NULL;
XBT_INFO("Wait for my first message");
while (1) {
/* Then it posts asynchronous receives (@ref sg_mailbox_get_async) and*/
sg_comm_t comm = sg_mailbox_get_async(mailbox, &received);
if (sleep_test_time > 0) { /* - if "test_time" is set to 0, wait on @ref sg_comm_wait */
while (sg_comm_test(comm) == 0) { /* - Call @ref sg_comm_test every "sleep_test_time" otherwise */
sg_actor_sleep_for(sleep_test_time);
}
} else {
sg_comm_wait(comm);
}
XBT_INFO("I got a '%s'.", (char*)received);
if (strcmp((char*)received, "finalize") == 0) { /* If the received task is "finalize", the actor ends */
free(received);
break;
}
free(received);
}
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 2,
"Usage: %s platform_file deployment_file\n"
"\tExample: %s platform.xml deployment.xml\n",
argv[0], argv[0]);
simgrid_load_platform(argv[1]); /* - Load the platform description */
simgrid_register_function("sender", sender);
simgrid_register_function("receiver", receiver);
simgrid_load_deployment(argv[2]); /* - Deploy the sender and receiver actors */
simgrid_run(); /* - Run the simulation */
return 0;
}
Waiting for communications with timeouts
There is two ways of declaring timeouts in SimGrid. waituntil
let you specify the deadline until when you want to wait, while
waitfor
expects the maximal wait duration.
This example is very similar to the previous one, simply adding how to declare timeouts when waiting on asynchronous communication.
See also simgrid::s4u::Activity::wait_until()
and simgrid::s4u::Comm::wait_for()
.
View examples/cpp/comm-waituntil/s4u-comm-waituntil.cpp
Download s4u-comm-waituntil.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This example shows how to use simgrid::s4u::Activity::wait_until() and
* simgrid::s4u::Activity::wait_for() on a given communication.
*
* It is very similar to the comm-wait example, but the sender initially
* does some waits that are too short before doing an infinite wait.
*/
#include "simgrid/s4u.hpp"
#include <cstdlib>
#include <iostream>
#include <string>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_comm_waituntil, "Messages specific for this s4u example");
static void sender(int messages_count, size_t payload_size)
{
std::vector<sg4::CommPtr> pending_comms;
sg4::Mailbox* mbox = sg4::Mailbox::by_name("receiver-0");
/* Start dispatching all messages to the receiver */
for (int i = 0; i < messages_count; i++) {
std::string message = "Message " + std::to_string(i);
auto* payload = new std::string(message); // copy the data we send:
// 'msgName' is not a stable storage location
XBT_INFO("Send '%s' to '%s'", message.c_str(), mbox->get_cname());
/* Create a communication representing the ongoing communication */
sg4::CommPtr comm = mbox->put_async(payload, payload_size);
/* Add this comm to the vector of all known comms */
pending_comms.push_back(comm);
}
/* Start the finalize signal to the receiver*/
auto* payload = new std::string("finalize"); // Make a copy of the data we will send
sg4::CommPtr final_comm = mbox->put_async(payload, 0);
pending_comms.push_back(final_comm);
XBT_INFO("Send 'finalize' to 'receiver-0'");
XBT_INFO("Done dispatching all messages");
/* Now that all message exchanges were initiated, wait for their completion, in order of creation. */
while (not pending_comms.empty()) {
sg4::CommPtr comm = pending_comms.back();
comm->wait_for(1);
pending_comms.pop_back(); // remove it from the list
}
XBT_INFO("Goodbye now!");
}
static void receiver()
{
sg4::Mailbox* mbox = sg4::Mailbox::by_name("receiver-0");
XBT_INFO("Wait for my first message");
for (bool cont = true; cont;) {
auto received = mbox->get_unique<std::string>();
XBT_INFO("I got a '%s'.", received->c_str());
if (*received == "finalize")
cont = false; // If it's a finalize message, we're done.
}
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("sender", e.host_by_name("Tremblay"), sender, 3, 5e7);
sg4::Actor::create("receiver", e.host_by_name("Ruby"), receiver);
e.run();
return 0;
}
See also simgrid.Comm.wait_until()
View examples/python/comm-waituntil/comm-waituntil.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
""" This example demonstrates Comm.wait_for() and Comm.wait_until to set timeouts on waits.
"""
from argparse import ArgumentParser
from typing import List
import sys
from simgrid import Actor, Comm, Engine, Mailbox, this_actor
FINALIZE_MESSAGE = "finalize"
def create_parser() -> ArgumentParser:
parser = ArgumentParser()
parser.add_argument(
'--platform',
type=str,
required=True,
help='path to the platform description'
)
return parser
def sender(receiver_mailbox: Mailbox, messages_count: int, payload_size: int):
pending_comms: List[Comm] = []
# Start dispatching all messages to the receiver
for i in range(messages_count):
payload = f"Message {i}"
this_actor.info(f"Send '{payload}' to '{receiver_mailbox.name}'")
# Create a communication representing the ongoing communication
comm = receiver_mailbox.put_async(payload, payload_size)
# Add this comm to the vector of all known comms
pending_comms.append(comm)
# Start the finalize signal to the receiver
final_comm = receiver_mailbox.put_async(FINALIZE_MESSAGE, 0)
pending_comms.append(final_comm)
this_actor.info(f"Send '{FINALIZE_MESSAGE}' to '{receiver_mailbox.name}'")
this_actor.info("Done dispatching all messages")
# Now that all message exchanges were initiated, wait for their completion, in order of creation
while pending_comms:
comm = pending_comms[-1]
comm.wait_until(Engine.clock + 1) # same as: current_comm.wait_for(1.0)
pending_comms.pop() # remove it from the list
this_actor.info("Goodbye now!")
def receiver(mailbox: Mailbox):
this_actor.info("Wait for my first message")
finalized = False
while not finalized:
received: str = mailbox.get()
this_actor.info(f"I got a '{received}'.")
# If it's a finalize message, we're done.
if received == FINALIZE_MESSAGE:
finalized = True
def main():
settings = create_parser().parse_known_args()[0]
e = Engine(sys.argv)
e.load_platform(settings.platform)
receiver_mailbox: Mailbox = Mailbox.by_name("receiver")
Actor.create("sender", e.host_by_name("Tremblay"), sender, receiver_mailbox, 3, int(5e7))
Actor.create("receiver", e.host_by_name("Ruby"), receiver, receiver_mailbox)
e.run()
if __name__ == "__main__":
main()
Checking for incoming communications
This example uses Mailbox.ready()
to check for completed communications. When this function returns true, then at least a message
is arrived, so you know that Mailbox.get()
will complete immediately. This is thus another way toward asynchronous communications.
See also simgrid::s4u::Mailbox::ready()
.
View examples/cpp/comm-ready/s4u-comm-ready.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This example shows how to use simgrid::s4u::Mailbox::ready() to check for completed communications.
*
* We have a number of peers which send and receive messages in two phases:
* -> sending phase: each one of them sends a number of messages to the others followed
* by a single "finalize" message.
* -> receiving phase: each one of them receives all the available messages that reached
* their corresponding mailbox until it has all the needed "finalize"
* messages to know that no more work needs to be done.
*
* To avoid doing a wait() over the ongoing communications, each peer makes use of the
* simgrid::s4u::Mailbox::ready() method. If it returns true then a following get() will fetch the
* message immediately, if not the peer will sleep for a fixed amount of time before checking again.
*
*/
#include "simgrid/s4u.hpp"
#include <cstdlib>
#include <iostream>
#include <string>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_async_ready, "Messages specific for this s4u example");
static void peer(int my_id, int messages_count, size_t payload_size, int peers_count)
{
/* Set myself as the persistent receiver of my mailbox so that messages start flowing to me as soon as they are put
* into it */
sg4::Mailbox* my_mbox = sg4::Mailbox::by_name("peer-" + std::to_string(my_id));
my_mbox->set_receiver(sg4::Actor::self());
sg4::ActivitySet pending_comms;
/* Start dispatching all messages to peers others that myself */
for (int i = 0; i < messages_count; i++) {
for (int peer_id = 0; peer_id < peers_count; peer_id++) {
if (peer_id != my_id) {
sg4::Mailbox* mbox = sg4::Mailbox::by_name("peer-" + std::to_string(peer_id));
std::string message = "Message " + std::to_string(i) + " from peer " + std::to_string(my_id);
auto* payload = new std::string(message); // copy the data we send:
// 'message' is not a stable storage location
XBT_INFO("Send '%s' to '%s'", message.c_str(), mbox->get_cname());
/* Create a communication representing the ongoing communication */
pending_comms.push(mbox->put_async(payload, payload_size));
}
}
}
/* Start sending messages to let peers know that they should stop */
for (int peer_id = 0; peer_id < peers_count; peer_id++) {
if (peer_id != my_id) {
sg4::Mailbox* mbox = sg4::Mailbox::by_name("peer-" + std::to_string(peer_id));
auto* payload = new std::string("finalize"); // Make a copy of the data we will send
pending_comms.push(mbox->put_async(payload, payload_size));
XBT_INFO("Send 'finalize' to 'peer-%d'", peer_id);
}
}
XBT_INFO("Done dispatching all messages");
/* Retrieve all the messages other peers have been sending to me until I receive all the corresponding "Finalize"
* messages */
long pending_finalize_messages = peers_count - 1;
while (pending_finalize_messages > 0) {
if (my_mbox->ready()) {
double start = sg4::Engine::get_clock();
auto received = my_mbox->get_unique<std::string>();
double waiting_time = sg4::Engine::get_clock() - start;
xbt_assert(
waiting_time == 0,
"Expecting the waiting time to be 0 because the communication was supposedly ready, but got %f instead",
waiting_time);
XBT_INFO("I got a '%s'.", received->c_str());
if (*received == "finalize") {
pending_finalize_messages--;
}
} else {
XBT_INFO("Nothing ready to consume yet, I better sleep for a while");
sg4::this_actor::sleep_for(.01);
}
}
XBT_INFO("I'm done, just waiting for my peers to receive the messages before exiting");
pending_comms.wait_all();
XBT_INFO("Goodbye now!");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("peer", e.host_by_name("Tremblay"), peer, 0, 2, 5e7, 3);
sg4::Actor::create("peer", e.host_by_name("Ruby"), peer, 1, 6, 2.5e5, 3);
sg4::Actor::create("peer", e.host_by_name("Perl"), peer, 2, 0, 5e7, 3);
e.run();
return 0;
}
See also simgrid.Mailbox.ready()
View examples/python/comm-ready/comm-ready.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
from argparse import ArgumentParser
from typing import List
import sys
from simgrid import Actor, ActivitySet, Comm, Engine, Mailbox, this_actor
FINALIZE_MESSAGE = "finalize"
def create_parser() -> ArgumentParser:
parser = ArgumentParser()
parser.add_argument(
'--platform',
type=str,
required=True,
help='path to the platform description'
)
return parser
def get_peer_mailbox(peer_id: int) -> Mailbox:
return Mailbox.by_name(f"peer-{peer_id}")
def peer(my_id: int, message_count: int, payload_size: int, peers_count: int):
my_mailbox: Mailbox = get_peer_mailbox(my_id)
my_mailbox.set_receiver(Actor.self())
pending_comms = ActivitySet()
# Start dispatching all messages to peers others that myself
for i in range(message_count):
for peer_id in range(peers_count):
if peer_id != my_id:
peer_mailbox = get_peer_mailbox(peer_id)
message = f"Message {i} from peer {my_id}"
this_actor.info(f"Send '{message}' to '{peer_mailbox.name}'")
pending_comms.push(peer_mailbox.put_async(message, payload_size))
# Start sending messages to let peers know that they should stop
for peer_id in range(peers_count):
if peer_id != my_id:
peer_mailbox = get_peer_mailbox(peer_id)
payload = str(FINALIZE_MESSAGE)
pending_comms.push(peer_mailbox.put_async(payload, payload_size))
this_actor.info(f"Send '{payload}' to '{peer_mailbox.name}'")
this_actor.info("Done dispatching all messages")
# Retrieve all the messages other peers have been sending to me until I receive all the corresponding "Finalize"
# messages
pending_finalize_messages = peers_count - 1
while pending_finalize_messages > 0:
if my_mailbox.ready:
start = Engine.clock
received: str = my_mailbox.get()
waiting_time = Engine.clock - start
if waiting_time > 0.0:
raise AssertionError(f"Expecting the waiting time to be 0.0 because the communication was supposedly "
f"ready, but got {waiting_time} instead")
this_actor.info(f"I got a '{received}'.")
if received == FINALIZE_MESSAGE:
pending_finalize_messages -= 1
else:
this_actor.info("Nothing ready to consume yet, I better sleep for a while")
this_actor.sleep_for(0.01)
this_actor.info("I'm done, just waiting for my peers to receive the messages before exiting")
pending_comms.wait_all()
this_actor.info("Goodbye now!")
def main():
settings = create_parser().parse_known_args()[0]
e = Engine(sys.argv)
e.load_platform(settings.platform)
Actor.create("peer", e.host_by_name("Tremblay"), peer, 0, 2, int(5e7), 3)
Actor.create("peer", e.host_by_name("Ruby"), peer, 1, 6, int(2.5e5), 3)
Actor.create("peer", e.host_by_name("Perl"), peer, 2, 0, int(5e7), 3)
e.run()
if __name__ == "__main__":
main()
Suspending communications
The suspend()
and resume()
functions block the progression of a given communication for a while and then unblock it.
is_suspended()
returns whether that activity is currently blocked or not.
See also simgrid::s4u::Activity::suspend()
simgrid::s4u::Activity::resume()
and
simgrid::s4u::Activity::is_suspended()
.
View examples/cpp/comm-suspend/s4u-comm-suspend.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This example shows how to suspend and resume an asynchronous communication. */
#include "simgrid/s4u.hpp"
#include <cstdlib>
#include <iostream>
#include <string>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_comm_wait, "Messages specific for this s4u example");
static void sender()
{
sg4::Mailbox* mbox = sg4::Mailbox::by_name("receiver");
// Copy the data we send: the 'msg_content' variable is not a stable storage location.
// It will be destroyed when this actor leaves the loop, ie before the receiver gets the data
auto* payload = new std::string("Sent message");
/* Create a communication representing the ongoing communication and then */
sg4::CommPtr comm = mbox->put_init(payload, 13194230);
XBT_INFO("Suspend the communication before it starts (remaining: %.0f bytes) and wait a second.",
comm->get_remaining());
sg4::this_actor::sleep_for(1);
XBT_INFO("Now, start the communication (remaining: %.0f bytes) and wait another second.", comm->get_remaining());
comm->start();
sg4::this_actor::sleep_for(1);
XBT_INFO("There is still %.0f bytes to transfer in this communication. Suspend it for one second.",
comm->get_remaining());
comm->suspend();
XBT_INFO("Now there is %.0f bytes to transfer. Resume it and wait for its completion.", comm->get_remaining());
comm->resume();
comm->wait();
XBT_INFO("There is %f bytes to transfer after the communication completion.", comm->get_remaining());
XBT_INFO("Suspending a completed activity is a no-op.");
comm->suspend();
}
static void receiver()
{
sg4::Mailbox* mbox = sg4::Mailbox::by_name("receiver");
XBT_INFO("Wait for the message.");
auto received = mbox->get_unique<std::string>();
XBT_INFO("I got '%s'.", received->c_str());
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("sender", e.host_by_name("Tremblay"), sender);
sg4::Actor::create("receiver", e.host_by_name("Jupiter"), receiver);
e.run();
return 0;
}
See also simgrid.Comm.suspend()
and
simgrid.Comm.resume()
.
View examples/python/comm-suspend/comm-suspend.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
""" This example shows how to suspend and resume an asynchronous communication.
"""
from argparse import ArgumentParser
import sys
from simgrid import Actor, Comm, Engine, Mailbox, this_actor
def create_parser() -> ArgumentParser:
parser = ArgumentParser()
parser.add_argument(
'--platform',
type=str,
required=True,
help='path to the platform description'
)
return parser
def sender():
mailbox: Mailbox = Mailbox.by_name("receiver")
payload = "Sent message"
# Create a communication representing the ongoing communication and then
simulated_size_in_bytes = 13194230
comm: Comm = mailbox.put_init(payload, simulated_size_in_bytes)
this_actor.info(f"Suspend the communication before it starts (remaining: {comm.remaining:.0f} bytes)"
f" and wait a second.")
this_actor.sleep_for(1)
this_actor.info(f"Now, start the communication (remaining: {comm.remaining:.0f} bytes) and wait another second.")
comm.start()
this_actor.sleep_for(1)
this_actor.info(f"There is still {comm.remaining:.0f} bytes to transfer in this communication."
" Suspend it for one second.")
comm.suspend()
this_actor.info(f"Now there is {comm.remaining:.0f} bytes to transfer. Resume it and wait for its completion.")
comm.resume()
comm.wait()
this_actor.info(f"There is {comm.remaining:.0f} bytes to transfer after the communication completion.")
this_actor.info("Suspending a completed activity is a no-op.")
comm.suspend()
def receiver():
mailbox: Mailbox = Mailbox.by_name("receiver")
this_actor.info("Wait for the message.")
received: str = mailbox.get()
this_actor.info(f"I got '{received}'.")
def main():
settings = create_parser().parse_known_args()[0]
e = Engine(sys.argv)
e.load_platform(settings.platform)
Actor.create("sender", e.host_by_name("Tremblay"), sender)
Actor.create("receiver", e.host_by_name("Jupiter"), receiver)
e.run()
if __name__ == "__main__":
main()
Dealing with network failures
This examples shows how to survive to network exceptions that occurs when a link is turned off, or when the actor with whom
you communicate fails because its host is turned off. In this case, any blocking operation such as put
, get
or
wait
will raise an exception that you can catch and react to. See also Modeling churn (e.g., in P2P),
this example on how to attach a state profile to hosts and
that example on how to react to host failures.
View examples/cpp/comm-failure/s4u-comm-failure.cpp
/* Copyright (c) 2021-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This example shows how to react to a failed communication, which occurs when a link is turned off,
* or when the actor with whom you communicate fails because its host is turned off.
*/
#include <simgrid/s4u.hpp>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_comm_failure, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
class Sender {
std::string mailbox1_name;
std::string mailbox2_name;
public:
Sender(const std::string& mailbox1_name, const std::string& mailbox2_name)
: mailbox1_name(mailbox1_name), mailbox2_name(mailbox2_name)
{
}
void operator()() const
{
auto* mailbox1 = sg4::Mailbox::by_name(mailbox1_name);
auto* mailbox2 = sg4::Mailbox::by_name(mailbox2_name);
XBT_INFO("Initiating asynchronous send to %s", mailbox1->get_cname());
auto comm1 = mailbox1->put_async((void*)666, 5);
XBT_INFO("Initiating asynchronous send to %s", mailbox2->get_cname());
auto comm2 = mailbox2->put_async((void*)666, 2);
XBT_INFO("Calling wait_any..");
sg4::ActivitySet pending_comms;
pending_comms.push(comm1);
pending_comms.push(comm2);
try {
auto* acti = pending_comms.wait_any().get();
XBT_INFO("Wait any returned comm to %s", dynamic_cast<sg4::Comm*>(acti)->get_mailbox()->get_cname());
} catch (const simgrid::NetworkFailureException&) {
XBT_INFO("Sender has experienced a network failure exception, so it knows that something went wrong");
XBT_INFO("Now it needs to figure out which of the two comms failed by looking at their state:");
XBT_INFO(" Comm to %s has state: %s", comm1->get_mailbox()->get_cname(), comm1->get_state_str());
XBT_INFO(" Comm to %s has state: %s", comm2->get_mailbox()->get_cname(), comm2->get_state_str());
}
try {
comm1->wait();
} catch (const simgrid::NetworkFailureException& e) {
XBT_INFO("Waiting on a FAILED comm raises an exception: '%s'", e.what());
}
XBT_INFO("Wait for remaining comm, just to be nice");
pending_comms.wait_all();
}
};
class Receiver {
sg4::Mailbox* mailbox;
public:
explicit Receiver(const std::string& mailbox_name) : mailbox(sg4::Mailbox::by_name(mailbox_name)) {}
void operator()() const
{
XBT_INFO("Receiver posting a receive...");
try {
mailbox->get<void*>();
XBT_INFO("Receiver has received successfully!");
} catch (const simgrid::NetworkFailureException&) {
XBT_INFO("Receiver has experience a network failure exception");
}
}
};
int main(int argc, char** argv)
{
sg4::Engine engine(&argc, argv);
auto* zone = sg4::create_full_zone("AS0");
auto* host1 = zone->create_host("Host1", "1f");
auto* host2 = zone->create_host("Host2", "1f");
auto* host3 = zone->create_host("Host3", "1f");
const auto* link2 = zone->create_link("linkto2", "1bps")->seal();
const auto* link3 = zone->create_link("linkto3", "1bps")->seal();
zone->add_route(host1, host2, {link2});
zone->add_route(host1, host3, {link3});
zone->seal();
sg4::Actor::create("Sender", host1, Sender("mailbox2", "mailbox3"));
sg4::Actor::create("Receiver", host2, Receiver("mailbox2"));
sg4::Actor::create("Receiver", host3, Receiver("mailbox3"));
sg4::Actor::create("LinkKiller", host1, [](){
sg4::this_actor::sleep_for(10.0);
XBT_INFO("Turning off link 'linkto2'");
sg4::Link::by_name("linkto2")->turn_off();
});
engine.run();
return 0;
}
View examples/python/comm-failure/comm-failure.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
import sys
from simgrid import Engine, Actor, ActivitySet, Comm, NetZone, Link, LinkInRoute, Mailbox, this_actor, NetworkFailureException
def sender(mailbox1_name: str, mailbox2_name: str) -> None:
mailbox1: Mailbox = Mailbox.by_name(mailbox1_name)
mailbox2: Mailbox = Mailbox.by_name(mailbox2_name)
this_actor.info(f"Initiating asynchronous send to {mailbox1.name}")
comm1: Comm = mailbox1.put_async(666, 5)
this_actor.info(f"Initiating asynchronous send to {mailbox2.name}")
comm2: Comm = mailbox2.put_async(666, 2)
this_actor.info("Calling wait_any..")
pending_comms = ActivitySet([comm1, comm2])
try:
comm = pending_comms.wait_any()
this_actor.info(f"Wait any returned a comm to {comm.mailbox.name})")
except NetworkFailureException:
this_actor.info("Sender has experienced a network failure exception, so it knows that something went wrong")
this_actor.info("Now it needs to figure out which of the two comms failed by looking at their state:")
this_actor.info(f" Comm to {comm1.mailbox.name} has state: {comm1.state_str}")
this_actor.info(f" Comm to {comm2.mailbox.name} has state: {comm2.state_str}")
try:
comm1.wait()
except NetworkFailureException as err:
this_actor.info(f"Waiting on a FAILED comm raises an exception: '{err}'")
this_actor.info("Wait for remaining comm, just to be nice")
try:
pending_comms.wait_all()
except Exception as e:
this_actor.warning(str(e))
def receiver(mailbox_name: str) -> None:
mailbox: Mailbox = Mailbox.by_name(mailbox_name)
this_actor.info(f"Receiver posting a receive ({mailbox_name})...")
try:
mailbox.get()
this_actor.info(f"Receiver has received successfully ({mailbox_name})!")
except NetworkFailureException:
this_actor.info(f"Receiver has experience a network failure exception ({mailbox_name})")
def link_killer(link_name: str) -> None:
link_to_kill = Link.by_name(link_name)
this_actor.sleep_for(10.0)
this_actor.info(f"Turning off link '{link_to_kill.name}'")
link_to_kill.turn_off()
def main():
e = Engine(sys.argv)
zone: NetZone = NetZone.create_full_zone("AS0")
host1 = zone.create_host("Host1", "1f")
host2 = zone.create_host("Host2", "1f")
host3 = zone.create_host("Host3", "1f")
link_to_2 = zone.create_link("link_to_2", "1bps").seal()
link_to_3 = zone.create_link("link_to_3", "1bps").seal()
zone.add_route(host1, host2, [link_to_2])
zone.add_route(host1, host3, [link_to_3])
zone.seal()
Actor.create("Sender", host1, sender, "mailbox2", "mailbox3")
Actor.create("Receiver-1", host2, receiver, "mailbox2").daemonize()
Actor.create("Receiver-2", host3, receiver, "mailbox3").daemonize()
Actor.create("LinkKiller", host2, link_killer, "link_to_2").daemonize()
e.run()
if __name__ == "__main__":
main()
Direct host-to-host communication
This example demonstrates the direct communication mechanism, that allows to send data from one host to another without relying on the mailbox mechanism.
See also simgrid::s4u::Comm::sendto_init()
and simgrid::s4u::Comm::sendto_async()
.
View examples/cpp/comm-host2host/s4u-comm-host2host.cpp
Download s4u-comm-host2host.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This simple example demonstrates the Comm::sento_init() Comm::sento_async() functions,
that can be used to create a direct communication from one host to another without
relying on the mailbox mechanism.
There is not much to say, actually: The _init variant creates the communication and
leaves it unstarted (in case you want to modify this communication before it starts),
while the _async variant creates and start it. In both cases, you need to wait() it.
It is mostly useful when you want to have a centralized simulation of your settings,
with a central actor declaring all communications occurring on your distributed system.
*/
#include <simgrid/s4u.hpp>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_comm_host2host, "Messages specific for this s4u example");
static void sender(sg4::Host* h1, sg4::Host* h2, sg4::Host* h3, sg4::Host* h4)
{
XBT_INFO("Send c12 with sendto_async(%s -> %s), and c34 with sendto_init(%s -> %s)", h1->get_cname(), h2->get_cname(),
h3->get_cname(), h4->get_cname());
auto c12 = sg4::Comm::sendto_async(h1, h2, 1.5e7); // Creates and start a direct communication
auto c34 = sg4::Comm::sendto_init(h3, h4); // Creates but do not start another direct communication
c34->set_payload_size(1e7); // Specify the amount of bytes to exchange in this comm
// You can also detach() communications that you never plan to test() or wait().
// Here we create a communication that only slows down the other ones
auto noise = sg4::Comm::sendto_init(h1, h2);
noise->set_payload_size(10000);
noise->detach();
XBT_INFO("After creation, c12 is %s (remaining: %.2e bytes); c34 is %s (remaining: %.2e bytes)",
c12->get_state_str(), c12->get_remaining(), c34->get_state_str(), c34->get_remaining());
sg4::this_actor::sleep_for(1);
XBT_INFO("One sec later, c12 is %s (remaining: %.2e bytes); c34 is %s (remaining: %.2e bytes)",
c12->get_state_str(), c12->get_remaining(), c34->get_state_str(), c34->get_remaining());
c34->start();
XBT_INFO("After c34->start,c12 is %s (remaining: %.2e bytes); c34 is %s (remaining: %.2e bytes)",
c12->get_state_str(), c12->get_remaining(), c34->get_state_str(), c34->get_remaining());
c12->wait();
XBT_INFO("After c12->wait, c12 is %s (remaining: %.2e bytes); c34 is %s (remaining: %.2e bytes)",
c12->get_state_str(), c12->get_remaining(), c34->get_state_str(), c34->get_remaining());
c34->wait();
XBT_INFO("After c34->wait, c12 is %s (remaining: %.2e bytes); c34 is %s (remaining: %.2e bytes)",
c12->get_state_str(), c12->get_remaining(), c34->get_state_str(), c34->get_remaining());
/* As usual, you don't have to explicitly start communications that were just init()ed.
The wait() will start it automatically. */
auto c14 = sg4::Comm::sendto_init(h1, h4);
c14->set_payload_size(100)->wait(); // Chaining 2 operations on this new communication
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("sender", e.host_by_name("Boivin"), sender, e.host_by_name("Tremblay"), e.host_by_name("Jupiter"),
e.host_by_name("Fafard"), e.host_by_name("Ginette"));
e.run();
XBT_INFO("Total simulation time: %.3f", sg4::Engine::get_clock());
return 0;
}
See also simgrid.Comm.sendto_init()
and simgrid.Comm.sendto_async()
.
View examples/python/comm-host2host/comm-host2host.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
This simple example demonstrates the Comm.sento_init() Comm.sento_async() functions,
that can be used to create a direct communication from one host to another without
relying on the mailbox mechanism.
There is not much to say, actually: The _init variant creates the communication and
leaves it unstarted (in case you want to modify this communication before it starts),
while the _async variant creates and start it. In both cases, you need to wait() it.
It is mostly useful when you want to have a centralized simulation of your settings,
with a central actor declaring all communications occurring on your distributed system.
"""
from argparse import ArgumentParser
import sys
from simgrid import Actor, Comm, Engine, Host, this_actor
def create_parser() -> ArgumentParser:
parser = ArgumentParser()
parser.add_argument(
'--platform',
type=str,
required=True,
help='path to the platform description'
)
return parser
def sender(h1: Host, h2: Host, h3: Host, h4: Host):
this_actor.info(f"Send c12 with sendto_async({h1.name} -> {h2.name}),"
f" and c34 with sendto_init({h3.name} -> {h4.name})")
c12: Comm = Comm.sendto_async(h1, h2, int(1.5e7))
c34: Comm = Comm.sendto_init(h3, h4)
c34.set_payload_size(int(1e7))
# You can also detach() communications that you never plan to test() or wait().
# Here we create a communication that only slows down the other ones
noise: Comm = Comm.sendto_init(h1, h2)
noise.set_payload_size(10000)
noise.detach()
this_actor.info(f"After creation, c12 is {c12.state_str} (remaining: {c12.remaining:.2e} bytes);"
f" c34 is {c34.state_str} (remaining: {c34.remaining:.2e} bytes)")
this_actor.sleep_for(1)
this_actor.info(f"One sec later, c12 is {c12.state_str} (remaining: {c12.remaining:.2e} bytes);"
f" c34 is {c34.state_str} (remaining: {c34.remaining:.2e} bytes)")
c34.start()
this_actor.info(f"After c34.start(), c12 is {c12.state_str} (remaining: {c12.remaining:.2e} bytes);"
f" c34 is {c34.state_str} (remaining: {c34.remaining:.2e} bytes)")
c12.wait()
this_actor.info(f"After c12.wait(), c12 is {c12.state_str} (remaining: {c12.remaining:.2e} bytes);"
f" c34 is {c34.state_str} (remaining: {c34.remaining:.2e} bytes)")
c34.wait()
this_actor.info(f"After c34.wait(), c12 is {c12.state_str} (remaining: {c12.remaining:.2e} bytes);"
f" c34 is {c34.state_str} (remaining: {c34.remaining:.2e} bytes)")
# As usual, you don't have to explicitly start communications that were just init()ed.
# The wait() will start it automatically.
c14: Comm = Comm.sendto_init(h1, h4)
c14.set_payload_size(100).wait()
def main():
settings = create_parser().parse_known_args()[0]
e = Engine(sys.argv)
e.load_platform(settings.platform)
Actor.create(
"sender", e.host_by_name("Boivin"), sender,
e.host_by_name("Tremblay"), # h1
e.host_by_name("Jupiter"), # h2
e.host_by_name("Fafard"), # h3
e.host_by_name("Ginette") # h4
)
e.run()
this_actor.info(f"Total simulation time: {e.clock:.3f}")
if __name__ == "__main__":
main()
Executions on the CPU
Basic execution
The computations done in your program are not reported to the simulated world unless you explicitly request the simulator to pause the actor until a given amount of flops gets computed on its simulated host. Some executions can be given a higher priority so that they get more resources.
See also void simgrid::s4u::this_actor::execute(double)
and void simgrid::s4u::this_actor::execute(double, double)
.
View examples/cpp/exec-basic/s4u-exec-basic.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void executor()
{
/* this_actor::execute() tells SimGrid to pause the calling actor
* until its host has computed the amount of flops passed as a parameter */
sg4::this_actor::execute(98095);
XBT_INFO("Done.");
/* This simple example does not do anything beyond that */
}
static void privileged()
{
/* This version of this_actor::execute() specifies that this execution gets a larger share of the resource.
*
* Since the priority is 2, it computes twice as fast as a regular one.
*
* So instead of a half/half sharing between the two executions, we get a 1/3 vs 2/3 sharing. */
sg4::this_actor::execute(98095, 2);
XBT_INFO("Done.");
/* Note that the timings printed when running this example are a bit misleading, because the uneven sharing only last
* until the privileged actor ends. After this point, the unprivileged one gets 100% of the CPU and finishes quite
* quickly. */
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform_file\n\tExample: %s platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
sg4::Actor::create("executor", e.host_by_name("Tremblay"), executor);
sg4::Actor::create("privileged", e.host_by_name("Tremblay"), privileged);
e.run();
return 0;
}
See also simgrid.this_actor.execute()
.
View examples/python/exec-basic/exec-basic.py
# Copyright (c) 2018-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
import sys
from simgrid import Actor, Engine, Host, this_actor
def executor():
# execute() tells SimGrid to pause the calling actor until
# its host has computed the amount of flops passed as a parameter
this_actor.execute(98095)
this_actor.info("Done.")
# This simple example does not do anything beyond that
def privileged():
# You can also specify the priority of your execution as follows.
# An execution of priority 2 computes twice as fast as a regular one.
#
# So instead of a half/half sharing between the two executions,
# we get a 1/3 vs 2/3 sharing.
this_actor.execute(98095, priority=2)
this_actor.info("Done.")
# Note that the timings printed when executing this example are a bit misleading,
# because the uneven sharing only last until the privileged actor ends.
# After this point, the unprivileged one gets 100% of the CPU and finishes
# quite quickly.
if __name__ == '__main__':
e = Engine(sys.argv)
e.load_platform(sys.argv[1])
Actor.create("executor", Host.by_name("Tremblay"), executor)
Actor.create("privileged", Host.by_name("Tremblay"), privileged)
e.run()
See also void sg_actor_execute(double)
and void sg_actor_execute_with_priority(double, double)
.
View examples/c/exec-basic/exec-basic.c
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "xbt/asserts.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(exec_basic, "Messages specific for this example");
static void executor(int argc, char* argv[])
{
/* sg_actor_execute() tells SimGrid to pause the calling actor
* until its host has computed the amount of flops passed as a parameter */
sg_actor_execute(98095);
XBT_INFO("Done.");
/* This simple example does not do anything beyond that */
}
static void privileged(int argc, char* argv[])
{
/* sg_actor_execute_with_priority() specifies that this execution gets a larger share of the resource.
*
* Since the priority is 2, it computes twice as fast as a regular one.
*
* So instead of a half/half sharing between the two executions,
* we get a 1/3 vs 2/3 sharing. */
sg_actor_execute_with_priority(98095, 2);
XBT_INFO("Done.");
/* Note that the timings printed when executing this example are a bit misleading,
* because the uneven sharing only last until the privileged actor ends.
* After this point, the unprivileged one gets 100% of the CPU and finishes
* quite quickly. */
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform_file\n\tExample: %s ../platforms/small_platform.xml\n", argv[0], argv[0]);
simgrid_load_platform(argv[1]);
sg_actor_create("executor", sg_host_by_name("Tremblay"), &executor, 0, NULL);
sg_actor_create("privileged", sg_host_by_name("Tremblay"), &privileged, 0, NULL);
simgrid_run();
return 0;
}
Asynchronous execution
You can start asynchronous executions, just like you would fire background threads.
See also simgrid::s4u::this_actor::exec_init()
,
simgrid::s4u::Activity::start()
,
simgrid::s4u::Activity::wait()
,
simgrid::s4u::Activity::get_remaining()
,
simgrid::s4u::Exec::get_remaining_ratio()
,
simgrid::s4u::this_actor::exec_async()
and
simgrid::s4u::Activity::cancel()
.
View examples/cpp/exec-async/s4u-exec-async.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
/* This actor simply waits for its activity completion after starting it.
* That's exactly equivalent to synchronous execution. */
static void waiter()
{
double computation_amount = sg4::this_actor::get_host()->get_speed();
XBT_INFO("Execute %g flops, should take 1 second.", computation_amount);
sg4::ExecPtr activity = sg4::this_actor::exec_init(computation_amount);
activity->start();
activity->wait();
XBT_INFO("Goodbye now!");
}
/* This actor tests the ongoing execution until its completion, and don't wait before it's terminated. */
static void monitor()
{
double computation_amount = sg4::this_actor::get_host()->get_speed();
XBT_INFO("Execute %g flops, should take 1 second.", computation_amount);
sg4::ExecPtr activity = sg4::this_actor::exec_init(computation_amount);
activity->start();
while (not activity->test()) {
XBT_INFO("Remaining amount of flops: %g (%.0f%%)", activity->get_remaining(),
100 * activity->get_remaining_ratio());
sg4::this_actor::sleep_for(0.3);
}
XBT_INFO("Goodbye now!");
}
/* This actor cancels the ongoing execution after a while. */
static void canceller()
{
double computation_amount = sg4::this_actor::get_host()->get_speed();
XBT_INFO("Execute %g flops, should take 1 second.", computation_amount);
sg4::ExecPtr activity = sg4::this_actor::exec_async(computation_amount);
sg4::this_actor::sleep_for(0.5);
XBT_INFO("I changed my mind, cancel!");
activity->cancel();
XBT_INFO("Goodbye now!");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Host* fafard = e.host_by_name("Fafard");
sg4::Host* ginette = e.host_by_name("Ginette");
sg4::Host* boivin = e.host_by_name("Boivin");
sg4::Actor::create("wait", fafard, waiter);
sg4::Actor::create("monitor", ginette, monitor);
sg4::Actor::create("cancel", boivin, canceller);
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
See also simgrid.this_actor.exec_init()
,
simgrid.Exec.start()
,
simgrid.Exec.wait()
,
simgrid.Exec.remaining
,
simgrid.Exec.remaining_ratio
,
simgrid.this_actor.exec_async()
and
simgrid.Exec.cancel()
.
View examples/python/exec-async/exec-async.py
# Copyright (c) 2018-2024. The SimGrid Team. All rights reserved.
#
# This program is free software you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
Usage: exec-async.py platform_file [other parameters]
"""
import sys
from simgrid import Actor, Engine, Host, this_actor
class Waiter:
"""
This actor simply waits for its task completion after starting it.
That's exactly equivalent to synchronous execution.
"""
def __call__(self):
computation_amount = this_actor.get_host().speed
this_actor.info("Waiter executes {:.0f} flops, should take 1 second.".format(computation_amount))
activity = this_actor.exec_init(computation_amount)
activity.start()
activity.wait()
this_actor.info("Goodbye from waiter!")
class Monitor:
"""This actor tests the ongoing execution until its completion, and don't wait before it's terminated."""
def __call__(self):
computation_amount = this_actor.get_host().speed
this_actor.info("Monitor executes {:.0f} flops, should take 1 second.".format(computation_amount))
activity = this_actor.exec_init(computation_amount).start()
while not activity.test():
this_actor.info("Remaining amount of flops: {:.0f} ({:.0f}%)".format(
activity.remaining, 100 * activity.remaining_ratio))
this_actor.sleep_for(0.3)
activity.wait()
this_actor.info("Goodbye from monitor!")
class Canceller:
"""This actor cancels the ongoing execution after a while."""
def __call__(self):
computation_amount = this_actor.get_host().speed
this_actor.info("Canceller executes {:.0f} flops, should take 1 second.".format(computation_amount))
activity = this_actor.exec_async(computation_amount)
this_actor.sleep_for(0.5)
this_actor.info("I changed my mind, cancel!")
activity.cancel()
this_actor.info("Goodbye from canceller!")
if __name__ == '__main__':
e = Engine(sys.argv)
if len(sys.argv) < 2:
raise AssertionError("Usage: exec-async.py platform_file [other parameters]")
e.load_platform(sys.argv[1])
Actor.create("wait", Host.by_name("Fafard"), Waiter())
Actor.create("monitor", Host.by_name("Ginette"), Monitor())
Actor.create("cancel", Host.by_name("Boivin"), Canceller())
e.run()
See also sg_actor_exec_init()
,
sg_exec_start()
,
sg_exec_wait()
,
sg_exec_get_remaining()
,
sg_exec_get_remaining_ratio()
,
sg_actor_exec_async()
and
sg_exec_cancel()
,
View examples/c/exec-async/exec-async.c
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/exec.h"
#include "simgrid/host.h"
#include "xbt/asserts.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(exec_async, "Messages specific for this example");
/* This actor simply waits for its task completion after starting it.
* That's exactly equivalent to synchronous execution. */
static void waiter(int argc, char* argv[])
{
double computation_amount = sg_host_get_speed(sg_host_self());
XBT_INFO("Execute %g flops, should take 1 second.", computation_amount);
sg_exec_t activity = sg_actor_exec_init(computation_amount);
sg_exec_start(activity);
sg_exec_wait(activity);
XBT_INFO("Goodbye now!");
}
/* This actor tests the ongoing execution until its completion, and don't wait before it's terminated. */
static void monitor(int argc, char* argv[])
{
double computation_amount = sg_host_get_speed(sg_host_self());
XBT_INFO("Execute %g flops, should take 1 second.", computation_amount);
sg_exec_t activity = sg_actor_exec_init(computation_amount);
sg_exec_start(activity);
while (!sg_exec_test(activity)) {
XBT_INFO("Remaining amount of flops: %g (%.0f%%)", sg_exec_get_remaining(activity),
100 * sg_exec_get_remaining_ratio(activity));
sg_actor_sleep_for(0.3);
}
XBT_INFO("Goodbye now!");
}
/* This actor cancels the ongoing execution after a while. */
static void canceller(int argc, char* argv[])
{
double computation_amount = sg_host_get_speed(sg_host_self());
XBT_INFO("Execute %g flops, should take 1 second.", computation_amount);
sg_exec_t activity = sg_actor_exec_async(computation_amount);
sg_actor_sleep_for(0.5);
XBT_INFO("I changed my mind, cancel!");
sg_exec_cancel(activity);
XBT_INFO("Goodbye now!");
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
simgrid_load_platform(argv[1]);
sg_host_t fafard = sg_host_by_name("Fafard");
sg_host_t ginette = sg_host_by_name("Ginette");
sg_host_t boivin = sg_host_by_name("Boivin");
sg_actor_create("wait", fafard, &waiter, 0, NULL);
sg_actor_create("monitor", ginette, &monitor, 0, NULL);
sg_actor_create("cancel", boivin, &canceller, 0, NULL);
simgrid_run();
XBT_INFO("Simulation time %g", simgrid_get_clock());
return 0;
}
Remote execution
You can start executions on remote hosts, or even change the host on which they occur during their execution. This is naturally not very realistic, but it’s something handy to have.
See also simgrid::s4u::Exec::set_host()
.
View examples/cpp/exec-remote/s4u-exec-remote.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void wizard()
{
const sg4::Host* fafard = sg4::Host::by_name("Fafard");
sg4::Host* ginette = sg4::Host::by_name("Ginette");
sg4::Host* boivin = sg4::Host::by_name("Boivin");
XBT_INFO("I'm a wizard! I can run an activity on the Ginette host from the Fafard one! Look!");
sg4::ExecPtr exec = sg4::this_actor::exec_init(48.492e6);
exec->set_host(ginette);
exec->start();
XBT_INFO("It started. Running 48.492Mf takes exactly one second on Ginette (but not on Fafard).");
sg4::this_actor::sleep_for(0.1);
XBT_INFO("Loads in flops/s: Boivin=%.0f; Fafard=%.0f; Ginette=%.0f", boivin->get_load(), fafard->get_load(),
ginette->get_load());
exec->wait();
XBT_INFO("Done!");
XBT_INFO("And now, harder. Start a remote activity on Ginette and move it to Boivin after 0.5 sec");
exec = sg4::this_actor::exec_init(73293500)->set_host(ginette);
exec->start();
sg4::this_actor::sleep_for(0.5);
XBT_INFO("Loads before the move: Boivin=%.0f; Fafard=%.0f; Ginette=%.0f", boivin->get_load(), fafard->get_load(),
ginette->get_load());
exec->set_host(boivin);
sg4::this_actor::sleep_for(0.1);
XBT_INFO("Loads after the move: Boivin=%.0f; Fafard=%.0f; Ginette=%.0f", boivin->get_load(), fafard->get_load(),
ginette->get_load());
exec->wait();
XBT_INFO("Done!");
XBT_INFO("And now, even harder. Start a remote activity on Ginette and turn off the host after 0.5 sec");
exec = sg4::this_actor::exec_init(48.492e6)->set_host(ginette);
exec->start();
sg4::this_actor::sleep_for(0.5);
ginette->turn_off();
try {
exec->wait();
} catch (const simgrid::HostFailureException&) {
XBT_INFO("Execution failed on %s", ginette->get_cname());
}
XBT_INFO("Done!");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("test", e.host_by_name("Fafard"), wizard);
e.run();
return 0;
}
See also simgrid.Exec.host
.
View examples/python/exec-remote/exec-remote.py
# Copyright (c) 2018-2024. The SimGrid Team. All rights reserved.
#
# This program is free software you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
import sys
from simgrid import Actor, Engine, Host, this_actor
class Wizard:
def __call__(self):
fafard = Host.by_name("Fafard")
ginette = Host.by_name("Ginette")
boivin = Host.by_name("Boivin")
this_actor.info("I'm a wizard! I can run a task on the Ginette host from the Fafard one! Look!")
activity = this_actor.exec_init(48.492e6)
activity.host = ginette
activity.start()
this_actor.info("It started. Running 48.492Mf takes exactly one second on Ginette (but not on Fafard).")
this_actor.sleep_for(0.1)
this_actor.info("Loads in flops/s: Boivin={:.0f}; Fafard={:.0f}; Ginette={:.0f}".format(boivin.load,
fafard.load,
ginette.load))
activity.wait()
this_actor.info("Done!")
this_actor.info("And now, harder. Start a remote task on Ginette and move it to Boivin after 0.5 sec")
activity = this_actor.exec_init(73293500)
activity.host = ginette
activity.start()
this_actor.sleep_for(0.5)
this_actor.info(
"Loads before the move: Boivin={:.0f}; Fafard={:.0f}; Ginette={:.0f}".format(
boivin.load,
fafard.load,
ginette.load))
activity.host = boivin
this_actor.sleep_for(0.1)
this_actor.info(
"Loads after the move: Boivin={:.0f}; Fafard={:.0f}; Ginette={:.0f}".format(
boivin.load,
fafard.load,
ginette.load))
activity.wait()
this_actor.info("Done!")
if __name__ == '__main__':
e = Engine(sys.argv)
e.load_platform(sys.argv[1])
Actor.create("test", Host.by_name("Fafard"), Wizard())
e.run()
See also sg_exec_set_host()
.
View examples/c/exec-remote/exec-remote.c
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/exec.h"
#include "simgrid/host.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(exec_remote, "Messages specific for this example");
static void wizard(int argc, char* argv[])
{
const_sg_host_t fafard = sg_host_by_name("Fafard");
sg_host_t ginette = sg_host_by_name("Ginette");
sg_host_t boivin = sg_host_by_name("Boivin");
XBT_INFO("I'm a wizard! I can run a task on the Ginette host from the Fafard one! Look!");
sg_exec_t exec = sg_actor_exec_init(48.492e6);
sg_exec_set_host(exec, ginette);
sg_exec_start(exec);
XBT_INFO("It started. Running 48.492Mf takes exactly one second on Ginette (but not on Fafard).");
sg_actor_sleep_for(0.1);
XBT_INFO("Loads in flops/s: Boivin=%.0f; Fafard=%.0f; Ginette=%.0f", sg_host_get_load(boivin),
sg_host_get_load(fafard), sg_host_get_load(ginette));
sg_exec_wait(exec);
XBT_INFO("Done!");
XBT_INFO("And now, harder. Start a remote task on Ginette and move it to Boivin after 0.5 sec");
exec = sg_actor_exec_init(73293500);
sg_exec_set_host(exec, ginette);
sg_exec_start(exec);
sg_actor_sleep_for(0.5);
XBT_INFO("Loads before the move: Boivin=%.0f; Fafard=%.0f; Ginette=%.0f", sg_host_get_load(boivin),
sg_host_get_load(fafard), sg_host_get_load(ginette));
sg_exec_set_host(exec, boivin);
sg_actor_sleep_for(0.1);
XBT_INFO("Loads after the move: Boivin=%.0f; Fafard=%.0f; Ginette=%.0f", sg_host_get_load(boivin),
sg_host_get_load(fafard), sg_host_get_load(ginette));
sg_exec_wait(exec);
XBT_INFO("Done!");
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
simgrid_load_platform(argv[1]);
sg_actor_create("test", sg_host_by_name("Fafard"), &wizard, 0, NULL);
simgrid_run();
return 0;
}
Parallel executions
These objects are convenient abstractions of parallel
computational kernels that span over several machines, such as a
PDGEM and the other ScaLAPACK routines. Note that this only works
with the “ptask_L07” host model (--cfg=host/model:ptask_L07
).
This example demonstrates several kinds of parallel tasks: regular ones, communication-only (without computation), computation-only (without communication), synchronization-only (neither communication nor computation). It also shows how to reconfigure a task after its start, to change the number of hosts it runs onto. This allows simulating malleable tasks.
See also simgrid::s4u::this_actor::parallel_execute()
.
View examples/cpp/exec-ptask/s4u-exec-ptask.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* Parallel activities are convenient abstractions of parallel computational kernels that span over several machines.
* To create a new one, you have to provide several things:
* - a vector of hosts on which the activity will execute
* - a vector of values, the amount of computation for each of the hosts (in flops)
* - a matrix of values, the amount of communication between each pair of hosts (in bytes)
*
* Each of these operation will be processed at the same relative speed.
* This means that at some point in time, all sub-executions and all sub-communications will be at 20% of completion.
* Also, they will all complete at the exact same time.
*
* This is obviously a simplistic abstraction, but this is very handful in a large amount of situations.
*
* Please note that you must have the LV07 platform model enabled to use such constructs.
*/
#include <simgrid/s4u.hpp>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_ptask, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void runner()
{
/* Retrieve the list of all hosts as an array of hosts */
auto hosts = sg4::Engine::get_instance()->get_all_hosts();
size_t hosts_count = hosts.size();
std::vector<double> computation_amounts;
std::vector<double> communication_amounts;
/* ------[ test 1 ]----------------- */
XBT_INFO("First, build a classical parallel activity, with 1 Gflop to execute on each node, "
"and 10MB to exchange between each pair");
computation_amounts.assign(hosts_count, 1e9 /*1Gflop*/);
communication_amounts.assign(hosts_count * hosts_count, 0);
for (size_t i = 0; i < hosts_count; i++)
for (size_t j = i + 1; j < hosts_count; j++)
communication_amounts[i * hosts_count + j] = 1e7; // 10 MB
sg4::this_actor::parallel_execute(hosts, computation_amounts, communication_amounts);
/* ------[ test 2 ]----------------- */
XBT_INFO("We can do the same with a timeout of 10 seconds enabled.");
computation_amounts.assign(hosts_count, 1e9 /*1Gflop*/);
communication_amounts.assign(hosts_count * hosts_count, 0);
for (size_t i = 0; i < hosts_count; i++)
for (size_t j = i + 1; j < hosts_count; j++)
communication_amounts[i * hosts_count + j] = 1e7; // 10 MB
sg4::ExecPtr activity = sg4::this_actor::exec_init(hosts, computation_amounts, communication_amounts);
try {
activity->wait_for(10.0 /* timeout (in seconds)*/);
xbt_die("Woops, this did not timeout as expected... Please report that bug.");
} catch (const simgrid::TimeoutException&) {
XBT_INFO("Caught the expected timeout exception.");
activity->cancel();
}
/* ------[ test 3 ]----------------- */
XBT_INFO("Then, build a parallel activity involving only computations (of different amounts) and no communication");
computation_amounts = {3e8, 6e8, 1e9}; // 300Mflop, 600Mflop, 1Gflop
communication_amounts.clear(); // no comm
sg4::this_actor::parallel_execute(hosts, computation_amounts, communication_amounts);
/* ------[ test 4 ]----------------- */
XBT_INFO("Then, build a parallel activity with no computation nor communication (synchro only)");
computation_amounts.clear();
communication_amounts.clear();
sg4::this_actor::parallel_execute(hosts, computation_amounts, communication_amounts);
/* ------[ test 5 ]----------------- */
XBT_INFO("Then, Monitor the execution of a parallel activity");
computation_amounts.assign(hosts_count, 1e6 /*1Mflop*/);
communication_amounts = {0, 1e6, 0, 0, 0, 1e6, 1e6, 0, 0};
activity = sg4::this_actor::exec_init(hosts, computation_amounts, communication_amounts);
activity->start();
while (not activity->test()) {
XBT_INFO("Remaining flop ratio: %.0f%%", 100 * activity->get_remaining_ratio());
sg4::this_actor::sleep_for(5);
}
activity->wait();
/* ------[ test 6 ]----------------- */
XBT_INFO("Finally, simulate a malleable task (a parallel execution that gets reconfigured after its start).");
XBT_INFO(" - Start a regular parallel execution, with both comm and computation");
computation_amounts.assign(hosts_count, 1e6 /*1Mflop*/);
communication_amounts = {0, 1e6, 0, 0, 1e6, 0, 1e6, 0, 0};
activity = sg4::this_actor::exec_init(hosts, computation_amounts, communication_amounts);
activity->start();
sg4::this_actor::sleep_for(10);
double remaining_ratio = activity->get_remaining_ratio();
XBT_INFO(" - After 10 seconds, %.2f%% remains to be done. Change it from 3 hosts to 2 hosts only.",
remaining_ratio * 100);
XBT_INFO(" Let's first suspend the task.");
activity->suspend();
XBT_INFO(" - Now, simulate the reconfiguration (modeled as a comm from the removed host to the remaining ones).");
std::vector<double> rescheduling_comp{0, 0, 0};
std::vector<double> rescheduling_comm{0, 0, 0, 0, 0, 0, 25000, 25000, 0};
sg4::this_actor::parallel_execute(hosts, rescheduling_comp, rescheduling_comm);
XBT_INFO(" - Now, let's cancel the old task and create a new task with modified comm and computation vectors:");
XBT_INFO(" What was already done is removed, and the load of the removed host is shared between remaining ones.");
for (int i = 0; i < 2; i++) {
// remove what we've done so far, for both comm and compute load
computation_amounts[i] *= remaining_ratio;
communication_amounts[i] *= remaining_ratio;
// The work from 1 must be shared between 2 remaining ones. 1/2=50% of extra work for each
computation_amounts[i] *= 1.5;
communication_amounts[i] *= 1.5;
}
hosts.resize(2);
computation_amounts.resize(2);
double remaining_comm = communication_amounts[1];
communication_amounts = {0, remaining_comm, remaining_comm, 0}; // Resizing a linearized matrix is hairly
activity->cancel();
activity = sg4::this_actor::exec_init(hosts, computation_amounts, communication_amounts);
XBT_INFO(" - Done, let's wait for the task completion");
activity->wait();
XBT_INFO("Goodbye now!");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc == 2, "Usage: %s <platform file>", argv[0]);
e.load_platform(argv[1]);
sg4::Actor::create("test", e.host_by_name("MyHost1"), runner);
e.run();
XBT_INFO("Simulation done.");
return 0;
}
See also simgrid.this_actor.parallel_execute()
View examples/python/exec-ptask/exec-ptask.py
# Copyright (c) 2018-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
# This script does exactly the same thing as file s4u-exec-ptask.cpp
import sys
from simgrid import Actor, Engine, Host, this_actor, TimeoutException
def runner():
hosts = Engine.instance.all_hosts
hosts_count = len(hosts)
# Test 1
this_actor.info("First, build a classical parallel activity, with 1 Gflop to execute on each node, "
"and 10MB to exchange between each pair")
computation_amounts = [1e9]*hosts_count
communication_amounts = [0]*hosts_count*hosts_count
for i in range(hosts_count):
for j in range(i+1, hosts_count):
communication_amounts[i * hosts_count + j] = 1e7
this_actor.parallel_execute(hosts, computation_amounts, communication_amounts)
# Test 2
this_actor.info("We can do the same with a timeout of 10 seconds enabled.")
activity = this_actor.exec_init(hosts, computation_amounts, communication_amounts)
try:
activity.wait_for(10.0)
sys.exit("Woops, this did not timeout as expected... Please report that bug.")
except TimeoutException:
this_actor.info("Caught the expected timeout exception.")
activity.cancel()
# Test 3
this_actor.info("Then, build a parallel activity involving only computations (of different amounts) and no communication")
computation_amounts = [3e8, 6e8, 1e9]
communication_amounts = []
this_actor.parallel_execute(hosts, computation_amounts, communication_amounts)
# Test 4
this_actor.info("Then, build a parallel activity with no computation nor communication (synchro only)")
computation_amounts = []
this_actor.parallel_execute(hosts, computation_amounts, communication_amounts)
# Test 5
this_actor.info("Then, Monitor the execution of a parallel activity")
computation_amounts = [1e6]*hosts_count
communication_amounts = [0, 1e6, 0, 0, 0, 1e6, 1e6, 0, 0]
activity = this_actor.exec_init(hosts, computation_amounts, communication_amounts)
activity.start()
while not activity.test():
ratio = activity.remaining_ratio * 100
this_actor.info(f"Remaining flop ratio: {ratio:.0f}%")
this_actor.sleep_for(5)
activity.wait()
# Test 6
this_actor.info("Finally, simulate a malleable task (a parallel execution that gets reconfigured after its start).")
this_actor.info(" - Start a regular parallel execution, with both comm and computation")
computation_amounts = [1e6]*hosts_count
communication_amounts = [0, 1e6, 0, 0, 1e6, 0, 1e6, 0, 0]
activity = this_actor.exec_init(hosts, computation_amounts, communication_amounts)
activity.start()
this_actor.sleep_for(10)
remaining_ratio = activity.remaining_ratio
this_actor.info(f" - After 10 seconds, {remaining_ratio*100:.2f}% remains to be done. Change it from 3 hosts to 2 hosts only.")
this_actor.info(" Let's first suspend the task.")
activity.suspend()
this_actor.info(" - Now, simulate the reconfiguration (modeled as a comm from the removed host to the remaining ones).")
rescheduling_comp = [0, 0, 0]
rescheduling_comm = [0, 0, 0, 0, 0, 0, 25000, 25000, 0]
this_actor.parallel_execute(hosts, rescheduling_comp, rescheduling_comm)
this_actor.info(" - Now, let's cancel the old task and create a new task with modified comm and computation vectors:")
this_actor.info(" What was already done is removed, and the load of the removed host is shared between remaining ones.")
for i in range(2):
# remove what we've done so far, for both comm and compute load
computation_amounts[i] *= remaining_ratio
communication_amounts[i] *= remaining_ratio
# The work from 1 must be shared between 2 remaining ones. 1/2=50% of extra work for each
computation_amounts[i] *= 1.5
communication_amounts[i] *= 1.5
hosts = hosts[:2]
computation_amounts = computation_amounts[:2]
remaining_comm = communication_amounts[1]
communication_amounts = [0, remaining_comm, remaining_comm, 0]
activity.cancel()
activity = this_actor.exec_init(hosts, computation_amounts, communication_amounts)
this_actor.info(" - Done, let's wait for the task completion")
activity.wait()
this_actor.info("Goodbye now!")
if __name__ == "__main__":
if len(sys.argv) != 2:
sys.exit(f"Syntax: {sys.argv[0]} <platform_file>")
platform = sys.argv[1]
engine = Engine.instance
Engine.set_config("host/model:ptask_L07") # /!\ this is required for running ptasks
engine.load_platform(platform)
Actor.create("foo", engine.host_by_name("MyHost1"), runner)
engine.run()
Ptasks play well with the host energy plugin, as shown in this example. There is not much new compared to the above ptask example or the examples about energy. It just works.
View examples/cpp/energy-exec-ptask/s4u-energy-exec-ptask.cpp
Download s4u-energy-exec-ptask.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include "simgrid/plugins/energy.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void runner()
{
sg4::Host* host1 = sg4::Host::by_name("MyHost1");
sg4::Host* host2 = sg4::Host::by_name("MyHost2");
std::vector<sg4::Host*> hosts{host1, host2};
double old_energy_host1 = sg_host_get_consumed_energy(host1);
double old_energy_host2 = sg_host_get_consumed_energy(host2);
XBT_INFO("[%s] Energetic profile: %s", host1->get_cname(), host1->get_property("wattage_per_state"));
XBT_INFO("[%s] Initial peak speed=%.0E flop/s; Total energy dissipated =%.0E J", host1->get_cname(), host1->get_speed(),
old_energy_host1);
XBT_INFO("[%s] Energetic profile: %s", host2->get_cname(), host2->get_property("wattage_per_state"));
XBT_INFO("[%s] Initial peak speed=%.0E flop/s; Total energy dissipated =%.0E J", host2->get_cname(), host2->get_speed(),
old_energy_host2);
double start = sg4::Engine::get_clock();
XBT_INFO("Sleep for 10 seconds");
sg4::this_actor::sleep_for(10);
double new_energy_host1 = sg_host_get_consumed_energy(host1);
double new_energy_host2 = sg_host_get_consumed_energy(host2);
XBT_INFO("Done sleeping (duration: %.2f s).\n"
"[%s] Current peak speed=%.0E; Energy dissipated during this step=%.2f J; Total energy dissipated=%.2f J\n"
"[%s] Current peak speed=%.0E; Energy dissipated during this step=%.2f J; Total energy dissipated=%.2f J\n",
sg4::Engine::get_clock() - start, host1->get_cname(), host1->get_speed(),
(new_energy_host1 - old_energy_host1), sg_host_get_consumed_energy(host1), host2->get_cname(),
host2->get_speed(), (new_energy_host2 - old_energy_host2), sg_host_get_consumed_energy(host2));
old_energy_host1 = new_energy_host1;
old_energy_host2 = new_energy_host2;
// ========= Execute something =========
start = sg4::Engine::get_clock();
double flopAmount = 1E9;
std::vector<double> cpu_amounts{flopAmount, flopAmount};
std::vector<double> com_amounts{0, 0, 0, 0};
XBT_INFO("Run a task of %.0E flops on two hosts", flopAmount);
sg4::this_actor::parallel_execute(hosts, cpu_amounts, com_amounts);
new_energy_host1 = sg_host_get_consumed_energy(host1);
new_energy_host2 = sg_host_get_consumed_energy(host2);
XBT_INFO(
"Task done (duration: %.2f s).\n"
"[%s] Current peak speed=%.0E flop/s; Energy dissipated during this step=%.2f J; Total energy dissipated=%.0f J\n"
"[%s] Current peak speed=%.0E flop/s; Energy dissipated during this step=%.2f J; Total energy dissipated=%.0f "
"J\n",
sg4::Engine::get_clock() - start, host1->get_cname(), host1->get_speed(), (new_energy_host1 - old_energy_host1),
sg_host_get_consumed_energy(host1), host2->get_cname(), host2->get_speed(), (new_energy_host2 - old_energy_host2),
sg_host_get_consumed_energy(host2));
old_energy_host1 = new_energy_host1;
old_energy_host2 = new_energy_host2;
// ========= Change power peak =========
int pstate = 2;
host1->set_pstate(pstate);
host2->set_pstate(pstate);
XBT_INFO("========= Requesting pstate %d for both hosts (speed should be of %.0E flop/s and is of %.0E flop/s)", pstate,
host1->get_pstate_speed(pstate), host1->get_speed());
// ========= Run another ptask =========
start = sg4::Engine::get_clock();
std::vector<double> cpu_amounts2{flopAmount, flopAmount};
std::vector<double> com_amounts2{0, 0, 0, 0};
XBT_INFO("Run a task of %.0E flops on %s and %.0E flops on %s.", flopAmount, host1->get_cname(), flopAmount, host2->get_cname());
sg4::this_actor::parallel_execute(hosts, cpu_amounts2, com_amounts2);
new_energy_host1 = sg_host_get_consumed_energy(host1);
new_energy_host2 = sg_host_get_consumed_energy(host2);
XBT_INFO(
"Task done (duration: %.2f s).\n"
"[%s] Current peak speed=%.0E flop/s; Energy dissipated during this step=%.2f J; Total energy dissipated=%.0f J\n"
"[%s] Current peak speed=%.0E flop/s; Energy dissipated during this step=%.2f J; Total energy dissipated=%.0f "
"J\n",
sg4::Engine::get_clock() - start, host1->get_cname(), host1->get_speed(), (new_energy_host1 - old_energy_host1),
sg_host_get_consumed_energy(host1), host2->get_cname(), host2->get_speed(), (new_energy_host2 - old_energy_host2),
sg_host_get_consumed_energy(host2));
old_energy_host1 = new_energy_host1;
old_energy_host2 = new_energy_host2;
// ========= A new ptask with computation and communication =========
start = sg4::Engine::get_clock();
double comAmount = 1E7;
std::vector<double> cpu_amounts3{flopAmount, flopAmount};
std::vector<double> com_amounts3{0, comAmount, comAmount, 0};
XBT_INFO("Run a task with computation and communication on two hosts.");
sg4::this_actor::parallel_execute(hosts, cpu_amounts3, com_amounts3);
new_energy_host1 = sg_host_get_consumed_energy(host1);
new_energy_host2 = sg_host_get_consumed_energy(host2);
XBT_INFO(
"Task done (duration: %.2f s).\n"
"[%s] Current peak speed=%.0E flop/s; Energy dissipated during this step=%.2f J; Total energy dissipated=%.0f J\n"
"[%s] Current peak speed=%.0E flop/s; Energy dissipated during this step=%.2f J; Total energy dissipated=%.0f "
"J\n",
sg4::Engine::get_clock() - start, host1->get_cname(), host1->get_speed(), (new_energy_host1 - old_energy_host1),
sg_host_get_consumed_energy(host1), host2->get_cname(), host2->get_speed(), (new_energy_host2 - old_energy_host2),
sg_host_get_consumed_energy(host2));
old_energy_host1 = new_energy_host1;
old_energy_host2 = new_energy_host2;
// ========= A new ptask with communication only =========
start = sg4::Engine::get_clock();
std::vector<double> cpu_amounts4{0, 0};
std::vector<double> com_amounts4{0, comAmount, comAmount, 0};
XBT_INFO("Run a task with only communication on two hosts.");
sg4::this_actor::parallel_execute(hosts, cpu_amounts4, com_amounts4);
new_energy_host1 = sg_host_get_consumed_energy(host1);
new_energy_host2 = sg_host_get_consumed_energy(host2);
XBT_INFO(
"Task done (duration: %.2f s).\n"
"[%s] Current peak speed=%.0E flop/s; Energy dissipated during this step=%.2f J; Total energy dissipated=%.0f J\n"
"[%s] Current peak speed=%.0E flop/s; Energy dissipated during this step=%.2f J; Total energy dissipated=%.0f "
"J\n",
sg4::Engine::get_clock() - start, host1->get_cname(), host1->get_speed(), (new_energy_host1 - old_energy_host1),
sg_host_get_consumed_energy(host1), host2->get_cname(), host2->get_speed(), (new_energy_host2 - old_energy_host2),
sg_host_get_consumed_energy(host2));
old_energy_host1 = new_energy_host1;
old_energy_host2 = new_energy_host2;
// ========= A new ptask with computation and a timeout =========
start = sg4::Engine::get_clock();
XBT_INFO("Run a task with computation on two hosts and a timeout of 20s.");
try {
std::vector<double> cpu_amounts5{flopAmount, flopAmount};
std::vector<double> com_amounts5{0, 0, 0, 0};
sg4::this_actor::exec_init(hosts, cpu_amounts5, com_amounts5)->wait_for(20);
} catch (const simgrid::TimeoutException &){
XBT_INFO("Finished WITH timeout");
}
new_energy_host1 = sg_host_get_consumed_energy(host1);
new_energy_host2 = sg_host_get_consumed_energy(host2);
XBT_INFO(
"Task ended (duration: %.2f s).\n"
"[%s] Current peak speed=%.0E flop/s; Energy dissipated during this step=%.2f J; Total energy dissipated=%.0f J\n"
"[%s] Current peak speed=%.0E flop/s; Energy dissipated during this step=%.2f J; Total energy dissipated=%.0f "
"J\n",
sg4::Engine::get_clock() - start, host1->get_cname(), host1->get_speed(), (new_energy_host1 - old_energy_host1),
sg_host_get_consumed_energy(host1), host2->get_cname(), host2->get_speed(), (new_energy_host2 - old_energy_host2),
sg_host_get_consumed_energy(host2));
XBT_INFO("Now is time to quit!");
}
int main(int argc, char* argv[])
{
sg_host_energy_plugin_init();
sg4::Engine e(&argc, argv);
sg4::Engine::set_config("host/model:ptask_L07");
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s ../platforms/energy_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
sg4::Actor::create("energy_ptask_test", e.host_by_name("MyHost1"), runner);
e.run();
XBT_INFO("End of simulation.");
return 0;
}
View examples/c/energy-exec-ptask/energy-exec-ptask.c
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/exec.h"
#include "simgrid/host.h"
#include "simgrid/plugins/energy.h"
#include "xbt/config.h"
#include "xbt/log.h"
#include "xbt/sysdep.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(energy_exec_ptask, "Messages specific for this example");
static void runner(int argc, char* argv[])
{
/* Retrieve the list of all hosts as an array of hosts */
int host_count = (int)sg_host_count();
sg_host_t* hosts = sg_host_list();
XBT_INFO("First, build a classical parallel task, with 1 Gflop to execute on each node, "
"and 10MB to exchange between each pair");
double* computation_amounts = xbt_new0(double, host_count);
double* communication_amounts = xbt_new0(double, host_count * host_count);
for (int i = 0; i < host_count; i++)
computation_amounts[i] = 1e9; // 1 Gflop
for (int i = 0; i < host_count; i++)
for (int j = i + 1; j < host_count; j++)
communication_amounts[i * host_count + j] = 1e7; // 10 MB
sg_actor_parallel_execute(host_count, hosts, computation_amounts, communication_amounts);
xbt_free(communication_amounts);
xbt_free(computation_amounts);
XBT_INFO("We can do the same with a timeout of one second enabled.");
computation_amounts = xbt_new0(double, host_count);
communication_amounts = xbt_new0(double, host_count * host_count);
for (int i = 0; i < host_count; i++)
computation_amounts[i] = 1e9; // 1 Gflop
for (int i = 0; i < host_count; i++)
for (int j = i + 1; j < host_count; j++)
communication_amounts[i * host_count + j] = 1e7; // 10 MB
sg_exec_t exec = sg_actor_parallel_exec_init(host_count, hosts, computation_amounts, communication_amounts);
if (sg_exec_wait_for(exec, 1 /* timeout (in seconds)*/) == SG_ERROR_TIMEOUT)
sg_exec_cancel(exec);
xbt_free(communication_amounts);
xbt_free(computation_amounts);
XBT_INFO("Then, build a parallel task involving only computations and no communication (1 Gflop per node)");
computation_amounts = xbt_new0(double, host_count);
for (int i = 0; i < host_count; i++)
computation_amounts[i] = 1e9; // 1 Gflop
sg_actor_parallel_execute(host_count, hosts, computation_amounts, NULL);
xbt_free(computation_amounts);
XBT_INFO("Then, build a parallel task with no computation nor communication (synchro only)");
computation_amounts = xbt_new0(double, host_count);
communication_amounts = xbt_new0(double, host_count * host_count);
sg_actor_parallel_execute(host_count, hosts, computation_amounts, communication_amounts);
xbt_free(communication_amounts);
xbt_free(computation_amounts);
XBT_INFO("Finally, trick the ptask to do a 'remote execution', on host %s", sg_host_get_name(hosts[1]));
computation_amounts = xbt_new0(double, 1);
computation_amounts[0] = 1e9; // 1 Gflop
sg_host_t remote[1];
remote[0] = hosts[1];
sg_actor_parallel_execute(1, remote, computation_amounts, NULL /* no comm */);
xbt_free(computation_amounts);
XBT_INFO("Goodbye now!");
xbt_free(hosts);
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
sg_cfg_set_string("host/model", "ptask_L07");
xbt_assert(argc <= 3, "1Usage: %s <platform file> [--energy]", argv[0]);
xbt_assert(argc >= 2, "2Usage: %s <platform file> [--energy]", argv[0]);
if (argc == 3 && argv[2][2] == 'e')
sg_host_energy_plugin_init();
simgrid_load_platform(argv[1]);
/* Pick the first host from the platform file */
sg_host_t* all_hosts = sg_host_list();
sg_host_t first_host = all_hosts[0];
xbt_free(all_hosts);
sg_actor_create("test", first_host, &runner, 0, NULL);
simgrid_run();
XBT_INFO("Simulation done.");
return 0;
}
Dealing with host failures
This examples shows how to survive to host failure exceptions that occur when an host is turned off. The actors do not get notified when the host
on which they run is turned off: they are just terminated in this case, and their on_exit()
callback gets executed. For remote executions on
failing hosts however, any blocking operation such as exec
or wait
will raise an exception that you can catch and react to. See also
Modeling churn (e.g., in P2P),
this example on how to attach a state profile to hosts, and
that example on how to react to network failures.
View examples/cpp/exec-failure/s4u-exec-failure.cpp
/* Copyright (c) 2021-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This examples shows how to survive to host failure exceptions that occur when an host is turned off.
*
* The actors do not get notified when the host on which they run is turned off: they are just terminated
* in this case, and their ``on_exit()`` callback gets executed.
*
* For remote executions on failing hosts however, any blocking operation such as ``exec`` or ``wait`` will
* raise an exception that you can catch and react to, as illustrated in this example.
*/
#include <simgrid/s4u.hpp>
#include "simgrid/kernel/ProfileBuilder.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_exec_failure, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void dispatcher(std::vector<sg4::Host*> const& hosts)
{
std::vector<sg4::ExecPtr> pending_execs;
for (auto* host: hosts) {
XBT_INFO("Initiating asynchronous exec on %s", host->get_cname());
// Computing 20 flops on an host which speed is 1f takes 20 seconds (when it does not fail)
auto exec = sg4::this_actor::exec_init(20)->set_host(host);
pending_execs.push_back(exec);
exec->start();
}
XBT_INFO("---------------------------------");
XBT_INFO("Wait on the first exec, which host is turned off at t=10 by the another actor.");
try {
pending_execs[0]->wait();
xbt_assert("This wait was not supposed to succeed.");
} catch (const simgrid::HostFailureException&) {
XBT_INFO("Dispatcher has experienced a host failure exception, so it knows that something went wrong.");
}
XBT_INFO("State of each exec:");
for (auto const& exec : pending_execs)
XBT_INFO(" Exec on %s has state: %s", exec->get_host()->get_cname(), exec->get_state_str());
XBT_INFO("---------------------------------");
XBT_INFO("Wait on the second exec, which host is turned off at t=12 by the state profile.");
try {
pending_execs[1]->wait();
xbt_assert("This wait was not supposed to succeed.");
} catch (const simgrid::HostFailureException&) {
XBT_INFO("Dispatcher has experienced a host failure exception, so it knows that something went wrong.");
}
XBT_INFO("State of each exec:");
for (auto const& exec : pending_execs)
XBT_INFO(" Exec on %s has state: %s", exec->get_host()->get_cname(), exec->get_state_str());
XBT_INFO("---------------------------------");
XBT_INFO("Wait on the third exec, which should succeed.");
try {
pending_execs[2]->wait();
XBT_INFO("No exception occured.");
} catch (const simgrid::HostFailureException&) {
xbt_assert("This wait was not supposed to fail.");
}
XBT_INFO("State of each exec:");
for (auto const& exec : pending_execs)
XBT_INFO(" Exec on %s has state: %s", exec->get_host()->get_cname(), exec->get_state_str());
}
static void host_killer(sg4::Host* to_kill)
{
sg4::this_actor::sleep_for(10.0);
XBT_INFO("HostKiller turns off the host '%s'.", to_kill->get_cname());
to_kill->turn_off();
}
int main(int argc, char** argv)
{
sg4::Engine engine(&argc, argv);
auto* zone = sg4::create_full_zone("world");
std::vector<sg4::Host*> hosts;
for (const auto* name : {"Host1", "Host2", "Host3"}) {
auto* host = zone->create_host(name, "1f");
hosts.push_back(host);
}
/* Attaching a state profile (ie a list of events changing the on/off state of the resource) to host3.
* The syntax of the profile (second parameter) is a list of: "date state\n"
* The R"( )" thing is the C++ way of writing multiline strings, including literals \n.
* You'd have the same behavior by using "12 0\n20 1\n" instead.
* So here, the link is turned off at t=12 and back on at t=20.
* The last parameter is the period of that profile, meaning that it loops after 30 seconds.
*/
hosts[1]->set_state_profile(simgrid::kernel::profile::ProfileBuilder::from_string("profile name", R"(
12 0
20 1
)", 30));
zone->seal();
sg4::Actor::create("Dispatcher", hosts[2], dispatcher, hosts);
sg4::Actor::create("HostKiller", hosts[2], host_killer, hosts[0]);
engine.run();
return 0;
}
DVFS and pstates
This example shows how to define a set of pstates in the XML. The current pstate of a host can then be accessed and changed from the program.
See also simgrid::s4u::Host::get_pstate_speed()
and simgrid::s4u::Host::set_pstate()
.
View examples/cpp/exec-dvfs/s4u-exec-dvfs.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(test, "Pstate properties test");
namespace sg4 = simgrid::s4u;
static int dvfs()
{
double workload = 100E6;
sg4::Host* host = sg4::this_actor::get_host();
unsigned long nb = host->get_pstate_count();
XBT_INFO("Count of Processor states=%lu", nb);
XBT_INFO("Current power peak=%f", host->get_speed());
// Run a Computation
sg4::this_actor::execute(workload);
double exec_time = sg4::Engine::get_clock();
XBT_INFO("Computation1 duration: %.2f", exec_time);
// Change power peak
unsigned long new_pstate = 2;
XBT_INFO("Changing power peak value to %f (at index %lu)", host->get_pstate_speed(new_pstate), new_pstate);
host->set_pstate(new_pstate);
XBT_INFO("Current power peak=%f", host->get_speed());
// Run a second Computation
sg4::this_actor::execute(workload);
exec_time = sg4::Engine::get_clock() - exec_time;
XBT_INFO("Computation2 duration: %.2f", exec_time);
// Verify that the default pstate is set to 0
host = sg4::Host::by_name_or_null("MyHost2");
XBT_INFO("Count of Processor states=%lu", host->get_pstate_count());
XBT_INFO("Current power peak=%f", host->get_speed());
return 0;
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s ../platforms/energy_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
sg4::Actor::create("dvfs_test", e.host_by_name("MyHost1"), dvfs);
sg4::Actor::create("dvfs_test", e.host_by_name("MyHost2"), dvfs);
e.run();
XBT_INFO("Total simulation time: %e", sg4::Engine::get_clock());
return 0;
}
See also sg_host_get_pstate_speed()
and sg_host_set_pstate()
.
View examples/c/exec-dvfs/exec-dvfs.c
/* Copyright (c) 2007-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "xbt/asserts.h"
#include "xbt/config.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(test, "Pstate properties test");
static void dvfs(int argc, char* argv[])
{
sg_host_t host = sg_host_self();
unsigned long nb = sg_host_get_nb_pstates(host);
XBT_INFO("Count of Processor states=%lu", nb);
double current_peak = sg_host_get_speed(host);
XBT_INFO("Current power peak=%f", current_peak);
sg_actor_execute(100E6);
double task_time = simgrid_get_clock();
XBT_INFO("Task1 simulation time: %e", task_time);
// Change power peak
unsigned long new_pstate = 2;
xbt_assert(new_pstate < nb, "Cannot set the host %s at pstate %lu because it only provides %lu pstates.",
sg_host_get_name(host), new_pstate, nb);
double peak_at = sg_host_get_pstate_speed(host, new_pstate);
XBT_INFO("Changing power peak value to %f (at index %lu)", peak_at, new_pstate);
sg_host_set_pstate(host, new_pstate);
current_peak = sg_host_get_speed(host);
XBT_INFO("Current power peak=%f", current_peak);
sg_actor_execute(100E6);
task_time = simgrid_get_clock() - task_time;
XBT_INFO("Task2 simulation time: %e", task_time);
// Verify the default pstate is set to 0
host = sg_host_by_name("MyHost2");
unsigned long nb2 = sg_host_get_nb_pstates(host);
XBT_INFO("Count of Processor states=%lu", nb2);
double current_peak2 = sg_host_get_speed(host);
XBT_INFO("Current power peak=%f", current_peak2);
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s platform.xml\n", argv[0], argv[0]);
simgrid_load_platform(argv[1]);
sg_actor_create("dvfs_test", sg_host_by_name("MyHost1"), &dvfs, 0, NULL);
sg_actor_create("dvfs_test", sg_host_by_name("MyHost2"), &dvfs, 0, NULL);
simgrid_run();
XBT_INFO("Total simulation time: %e", simgrid_get_clock());
return 0;
}
See also simgrid.Host.pstate_speed()
and simgrid.Host.pstate
.
View examples/python/exec-dvfs/exec-dvfs.py
# Copyright (c) 2007-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
Usage: exec-dvfs.py platform_file [other parameters]
"""
import sys
from simgrid import Actor, Engine, Host, this_actor
class Dvfs:
def __call__(self):
workload = 100E6
host = this_actor.get_host()
nb = host.pstate_count
this_actor.info("Count of Processor states={:d}".format(nb))
this_actor.info("Current power peak={:f}".format(host.speed))
# Run a task
this_actor.execute(workload)
task_time = Engine.clock
this_actor.info("Task1 duration: {:.2f}".format(task_time))
# Change power peak
new_pstate = 2
this_actor.info("Changing power peak value to {:f} (at index {:d})".format(host.pstate_speed(new_pstate),
new_pstate))
host.pstate = new_pstate
this_actor.info("Changed power peak={:f}".format(host.speed))
# Run a second task
this_actor.execute(workload)
task_time = Engine.clock - task_time
this_actor.info("Task2 duration: {:.2f}".format(task_time))
# Verify that the default pstate is set to 0
host2 = Host.by_name("MyHost2")
this_actor.info("Count of Processor states={:d}".format(host2.pstate_count))
this_actor.info("Final power peak={:f}".format(host2.speed))
if __name__ == '__main__':
e = Engine(sys.argv)
if len(sys.argv) < 2:
raise AssertionError("Usage: exec-dvfs.py platform_file [other parameters] (got {:d} params)"
.format(len(sys.argv)))
e.load_platform(sys.argv[1])
Actor.create("dvfs_test", Host.by_name("MyHost1"), Dvfs())
Actor.create("dvfs_test", Host.by_name("MyHost2"), Dvfs())
e.run()
The important parts are in the <host> tag. The pstate
attribute is the initial pstate while the speed
attribute must
be a comma-separated list of values: the speed at each pstate. This platform file also describes the wattage_per_state
and
wattage_off
properties, that are used by the Host Energy plugin.
View examples/platforms/energy_platform.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
<platform version="4.1">
<zone id="AS0" routing="Full">
<!-- Multiple pstate processor capacities can be defined as a list of powers specified for a given host -->
<!-- Attribute 'pstate' specifies the initially selected pstate (here, the lowest pstate corresponds to the highest
processor speed) -->
<host core="4" id="MyHost1" pstate="0" speed="100.0Mf,50.0Mf,20.0Mf">
<!-- List of Idle:Epsilon:AllCores (in Watts) corresponding to the speed consumed when the processor is idle,
when all cores have a tiny epsilon load, and when all cores are fully loaded -->
<!-- The list must contain one energetic profile for each previously defined pstate-->
<prop id="wattage_per_state" value="100.0:93.33333333333333:200.0, 93.0:90.0:170.0, 90.0:90.0:150.0" />
<prop id="wattage_off" value="10" />
</host>
<host core="1" id="MyHost2" pstate="0" speed="100.0Mf,50.0Mf,20.0Mf">
<!-- This host is mono-core and its consumption is either idle or full load (Epsilon=AllCores) -->
<prop id="wattage_per_state" value="100.0:200.0:200.0, 93.0:170.0:170.0, 90.0:150.0:150.0" />
<prop id="wattage_off" value="10" />
</host>
<host core="1" id="MyHost3" pstate="0" speed="100.0Mf,50.0Mf,20.0Mf">
<!-- This host is mono-core and its consumption is either idle or full load (Epsilon=AllCores) -->
<prop id="wattage_per_state" value="100.0:200.0:200.0, 93.0:170.0:170.0, 90.0:150.0:150.0" />
<prop id="wattage_off" value="10" />
</host>
<link bandwidth="100kBps" id="bus" latency="0" sharing_policy="SHARED">
<!-- REALISTIC VALUES <prop id="wattage_range" value="10.3581:10.7479" /> -->
<!-- IREALISTIC VALUES FOR THE TEST --> <prop id="wattage_range" value="1:3" />
</link>
<route dst="MyHost2" src="MyHost1">
<link_ctn id="bus" />
</route>
<route dst="MyHost3" src="MyHost1">
<link_ctn id="bus" />
</route>
<route dst="MyHost3" src="MyHost2">
<link_ctn id="bus" />
</route>
</zone>
</platform>
I/O on Disks and Files
SimGrid provides two levels of abstraction to interact with the simulated disks. At the simplest level, you simply create read and write actions on the disk resources.
Access to raw disk devices
This example illustrates how to simply read and write data on a simulated disk resource.
View examples/cpp/io-disk-raw/s4u-io-disk-raw.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <string>
#include <unordered_map>
XBT_LOG_NEW_DEFAULT_CATEGORY(disk_test, "Messages specific for this simulation");
namespace sg4 = simgrid::s4u;
static void host()
{
/* -Add an extra disk in a programmatic way */
sg4::Host::current()->create_disk("Disk3", /*read bandwidth*/ 9.6e7, /*write bandwidth*/ 6.4e7)->seal();
/* - Display information on the disks mounted by the current host */
XBT_INFO("*** Storage info on %s ***", sg4::Host::current()->get_cname());
/* - Retrieve all disks from current host */
std::vector<sg4::Disk*> const& disk_list = sg4::Host::current()->get_disks();
/* - For each disk mounted on host, display disk name and mount point */
for (auto const& disk : disk_list)
XBT_INFO("Disk name: %s (read: %.0f B/s -- write: %.0f B/s", disk->get_cname(), disk->get_read_bandwidth(),
disk->get_write_bandwidth());
/* - Write 400,000 bytes on Disk1 */
sg4::Disk* disk = disk_list.front();
sg_size_t write = disk->write(400000);
XBT_INFO("Wrote %llu bytes on '%s'", write, disk->get_cname());
/* - Now read 200,000 bytes */
sg_size_t read = disk->read(200000);
XBT_INFO("Read %llu bytes on '%s'", read, disk->get_cname());
/* - Write 800,000 bytes on Disk3 */
const sg4::Disk* disk3 = disk_list.back();
sg_size_t write_on_disk3 = disk3->write(800000);
XBT_INFO("Wrote %llu bytes on '%s'", write_on_disk3, disk3->get_cname());
/* - Attach some user data to disk1 */
XBT_INFO("*** Get/set data for storage element: Disk1 ***");
auto data = disk->get_unique_data<std::string>();
XBT_INFO("Get storage data: '%s'", data ? data->c_str() : "No user data");
disk->set_data(new std::string("Some user data"));
data = disk->get_unique_data<std::string>();
XBT_INFO("Set and get data: '%s'", data->c_str());
}
int main(int argc, char** argv)
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
/* - Display Host properties */
for (auto const* h : e.get_all_hosts()) {
XBT_INFO("*** %s properties ****", h->get_cname());
for (auto const& [key, value] : *h->get_properties())
XBT_INFO(" %s -> %s", key.c_str(), value.c_str());
}
sg4::Actor::create("", e.host_by_name("bob"), host);
e.run();
XBT_INFO("Simulated time: %g", sg4::Engine::get_clock());
return 0;
}
View examples/c/io-disk-raw/io-disk-raw.c
/* Copyright (c) 2006-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/disk.h"
#include "simgrid/engine.h"
#include "simgrid/forward.h"
#include "simgrid/host.h"
#include "xbt/dict.h"
#include "xbt/log.h"
#include "xbt/sysdep.h"
#include <stddef.h>
#include <stdlib.h>
XBT_LOG_NEW_DEFAULT_CATEGORY(disk_example, "Messages specific for this simulation");
static void host(int argc, char* argv[])
{
const char* host_name = sg_host_get_name(sg_host_self());
/* - Display information on the disks mounted by the current host */
XBT_INFO("*** Storage info on %s ***", host_name);
/* - Retrieve all disks from current host */
unsigned int disk_count;
sg_disk_t* disk_list;
sg_host_get_disks(sg_host_self(), &disk_count, &disk_list);
for (unsigned int i = 0; i < disk_count; i++)
XBT_INFO("Disk name: %s (read: %.0f B/s -- write: %.0f B/s", sg_disk_get_name(disk_list[i]),
sg_disk_read_bandwidth(disk_list[i]), sg_disk_write_bandwidth(disk_list[i]));
/* - Write 400,000 bytes on Disk1 */
sg_disk_t disk = disk_list[0];
sg_size_t write = sg_disk_write(disk, 400000);
XBT_INFO("Wrote %llu bytes on '%s'", write, sg_disk_get_name(disk));
/* - Now read 200,000 bytes */
sg_size_t read = sg_disk_read(disk, 200000);
XBT_INFO("Read %llu bytes on '%s'", read, sg_disk_get_name(disk));
/* - Attach some user data to disk1 */
XBT_INFO("*** Get/set data for storage element: Disk1 ***");
char* data = (char*)sg_disk_get_data(disk);
XBT_INFO("Get storage data: '%s'", data ? data : "No user data");
sg_disk_set_data(disk, xbt_strdup("Some user data"));
data = (char*)sg_disk_get_data(disk);
XBT_INFO("Set and get data: '%s'", data);
free(data);
free(disk_list);
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
simgrid_load_platform(argv[1]);
simgrid_register_function("host", host);
size_t host_count = sg_host_count();
sg_host_t* hosts = sg_host_list();
for (size_t i = 0; i < host_count; i++) {
XBT_INFO("*** %s properties ****", sg_host_get_name(hosts[i]));
xbt_dict_t props = sg_host_get_properties(hosts[i]);
xbt_dict_cursor_t cursor = NULL;
char* key;
void* data;
xbt_dict_foreach (props, cursor, key, data)
XBT_INFO(" %s -> %s", key, (char*)data);
xbt_dict_free(&props);
}
free(hosts);
sg_actor_create("", sg_host_by_name("bob"), &host, 0, NULL);
simgrid_run();
XBT_INFO("Simulated time %g", simgrid_get_clock());
return 0;
}
This shows how to declare disks in XML.
View examples/platforms/hosts_with_disks.xml
<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
<platform version="4.1">
<zone id="AS0" routing="Full">
<host id="bob" speed="1Gf">
<prop id="ram" value="100B" />
<disk id="Disk1" read_bw="100MBps" write_bw="40MBps">
<prop id="size" value="500GiB"/>
<prop id="mount" value="/scratch"/>
<prop id="content" value="storage/content/storage_content.txt"/>
</disk>
<disk id="Disk2" read_bw="200MBps" write_bw="80MBps"/>
</host>
<host id="alice" speed="1Gf">
<disk id="Disk1" read_bw="200MBps" write_bw="80MBps">
<prop id="content" value="storage/content/small_content.txt"/>
</disk>
<prop id="ram" value="100B" />
</host>
<host id="carl" speed="1Gf">
<prop id="remote_disk" value="/scratch:Disk1:bob"/>
</host>
<link id="link1" bandwidth="125MBps" latency="150us" />
<link id="link2" bandwidth="125MBps" latency="150us" />
<link id="link3" bandwidth="125MBps" latency="150us" />
<route src="bob" dst="alice">
<link_ctn id="link1" />
</route>
<route src="bob" dst="carl">
<link_ctn id="link2" />
</route>
<route src="alice" dst="carl">
<link_ctn id="link3" />
</route>
</zone>
</platform>
Asynchronous raw accesses
As most other activities, raw IO accesses can be used asynchronously, as illustrated in this example.
View examples/cpp/io-async/s4u-io-async.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void test(sg_size_t size)
{
const sg4::Disk* disk = sg4::Host::current()->get_disks().front();
XBT_INFO("Hello! read %llu bytes from %s", size, disk->get_cname());
sg4::IoPtr activity = disk->io_init(size, sg4::Io::OpType::READ);
activity->start();
activity->wait();
XBT_INFO("Goodbye now!");
}
static void test_waitfor(sg_size_t size)
{
const sg4::Disk* disk = sg4::Host::current()->get_disks().front();
XBT_INFO("Hello! write %llu bytes from %s", size, disk->get_cname());
sg4::IoPtr activity = disk->write_async(size);
try {
activity->wait_for(0.5);
} catch (const simgrid::TimeoutException&) {
XBT_INFO("Asynchronous write: Timeout!");
}
XBT_INFO("Goodbye now!");
}
static void test_cancel(sg_size_t size)
{
const sg4::Disk* disk = sg4::Host::current()->get_disks().front();
sg4::this_actor::sleep_for(0.5);
XBT_INFO("Hello! write %llu bytes from %s", size, disk->get_cname());
sg4::IoPtr activity = disk->write_async(size);
sg4::this_actor::sleep_for(0.5);
XBT_INFO("I changed my mind, cancel!");
activity->cancel();
XBT_INFO("Goodbye now!");
}
static void test_monitor(sg_size_t size)
{
const sg4::Disk* disk = sg4::Host::current()->get_disks().front();
sg4::this_actor::sleep_for(1);
sg4::IoPtr activity = disk->write_async(size);
while (not activity->test()) {
XBT_INFO("Remaining amount of bytes to write: %g", activity->get_remaining());
sg4::this_actor::sleep_for(0.2);
}
activity->wait();
XBT_INFO("Goodbye now!");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("test", e.host_by_name("bob"), test, 2e7);
sg4::Actor::create("test_waitfor", e.host_by_name("alice"), test_waitfor, 5e7);
sg4::Actor::create("test_cancel", e.host_by_name("alice"), test_cancel, 5e7);
sg4::Actor::create("test_monitor", e.host_by_name("alice"), test_monitor, 5e7);
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
Filesystem plugin
The FileSystem plugin provides a more detailed view, with the classical operations over files: open, move, unlink, and of course, read and write. The file and disk sizes are also dealt with and can result in short reads and short writes, as in reality.
File Management: This example illustrates the use of operations on files (read, write, seek, tell, unlink, etc).
View examples/cpp/io-file-system/s4u-io-file-system.cpp
Download s4u-io-file-system.cpp
/* Copyright (c) 2006-2024. The SimGrid Team. All rights reserved. */ /* This program is free software; you can redistribute it and/or modify it * under the terms of the license (GNU LGPL) which comes with this package. */ #include <string> #include <vector> #include "simgrid/plugins/file_system.h" #include "simgrid/s4u.hpp" XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "a sample log category"); namespace sg4 = simgrid::s4u; class MyHost { public: void show_info(std::vector<sg4::Disk*> const& disks) const { XBT_INFO("Storage info on %s:", sg4::Host::current()->get_cname()); for (auto const& d : disks) { // Retrieve disk's information XBT_INFO(" %s (%s) Used: %llu; Free: %llu; Total: %llu.", d->get_cname(), sg_disk_get_mount_point(d), sg_disk_get_size_used(d), sg_disk_get_size_free(d), sg_disk_get_size(d)); } } void operator()() const { std::vector<sg4::Disk*> const& disks = sg4::Host::current()->get_disks(); show_info(disks); // Open a non-existing file to create it std::string filename = "/scratch/tmp/data.txt"; auto* file = sg4::File::open(filename, nullptr); sg_size_t write = file->write(200000); // Write 200,000 bytes XBT_INFO("Create a %llu bytes file named '%s' on /scratch", write, filename.c_str()); // check that sizes have changed show_info(disks); // Now retrieve the size of created file and read it completely const sg_size_t file_size = file->size(); file->seek(0); const sg_size_t read = file->read(file_size); XBT_INFO("Read %llu bytes on %s", read, filename.c_str()); // Now write 100,000 bytes in tmp/data.txt write = file->write(100000); // Write 100,000 bytes XBT_INFO("Write %llu bytes on %s", write, filename.c_str()); // Now rename file from ./tmp/data.txt to ./tmp/simgrid.readme std::string newpath = "/scratch/tmp/simgrid.readme"; XBT_INFO("Move '%s' to '%s'", file->get_path(), newpath.c_str()); file->move(newpath); // Test attaching some user data to the file file->set_data(new std::string("777")); auto file_data = file->get_unique_data<std::string>(); XBT_INFO("User data attached to the file: %s", file_data->c_str()); // Close the file file->close(); show_info(disks); // Reopen the file and then unlink it file = sg4::File::open("/scratch/tmp/simgrid.readme", nullptr); XBT_INFO("Unlink file: '%s'", file->get_path()); file->unlink(); file->close(); // Unlinking the file on "disk" does not close the file and free the object show_info(disks); } }; int main(int argc, char** argv) { sg4::Engine e(&argc, argv); sg_storage_file_system_init(); e.load_platform(argv[1]); sg4::Actor::create("host", e.host_by_name("bob"), MyHost()); e.run(); return 0; }View examples/c/io-file-system/io-file-system.c
/* Copyright (c) 2008-2024. The SimGrid Team. All rights reserved. */ /* This program is free software; you can redistribute it and/or modify it * under the terms of the license (GNU LGPL) which comes with this package. */ #include "simgrid/actor.h" #include "simgrid/disk.h" #include "simgrid/engine.h" #include "simgrid/host.h" #include "simgrid/plugins/file_system.h" #include "xbt/log.h" #include "xbt/str.h" #include "xbt/sysdep.h" #include <stdio.h> /* SEEK_SET */ XBT_LOG_NEW_DEFAULT_CATEGORY(io_file_system, "Messages specific for this io example"); static void show_info(unsigned int disk_count, const sg_disk_t* disks) { XBT_INFO("Storage info on %s:", sg_host_self_get_name()); for (unsigned int i = 0; i < disk_count; i++) { const_sg_disk_t d = disks[i]; // Retrieve disk's information XBT_INFO(" %s (%s) Used: %llu; Free: %llu; Total: %llu.", sg_disk_get_name(d), sg_disk_get_mount_point(d), sg_disk_get_size_used(d), sg_disk_get_size_free(d), sg_disk_get_size(d)); } } static void host(int argc, char* argv[]) { unsigned int disk_count; sg_disk_t* disks; sg_host_get_disks(sg_host_self(), &disk_count, &disks); show_info(disk_count, disks); // Open a non-existing file to create it const char* filename = "/scratch/tmp/data.txt"; sg_file_t file = sg_file_open(filename, NULL); sg_size_t write = sg_file_write(file, 200000); // Write 200,000 bytes XBT_INFO("Create a %llu bytes file named '%s' on /scratch", write, filename); // check that sizes have changed show_info(disk_count, disks); // Now retrieve the size of created file and read it completely const sg_size_t file_size = sg_file_get_size(file); sg_file_seek(file, 0, SEEK_SET); const sg_size_t read = sg_file_read(file, file_size); XBT_INFO("Read %llu bytes on %s", read, filename); // Now write 100,000 bytes in tmp/data.txt write = sg_file_write(file, 100000); // Write 100,000 bytes XBT_INFO("Write %llu bytes on %s", write, filename); // Now rename file from ./tmp/data.txt to ./tmp/simgrid.readme const char* newpath = "/scratch/tmp/simgrid.readme"; XBT_INFO("Move '%s' to '%s'", sg_file_get_name(file), newpath); sg_file_move(file, newpath); // Test attaching some user data to the file sg_file_set_data(file, xbt_strdup("777")); char* file_data = (char*)(sg_file_get_data(file)); XBT_INFO("User data attached to the file: %s", file_data); xbt_free(file_data); // Close the file sg_file_close(file); show_info(disk_count, disks); // Reopen the file and then unlink it file = sg_file_open("/scratch/tmp/simgrid.readme", NULL); XBT_INFO("Unlink file: '%s'", sg_file_get_name(file)); sg_file_unlink(file); show_info(disk_count, disks); xbt_free(disks); } int main(int argc, char** argv) { simgrid_init(&argc, argv); sg_storage_file_system_init(); simgrid_load_platform(argv[1]); sg_actor_create("host", sg_host_by_name("bob"), &host, 0, NULL); simgrid_run(); return 0; }Remote I/O: I/O operations on files can also be done remotely, i.e. when the accessed disk is not mounted on the caller’s host.
View examples/cpp/io-file-remote/s4u-io-file-remote.cpp
Download s4u-io-file-remote.cpp
/* Copyright (c) 2014-2024. The SimGrid Team. All rights reserved. */ /* This program is free software; you can redistribute it and/or modify it * under the terms of the license (GNU LGPL) which comes with this package. */ #include <simgrid/plugins/file_system.h> #include <simgrid/s4u.hpp> #include <string> #define INMEGA (1024 * 1024) XBT_LOG_NEW_DEFAULT_CATEGORY(remote_io, "Messages specific for this io example"); namespace sg4 = simgrid::s4u; static void host(std::vector<std::string> args) { sg4::File* file = sg4::File::open(args[1], nullptr); const char* filename = file->get_path(); XBT_INFO("Opened file '%s'", filename); file->dump(); XBT_INFO("Try to write %llu MiB to '%s'", file->size() / 1024, filename); sg_size_t write = file->write(file->size() * 1024); XBT_INFO("Have written %llu MiB to '%s'.", write / (1024 * 1024), filename); if (args.size() > 4) { if (std::stoi(args[4]) != 0) { XBT_INFO("Move '%s' (of size %llu) from '%s' to '%s'", filename, file->size(), sg4::Host::current()->get_cname(), args[2].c_str()); file->remote_move(sg4::Host::by_name(args[2]), args[3]); } else { XBT_INFO("Copy '%s' (of size %llu) from '%s' to '%s'", filename, file->size(), sg4::Host::current()->get_cname(), args[2].c_str()); file->remote_copy(sg4::Host::by_name(args[2]), args[3]); } } file->close(); } int main(int argc, char** argv) { sg4::Engine e(&argc, argv); sg_storage_file_system_init(); e.load_platform(argv[1]); e.register_function("host", host); e.load_deployment(argv[2]); std::vector<sg4::Host*> all_hosts = e.get_all_hosts(); for (auto const& h : all_hosts) { for (auto const& d : h->get_disks()) XBT_INFO("Init: %s: %llu/%llu MiB used/free on '%s@%s'", h->get_cname(), sg_disk_get_size_used(d) / INMEGA, sg_disk_get_size_free(d) / INMEGA, d->get_cname(), d->get_host()->get_cname()); } e.run(); for (auto const& h : all_hosts) { for (auto const& d : h->get_disks()) XBT_INFO("End: %llu/%llu MiB used/free on '%s@%s'", sg_disk_get_size_used(d) / INMEGA, sg_disk_get_size_free(d) / INMEGA, d->get_cname(), h->get_cname()); } XBT_INFO("Simulation time %g", sg4::Engine::get_clock()); return 0; }View examples/c/io-file-remote/io-file-remote.c
/* Copyright (c) 2014-2024. The SimGrid Team. All rights reserved. */ /* This program is free software; you can redistribute it and/or modify it * under the terms of the license (GNU LGPL) which comes with this package. */ #include "simgrid/actor.h" #include "simgrid/disk.h" #include "simgrid/engine.h" #include "simgrid/host.h" #include <simgrid/plugins/file_system.h> #include "xbt/asserts.h" #include "xbt/log.h" #define INMEGA (1024 * 1024) XBT_LOG_NEW_DEFAULT_CATEGORY(remote_io, "Messages specific for this io example"); static void host(int argc, char* argv[]) { sg_file_t file = sg_file_open(argv[1], NULL); const char* filename = sg_file_get_name(file); XBT_INFO("Opened file '%s'", filename); sg_file_dump(file); XBT_INFO("Try to write %llu MiB to '%s'", sg_file_get_size(file) / 1024, filename); sg_size_t write = sg_file_write(file, sg_file_get_size(file) * 1024); XBT_INFO("Have written %llu MiB to '%s'.", write / (1024 * 1024), filename); if (argc > 4) { if (atoi(argv[4]) != 0) { XBT_INFO("Move '%s' (of size %llu) from '%s' to '%s'", filename, sg_file_get_size(file), sg_host_self_get_name(), argv[2]); sg_file_rmove(file, sg_host_by_name(argv[2]), argv[3]); } else { XBT_INFO("Copy '%s' (of size %llu) from '%s' to '%s'", filename, sg_file_get_size(file), sg_host_self_get_name(), argv[2]); sg_file_rcopy(file, sg_host_by_name(argv[2]), argv[3]); } } sg_file_close(file); } int main(int argc, char** argv) { simgrid_init(&argc, argv); sg_storage_file_system_init(); simgrid_load_platform(argv[1]); simgrid_register_function("host", host); simgrid_load_deployment(argv[2]); size_t host_count = sg_host_count(); sg_host_t* hosts = sg_host_list(); for (size_t i = 0; i < host_count; i++) { unsigned int disk_count; sg_disk_t* disks; sg_host_get_disks(hosts[i], &disk_count, &disks); for (unsigned int j = 0; j < disk_count; j++) XBT_INFO("Init: %s: %llu/%llu MiB used/free on '%s@%s'", sg_host_get_name(hosts[i]), sg_disk_get_size_used(disks[j]) / INMEGA, sg_disk_get_size_free(disks[j]) / INMEGA, sg_disk_get_name(disks[j]), sg_host_get_name(sg_disk_get_host(disks[j]))); free(disks); } simgrid_run(); for (size_t i = 0; i < host_count; i++) { unsigned int disk_count; sg_disk_t* disks; sg_host_get_disks(hosts[i], &disk_count, &disks); for (unsigned int j = 0; j < disk_count; j++) XBT_INFO("End: %llu/%llu MiB used/free on '%s@%s'", sg_disk_get_size_used(disks[j]) / INMEGA, sg_disk_get_size_free(disks[j]) / INMEGA, sg_disk_get_name(disks[j]), sg_host_get_name(hosts[i])); free(disks); } free(hosts); XBT_INFO("Simulation time %g", simgrid_get_clock()); return 0; }
Bags of activities
Sometimes, you want to block on a set of activities, getting unblocked when any activity of the set unblocks, or waiting for the completion of all activities in the set. This is where the ActivitySet become useful.
Waiting for all activities in a set
The wait_all()
function is useful when you want to block until all activities in a given set have been completed.
See also simgrid::s4u::ActivitySet::wait_all()
.
View examples/cpp/activityset-waitall/s4u-activityset-waitall.cpp
Download s4u-activityset-waitall.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <cstdlib>
#include <iostream>
#include <string>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_activity_waitall, "Messages specific for this s4u example");
static void bob()
{
sg4::Mailbox* mbox = sg4::Mailbox::by_name("mbox");
sg4::MessageQueue* mqueue = sg4::MessageQueue::by_name("mqueue");
const sg4::Disk* disk = sg4::Host::current()->get_disks().front();
std::string* payload;
std::string* message;
XBT_INFO("Create my asynchronous activities");
auto exec = sg4::this_actor::exec_async(5e9);
auto comm = mbox->get_async(&payload);
auto io = disk->read_async(3e8);
auto mess = mqueue->get_async(&message);
sg4::ActivitySet pending_activities({exec, comm, io, mess});
XBT_INFO("Wait for asynchronous activities to complete, all in one shot.");
pending_activities.wait_all();
XBT_INFO("All activities are completed.");
delete payload;
delete message;
}
static void alice()
{
auto* payload = new std::string("Message");
XBT_INFO("Send '%s'", payload->c_str());
sg4::Mailbox::by_name("mbox")->put(payload, 6e8);
}
static void carl()
{
auto* payload = new std::string("Control Message");
sg4::MessageQueue::by_name("mqueue")->put(payload);
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("bob", e.host_by_name("bob"), bob);
sg4::Actor::create("alice", e.host_by_name("alice"), alice);
sg4::Actor::create("carl", e.host_by_name("carl"), carl);
e.run();
return 0;
}
See also simgrid.ActivitySet.wait_all()
.
View examples/python/activityset-waitall/activityset-waitall.py
Download activityset-waitall.py
# Copyright (c) 2017-2024. The SimGrid Team. All rights reserved.
#
# This program is free software you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
Usage: activityset-waitall.py platform_file [other parameters]
"""
import sys
from simgrid import Actor, ActivitySet, Engine, Comm, Exec, Io, Host, Mailbox, this_actor
def bob():
mbox = Mailbox.by_name("mbox")
disk = Host.current().get_disks()[0]
this_actor.info("Create my asynchronous activities")
exe = this_actor.exec_async(5e9)
comm = mbox.get_async()
io = disk.read_async(300000000)
pending_activities = ActivitySet([exe, comm])
pending_activities.push(io) # Activities can be pushed after creation, too
this_actor.info("Wait for asynchronous activities to complete, all in one shot.")
pending_activities.wait_all()
this_actor.info("All activities are completed.")
def alice():
this_actor.info("Send 'Message'")
Mailbox.by_name("mbox").put("Message", 600000000)
if __name__ == '__main__':
e = Engine(sys.argv)
e.set_log_control("root.fmt:[%4.6r]%e[%5a]%e%m%n")
# Load the platform description
e.load_platform(sys.argv[1])
Actor.create("bob", Host.by_name("bob"), bob)
Actor.create("alice", Host.by_name("alice"), alice)
e.run()
See also sg_activity_set_wait_all()
.
View examples/c/activityset-waitall/activityset-waitall.c
Download activityset-waitall.c
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/activity_set.h"
#include "simgrid/actor.h"
#include "simgrid/comm.h"
#include "simgrid/engine.h"
#include "simgrid/exec.h"
#include "simgrid/host.h"
#include "simgrid/mailbox.h"
#include "xbt/log.h"
#include "xbt/sysdep.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_activity_waitall, "Messages specific for this s4u example");
static void bob()
{
XBT_INFO("Create my asynchronous activities");
sg_exec_t exec = sg_actor_exec_init(5e9);
sg_exec_start(exec);
sg_mailbox_t mbox = sg_mailbox_by_name("mbox");
void* payload = NULL;
sg_comm_t comm = sg_mailbox_get_async(mbox, &payload);
sg_activity_set_t pending_activities = sg_activity_set_init();
sg_activity_set_push(pending_activities, (sg_activity_t)exec);
sg_activity_set_push(pending_activities, (sg_activity_t)comm);
XBT_INFO("Wait for asynchronous activities to complete, all in one shot.");
sg_activity_set_wait_all(pending_activities);
sg_activity_unref((sg_activity_t)exec);
sg_activity_unref((sg_activity_t)comm);
XBT_INFO("All activities are completed.");
sg_activity_set_delete(pending_activities);
free(payload);
}
static void alice()
{
char* payload = xbt_strdup("Message");
XBT_INFO("Send '%s'", payload);
sg_mailbox_put(sg_mailbox_by_name("mbox"), payload, 6e8);
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 1,
"Usage: %s platform_file\n"
"\tExample: %s hosts_with_disks.xml\n",
argv[0], argv[0]);
simgrid_load_platform(argv[1]);
sg_actor_create("alice", sg_host_by_name("alice"), &alice, 0, NULL);
sg_actor_create("bob", sg_host_by_name("bob"), &bob, 0, NULL);
simgrid_run();
return 0;
}
Waiting for all activities in a set (with timeout)
The wait_all_for()
function is very similar to wait_all()
but allows to specify a timeout.
See also simgrid::s4u::ActivitySet::wait_all_for()
.
View examples/cpp/activityset-waitallfor/s4u-activityset-waitallfor.cpp
Download s4u-activityset-waitallfor.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <cstdlib>
#include <iostream>
#include <string>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_activity_waitallfor, "Messages specific for this s4u example");
static void bob()
{
sg4::Mailbox* mbox = sg4::Mailbox::by_name("mbox");
sg4::MessageQueue* mqueue = sg4::MessageQueue::by_name("mqueue");
const sg4::Disk* disk = sg4::Host::current()->get_disks().front();
std::string* payload;
std::string* message;
XBT_INFO("Create my asynchronous activities");
auto exec = sg4::this_actor::exec_async(5e9);
auto comm = mbox->get_async(&payload);
auto io = disk->read_async(3e8);
auto mess = mqueue->get_async(&message);
sg4::ActivitySet pending_activities({exec, comm, io, mess});
XBT_INFO("Wait for asynchronous activities to complete");
while (not pending_activities.empty()) {
try {
pending_activities.wait_all_for(1);
} catch (simgrid::TimeoutException&) {
XBT_INFO("Not all activities are terminated yet.");
}
while (auto completed_one = pending_activities.test_any()) {
if (boost::dynamic_pointer_cast<sg4::Comm>(completed_one))
XBT_INFO("Completed a Comm");
if (boost::dynamic_pointer_cast<sg4::Mess>(completed_one))
XBT_INFO("Completed a Mess");
if (boost::dynamic_pointer_cast<sg4::Exec>(completed_one))
XBT_INFO("Completed an Exec");
if (boost::dynamic_pointer_cast<sg4::Io>(completed_one))
XBT_INFO("Completed an I/O");
}
}
XBT_INFO("Last activity is complete");
delete payload;
delete message;
}
static void alice()
{
auto* payload = new std::string("Message");
XBT_INFO("Send '%s'", payload->c_str());
sg4::Mailbox::by_name("mbox")->put(payload, 6e8);
}
static void carl()
{
sg4::this_actor::sleep_for(1.99);
auto* payload = new std::string("Control Message");
XBT_INFO("Send '%s'", payload->c_str());
sg4::MessageQueue::by_name("mqueue")->put(payload);
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("bob", e.host_by_name("bob"), bob);
sg4::Actor::create("alice", e.host_by_name("alice"), alice);
sg4::Actor::create("carl", e.host_by_name("carl"), carl);
e.run();
return 0;
}
See also simgrid.ActivitySet.wait_all_for()
.
View examples/python/activityset-waitallfor/activityset-waitallfor.py
Download activityset-waitallfor.py
# Copyright (c) 2017-2024. The SimGrid Team. All rights reserved.
#
# This program is free software you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
Usage: activityset-waitallfor.py platform_file [other parameters]
"""
import sys
from simgrid import Actor, ActivitySet, Engine, Comm, Exec, Io, Host, Mailbox, this_actor, TimeoutException
def bob():
mbox = Mailbox.by_name("mbox")
disk = Host.current().get_disks()[0]
this_actor.info("Create my asynchronous activities")
exe = this_actor.exec_async(5e9)
comm = mbox.get_async()
io = disk.read_async(300000000)
pending_activities = ActivitySet([exe, comm])
pending_activities.push(io) # Activities can be pushed after creation, too
this_actor.info("Wait for asynchronous activities to complete")
while not pending_activities.empty():
try:
pending_activities.wait_all_for(1)
except TimeoutException:
this_actor.info("Not all activities are terminated yet.")
completed_one = pending_activities.test_any()
while completed_one != None:
if isinstance(completed_one, Comm):
this_actor.info("Completed a Comm")
elif isinstance(completed_one, Exec):
this_actor.info("Completed an Exec")
elif isinstance(completed_one, Io):
this_actor.info("Completed an I/O")
completed_one = pending_activities.test_any()
this_actor.info("Last activity is complete")
def alice():
this_actor.info("Send 'Message'")
Mailbox.by_name("mbox").put("Message", 600000000)
if __name__ == '__main__':
e = Engine(sys.argv)
e.set_log_control("root.fmt:[%4.6r]%e[%5a]%e%m%n")
# Load the platform description
e.load_platform(sys.argv[1])
Actor.create("bob", Host.by_name("bob"), bob)
Actor.create("alice", Host.by_name("alice"), alice)
e.run()
See also sg_activity_set_wait_all_for()
.
View examples/c/activityset-waitallfor/activityset-waitallfor.c
Download activityset-waitallfor.c
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/activity_set.h"
#include "simgrid/actor.h"
#include "simgrid/comm.h"
#include "simgrid/engine.h"
#include "simgrid/exec.h"
#include "simgrid/host.h"
#include "simgrid/mailbox.h"
#include "xbt/log.h"
#include "xbt/sysdep.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_activity_waitallfor, "Messages specific for this s4u example");
static void bob()
{
XBT_INFO("Create my asynchronous activities");
sg_exec_t exec = sg_actor_exec_init(5e9);
sg_exec_start(exec);
sg_mailbox_t mbox = sg_mailbox_by_name("mbox");
void* payload = NULL;
sg_comm_t comm = sg_mailbox_get_async(mbox, &payload);
sg_activity_set_t pending_activities = sg_activity_set_init();
sg_activity_set_push(pending_activities, (sg_activity_t)exec);
sg_activity_set_push(pending_activities, (sg_activity_t)comm);
XBT_INFO("Wait for asynchronous activities to complete");
while (!sg_activity_set_empty(pending_activities)) {
if (!sg_activity_set_wait_all_for(pending_activities, 1)) {
XBT_INFO("Not all activities are terminated yet.");
}
sg_activity_t completed_one = sg_activity_set_test_any(pending_activities);
while (completed_one != NULL) {
if (sg_comm_isinstance(completed_one))
XBT_INFO("Completed a Comm");
if (sg_exec_isinstance(completed_one))
XBT_INFO("Completed an Exec");
sg_activity_unref(completed_one);
completed_one = sg_activity_set_test_any(pending_activities);
}
}
XBT_INFO("Last activity is complete");
sg_activity_set_delete(pending_activities);
free(payload);
}
static void alice()
{
char* payload = xbt_strdup("Message");
XBT_INFO("Send '%s'", payload);
sg_mailbox_put(sg_mailbox_by_name("mbox"), payload, 6e8);
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 1,
"Usage: %s platform_file\n"
"\tExample: %s hosts_with_disks.xml\n",
argv[0], argv[0]);
simgrid_load_platform(argv[1]);
sg_actor_create("alice", sg_host_by_name("alice"), &alice, 0, NULL);
sg_actor_create("bob", sg_host_by_name("bob"), &bob, 0, NULL);
simgrid_run();
return 0;
}
Waiting for the first completed activity in a set
The wait_any()
blocks until one activity of the set completes, no matter which terminates first.
See also simgrid::s4u::ActivitySet::wait_any()
.
View examples/cpp/activityset-waitany/s4u-activityset-waitany.cpp
Download s4u-activityset-waitany.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <cstdlib>
#include <iostream>
#include <string>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_activity_waitany, "Messages specific for this s4u example");
static void bob()
{
sg4::Mailbox* mbox = sg4::Mailbox::by_name("mbox");
sg4::MessageQueue* mqueue = sg4::MessageQueue::by_name("mqueue");
const sg4::Disk* disk = sg4::Host::current()->get_disks().front();
std::string* payload;
std::string* message;
XBT_INFO("Create my asynchronous activities");
auto exec = sg4::this_actor::exec_async(5e9);
auto comm = mbox->get_async(&payload);
auto io = disk->read_async(3e8);
auto mess = mqueue->get_async(&message);
sg4::ActivitySet pending_activities({exec, comm, io, mess});
XBT_INFO("Wait for asynchronous activities to complete");
while (not pending_activities.empty()) {
auto completed_one = pending_activities.wait_any();
if (completed_one != nullptr) {
if (boost::dynamic_pointer_cast<sg4::Comm>(completed_one))
XBT_INFO("Completed a Comm");
if (boost::dynamic_pointer_cast<sg4::Mess>(completed_one))
XBT_INFO("Completed a Mess");
if (boost::dynamic_pointer_cast<sg4::Exec>(completed_one))
XBT_INFO("Completed an Exec");
if (boost::dynamic_pointer_cast<sg4::Io>(completed_one))
XBT_INFO("Completed an I/O");
}
}
XBT_INFO("Last activity is complete");
delete payload;
delete message;
}
static void alice()
{
auto* payload = new std::string("Message");
XBT_INFO("Send '%s'", payload->c_str());
sg4::Mailbox::by_name("mbox")->put(payload, 6e8);
}
static void carl()
{
sg4::this_actor::sleep_for(2);
auto* payload = new std::string("Control Message");
XBT_INFO("Send '%s'", payload->c_str());
sg4::MessageQueue::by_name("mqueue")->put(payload);
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("bob", e.host_by_name("bob"), bob);
sg4::Actor::create("alice", e.host_by_name("alice"), alice);
sg4::Actor::create("carl", e.host_by_name("carl"), carl);
e.run();
return 0;
}
See also simgrid.ActivitySet.wait_any()
.
View examples/python/activityset-waitany/activityset-waitany.py
Download activityset-waitany.py
# Copyright (c) 2017-2024. The SimGrid Team. All rights reserved.
#
# This program is free software you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
Usage: activityset-waitany.py platform_file [other parameters]
"""
import sys
from simgrid import Actor, ActivitySet, Engine, Comm, Exec, Io, Host, Mailbox, this_actor
def bob():
mbox = Mailbox.by_name("mbox")
disk = Host.current().get_disks()[0]
this_actor.info("Create my asynchronous activities")
exe = this_actor.exec_async(5e9)
comm = mbox.get_async()
io = disk.read_async(300000000)
pending_activities = ActivitySet([exe, comm])
pending_activities.push(io) # Activities can be pushed after creation, too
this_actor.info("Wait for asynchronous activities to complete")
while not pending_activities.empty():
completed_one = pending_activities.wait_any()
if isinstance(completed_one, Comm):
this_actor.info("Completed a Comm")
elif isinstance(completed_one, Exec):
this_actor.info("Completed an Exec")
elif isinstance(completed_one, Io):
this_actor.info("Completed an I/O")
this_actor.info("Last activity is complete")
def alice():
this_actor.info("Send 'Message'")
Mailbox.by_name("mbox").put("Message", 600000000)
if __name__ == '__main__':
e = Engine(sys.argv)
e.set_log_control("root.fmt:[%4.6r]%e[%5a]%e%m%n")
# Load the platform description
e.load_platform(sys.argv[1])
Actor.create("bob", Host.by_name("bob"), bob)
Actor.create("alice", Host.by_name("alice"), alice)
e.run()
See also sg_activity_set_wait_any()
.
View examples/c/activityset-waitany/activityset-waitany.c
Download activityset-waitany.c
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/activity_set.h"
#include "simgrid/actor.h"
#include "simgrid/comm.h"
#include "simgrid/engine.h"
#include "simgrid/exec.h"
#include "simgrid/host.h"
#include "simgrid/mailbox.h"
#include "xbt/log.h"
#include "xbt/sysdep.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_activity_waittany, "Messages specific for this s4u example");
static void bob()
{
XBT_INFO("Create my asynchronous activities");
sg_exec_t exec = sg_actor_exec_init(5e9);
sg_exec_start(exec);
sg_mailbox_t mbox = sg_mailbox_by_name("mbox");
void* payload = NULL;
sg_comm_t comm = sg_mailbox_get_async(mbox, &payload);
sg_activity_set_t pending_activities = sg_activity_set_init();
sg_activity_set_push(pending_activities, (sg_activity_t)exec);
sg_activity_set_push(pending_activities, (sg_activity_t)comm);
XBT_INFO("Wait for asynchronous activities to complete");
while (!sg_activity_set_empty(pending_activities)) {
sg_activity_t completed_one = sg_activity_set_wait_any(pending_activities);
if (sg_comm_isinstance(completed_one))
XBT_INFO("Completed a Comm");
else if (sg_exec_isinstance(completed_one))
XBT_INFO("Completed an Exec");
else
xbt_die("This activity set is supposed to only contain Comm or Exec");
sg_activity_unref(completed_one);
}
XBT_INFO("Last activity is complete");
sg_activity_set_delete(pending_activities);
free(payload);
}
static void alice()
{
char* payload = xbt_strdup("Message");
XBT_INFO("Send '%s'", payload);
sg_mailbox_put(sg_mailbox_by_name("mbox"), payload, 6e8);
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 1,
"Usage: %s platform_file\n"
"\tExample: %s hosts_with_disks.xml\n",
argv[0], argv[0]);
simgrid_load_platform(argv[1]);
sg_actor_create("alice", sg_host_by_name("alice"), &alice, 0, NULL);
sg_actor_create("bob", sg_host_by_name("bob"), &bob, 0, NULL);
simgrid_run();
return 0;
}
Testing whether at least one activity completed
The test_any()
returns whether at least one activity of the set has completed.
See also simgrid::s4u::ActivitySet::test_any()
.
View examples/cpp/activityset-testany/s4u-activityset-testany.cpp
Download s4u-activityset-testany.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <cstdlib>
#include <iostream>
#include <string>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_activity_testany, "Messages specific for this s4u example");
static void bob()
{
sg4::Mailbox* mbox = sg4::Mailbox::by_name("mbox");
sg4::MessageQueue* mqueue = sg4::MessageQueue::by_name("mqueue");
const sg4::Disk* disk = sg4::Host::current()->get_disks().front();
std::string* payload;
std::string* message;
XBT_INFO("Create my asynchronous activities");
auto exec = sg4::this_actor::exec_async(5e9);
auto comm = mbox->get_async(&payload);
auto mess = mqueue->get_async(&message);
auto io = disk->read_async(3e8);
sg4::ActivitySet pending_activities({exec, comm, mess, io});
XBT_INFO("Sleep_for a while");
sg4::this_actor::sleep_for(1);
XBT_INFO("Test for completed activities");
while (not pending_activities.empty()) {
auto completed_one = pending_activities.test_any();
if (completed_one != nullptr) {
if (boost::dynamic_pointer_cast<sg4::Comm>(completed_one))
XBT_INFO("Completed a Comm");
if (boost::dynamic_pointer_cast<sg4::Mess>(completed_one))
XBT_INFO("Completed a Mess");
if (boost::dynamic_pointer_cast<sg4::Exec>(completed_one))
XBT_INFO("Completed an Exec");
if (boost::dynamic_pointer_cast<sg4::Io>(completed_one))
XBT_INFO("Completed an I/O");
} else {
XBT_INFO("Nothing matches, test again in 0.5s");
sg4::this_actor::sleep_for(.5);
}
}
XBT_INFO("Last activity is complete");
delete payload;
delete message;
}
static void alice()
{
auto* payload = new std::string("Message");
XBT_INFO("Send '%s'", payload->c_str());
sg4::Mailbox::by_name("mbox")->put(payload, 6e8);
}
static void carl()
{
sg4::this_actor::sleep_for(1.99);
auto* payload = new std::string("Control Message");
XBT_INFO("Send '%s'", payload->c_str());
sg4::MessageQueue::by_name("mqueue")->put(payload);
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("bob", e.host_by_name("bob"), bob);
sg4::Actor::create("alice", e.host_by_name("alice"), alice);
sg4::Actor::create("carl", e.host_by_name("carl"), carl);
e.run();
return 0;
}
See also simgrid.ActivitySet.test_any()
.
View examples/python/activityset-testany/activityset-testany.py
Download activityset-testany.py
# Copyright (c) 2017-2024. The SimGrid Team. All rights reserved.
#
# This program is free software you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
Usage: activityset-testany.py platform_file [other parameters]
"""
import sys
from simgrid import Actor, ActivitySet, Engine, Comm, Exec, Io, Host, Mailbox, this_actor
def bob():
mbox = Mailbox.by_name("mbox")
disk = Host.current().get_disks()[0]
this_actor.info("Create my asynchronous activities")
exe = this_actor.exec_async(5e9)
comm = mbox.get_async()
io = disk.read_async(300000000)
pending_activities = ActivitySet([exe, comm])
pending_activities.push(io) # Activities can be pushed after creation, too
this_actor.info("Sleep_for a while")
this_actor.sleep_for(1)
this_actor.info("Test for completed activities")
while not pending_activities.empty():
completed_one = pending_activities.test_any()
if completed_one == None:
this_actor.info("Nothing matches, test again in 0.5s")
this_actor.sleep_for(.5)
elif isinstance(completed_one, Comm):
this_actor.info("Completed a Comm")
elif isinstance(completed_one, Exec):
this_actor.info("Completed an Exec")
elif isinstance(completed_one, Io):
this_actor.info("Completed an I/O")
this_actor.info("Last activity is complete")
def alice():
this_actor.info("Send 'Message'")
Mailbox.by_name("mbox").put("Message", 600000000)
if __name__ == '__main__':
e = Engine(sys.argv)
e.set_log_control("root.fmt:[%4.2r]%e[%5a]%e%m%n")
# Load the platform description
e.load_platform(sys.argv[1])
Actor.create("bob", Host.by_name("bob"), bob)
Actor.create("alice", Host.by_name("alice"), alice)
e.run()
See also sg_activity_set_test_any()
.
View examples/c/activityset-testany/activityset-testany.c
Download activityset-testany.c
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/activity_set.h"
#include "simgrid/actor.h"
#include "simgrid/comm.h"
#include "simgrid/engine.h"
#include "simgrid/exec.h"
#include "simgrid/host.h"
#include "simgrid/mailbox.h"
#include "xbt/log.h"
#include "xbt/sysdep.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_activity_testany, "Messages specific for this s4u example");
static void bob(int argc, char* argv[])
{
XBT_INFO("Create my asynchronous activities");
sg_exec_t exec = sg_actor_exec_init(5e9);
sg_exec_start(exec);
sg_mailbox_t mbox = sg_mailbox_by_name("mbox");
void* payload = NULL;
sg_comm_t comm = sg_mailbox_get_async(mbox, &payload);
sg_activity_set_t pending_activities = sg_activity_set_init();
sg_activity_set_push(pending_activities, (sg_activity_t)exec);
sg_activity_set_push(pending_activities, (sg_activity_t)comm);
XBT_INFO("Sleep_for a while");
sg_actor_sleep_for(1);
XBT_INFO("Test for completed activities");
while (!sg_activity_set_empty(pending_activities)) {
sg_activity_t completed_one = sg_activity_set_test_any(pending_activities);
if (completed_one != NULL) {
if (sg_comm_isinstance(completed_one))
XBT_INFO("Completed a Comm");
if (sg_exec_isinstance(completed_one))
XBT_INFO("Completed an Exec");
sg_activity_unref(completed_one);
} else {
XBT_INFO("Nothing matches, test again in 0.5s");
sg_actor_sleep_for(.5);
}
}
XBT_INFO("Last activity is complete");
sg_activity_set_delete(pending_activities);
free(payload);
}
static void alice(int argc, char* argv[])
{
char* payload = xbt_strdup("Message");
XBT_INFO("Send '%s'", payload);
sg_mailbox_put(sg_mailbox_by_name("mbox"), payload, 6e8);
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 1,
"Usage: %s platform_file\n"
"\tExample: %s hosts_with_disks.xml\n",
argv[0], argv[0]);
simgrid_load_platform(argv[1]);
sg_actor_create("alice", sg_host_by_name("alice"), &alice, 0, NULL);
sg_actor_create("bob", sg_host_by_name("bob"), &bob, 0, NULL);
simgrid_run();
return 0;
}
Dependencies between activities
SimGrid makes it easy to express dependencies between activities, where a given activity cannot start until the completion of all its predecessors. You can even have simulation not involving any actors, where the main thread (called maestro) creates and schedules activities itself.
Simple dependencies
When you declare dependencies between two activities, the dependent will not actually start until all its dependencies complete, as shown in the following examples. The first one declare dependencies between executions while the second one declare dependencies between communications. You could declare such dependencies between arbitrary activities.
View examples/cpp/exec-dependent/s4u-exec-dependent.cpp
Download s4u-exec-dependent.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <vector>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void worker()
{
// Define an amount of work that should take 1 second to execute.
double computation_amount = sg4::this_actor::get_host()->get_speed();
// Create a small DAG
// + Two parents and a child
// + First parent ends after 1 second and the Second parent after 2 seconds.
sg4::ExecPtr first_parent = sg4::this_actor::exec_init(computation_amount);
sg4::ExecPtr second_parent = sg4::this_actor::exec_init(2 * computation_amount);
sg4::ExecPtr child = sg4::Exec::init()->set_flops_amount(computation_amount);
sg4::ActivitySet pending_execs ({first_parent, second_parent, child});
// Name the activities (for logging purposes only)
first_parent->set_name("parent 1");
second_parent->set_name("parent 2");
child->set_name("child");
// Create the dependencies by declaring 'child' as a successor of first_parent and second_parent
first_parent->add_successor(child);
second_parent->add_successor(child);
// Start the activities.
first_parent->start();
second_parent->start();
child->start();
// wait for the completion of all activities
while (not pending_execs.empty()) {
auto completed_one = pending_execs.wait_any();
if (completed_one != nullptr)
XBT_INFO("Exec '%s' is complete", completed_one->get_cname());
}
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("worker", e.host_by_name("Fafard"), worker);
sg4::Exec::on_veto_cb([&e](sg4::Exec& exec) {
// First display the situation
XBT_INFO("Activity '%s' vetoed. Dependencies: %s; Ressources: %s", exec.get_cname(),
(exec.dependencies_solved() ? "solved" : "NOT solved"),
(exec.is_assigned() ? "assigned" : "NOT assigned"));
// In this simple case, we just assign the child task to a resource when its dependencies are solved
if (exec.dependencies_solved() && not exec.is_assigned()) {
XBT_INFO("Activity %s's dependencies are resolved. Let's assign it to Fafard.", exec.get_cname());
exec.set_host(e.host_by_name("Fafard"));
}
});
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
View examples/cpp/comm-dependent/s4u-comm-dependent.cpp
Download s4u-comm-dependent.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <simgrid/s4u.hpp>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_comm_dependent, "Messages specific for this s4u example");
static void sender(sg4::Mailbox* mailbox)
{
auto* computation_amount = new double(sg4::this_actor::get_host()->get_speed());
sg4::ExecPtr exec = sg4::this_actor::exec_init(2 * (*computation_amount));
sg4::CommPtr comm = mailbox->put_init(computation_amount, 7e6);
exec->set_name("exec on sender")->add_successor(comm)->start();
comm->set_name("comm to receiver")->start();
exec->wait();
comm->wait();
}
static void receiver(sg4::Mailbox* mailbox)
{
double* received = nullptr;
double computation_amount = sg4::this_actor::get_host()->get_speed();
sg4::ExecPtr exec = sg4::this_actor::exec_init(2 * computation_amount);
sg4::CommPtr comm = mailbox->get_init()->set_dst_data((void**)&received, sizeof(double));
comm->set_name("comm from sender")->add_successor(exec)->start();
exec->set_name("exec on receiver")->start();
comm->wait();
exec->wait();
XBT_INFO("Received: %.0f flops were computed on sender", *received);
delete received;
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Mailbox* mbox = e.mailbox_by_name_or_create("Mailbox");
sg4::Actor::create("sender", e.host_by_name("Tremblay"), sender, mbox);
sg4::Actor::create("receiver", e.host_by_name("Jupiter"), receiver, mbox);
e.run();
XBT_INFO("Simulation time: %.3f", sg4::Engine::get_clock());
return 0;
}
Assigning activities
To actually start, an activity needs to be assigned to a given resource. This examples illustrates how an execution that is not assigned will not actually start until being assigned. In some sense, activities’ assignment can be seen as a specific dependency that can withdraw their execution.
View examples/cpp/exec-unassigned/s4u-exec-unassigned.cpp
Download s4u-exec-unassigned.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <vector>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void worker()
{
// Define an amount of work that should take 1 second to execute.
double computation_amount = sg4::this_actor::get_host()->get_speed();
// Create an unassigned activity and start it. It will not actually start, because it's not assigned to any host yet
sg4::ExecPtr exec = sg4::Exec::init()->set_flops_amount(computation_amount)->set_name("exec")->start();
// Wait for a while
sg4::this_actor::sleep_for(10);
// Assign the activity to the current host. This triggers its start, then waits for it completion.
exec->set_host(sg4::Host::current())->wait();
XBT_INFO("Exec '%s' is complete", exec->get_cname());
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("worker", e.host_by_name("Fafard"), worker);
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
Simple DAG of activities
This example shows how to create activities from the maestro directly without relying on an actor, organize the dependencies of activities as a DAG (direct acyclic graph), and start them. Each activity will start as soon as its dependencies are fulfilled.
View examples/cpp/dag-simple/s4u-dag-simple.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <vector>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
std::set<sg4::Activity*> vetoed;
e.track_vetoed_activities(&vetoed);
auto* fafard = e.host_by_name("Fafard");
// Display the details on vetoed activities
sg4::Exec::on_veto_cb([](sg4::Exec const& exec) {
XBT_INFO("Execution '%s' vetoed. Dependencies: %s; Ressources: %s", exec.get_cname(),
(exec.dependencies_solved() ? "solved" : "NOT solved"),
(exec.is_assigned() ? "assigned" : "NOT assigned"));
});
sg4::Exec::on_completion_cb([](sg4::Exec const& exec) {
XBT_INFO("Execution '%s' is complete (start time: %f, finish time: %f)", exec.get_cname(), exec.get_start_time(),
exec.get_finish_time());
});
// Define an amount of work that should take 1 second to execute.
double computation_amount = fafard->get_speed();
// Create a small DAG: Two parents and a child
sg4::ExecPtr first_parent = sg4::Exec::init();
sg4::ExecPtr second_parent = sg4::Exec::init();
sg4::ExecPtr child = sg4::Exec::init();
first_parent->add_successor(child);
second_parent->add_successor(child);
// Set the parameters (the name is for logging purposes only)
// + First parent ends after 1 second and the Second parent after 2 seconds.
first_parent->set_name("parent 1")->set_flops_amount(computation_amount);
second_parent->set_name("parent 2")->set_flops_amount(2 * computation_amount);
child->set_name("child")->set_flops_amount(computation_amount);
// Only the parents are scheduled so far
first_parent->set_host(fafard);
second_parent->set_host(fafard);
// Start all activities that can actually start.
first_parent->start();
second_parent->start();
child->start();
while (child->get_state() != sg4::Activity::State::FINISHED) {
e.run();
for (auto* a : vetoed) {
auto* exec = static_cast<sg4::Exec*>(a);
// In this simple case, we just assign the child task to a resource when its dependencies are solved
if (exec->dependencies_solved() && not exec->is_assigned()) {
XBT_INFO("Activity %s's dependencies are resolved. Let's assign it to Fafard.", exec->get_cname());
exec->set_host(fafard);
} else {
XBT_INFO("Activity %s not ready.", exec->get_cname());
}
}
vetoed.clear(); // DON'T FORGET TO CLEAR this set between two calls to run
}
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
DAG with communication
This is a little example showing how add communication activities to your DAG, representing inter-task data exchanges.
View examples/cpp/dag-comm/s4u-dag-comm.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <simgrid/plugins/file_system.h>
#include <simgrid/s4u.hpp>
#include <vector>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
auto* tremblay = e.host_by_name("Tremblay");
auto* jupiter = e.host_by_name("Jupiter");
// Display the details on vetoed activities
sg4::Exec::on_veto_cb([](sg4::Exec const& exec) {
XBT_INFO("Execution '%s' vetoed. Dependencies: %s; Ressources: %s", exec.get_cname(),
(exec.dependencies_solved() ? "solved" : "NOT solved"), (exec.is_assigned() ? "assigned" : "NOT assigned"));
});
sg4::Comm::on_veto_cb([](sg4::Comm const& comm) {
XBT_INFO("Communication '%s' vetoed. Dependencies: %s; Ressources: %s", comm.get_cname(),
(comm.dependencies_solved() ? "solved" : "NOT solved"), (comm.is_assigned() ? "assigned" : "NOT assigned"));
});
sg4::Exec::on_completion_cb([](sg4::Exec const& exec) {
XBT_INFO("Exec '%s' is complete (start time: %f, finish time: %f)", exec.get_cname(), exec.get_start_time(),
exec.get_finish_time());
});
sg4::Comm::on_completion_cb([](sg4::Comm const& comm) {
XBT_INFO("Comm '%s' is complete", comm.get_cname());
});
// Create a small DAG: parent->transfer->child
sg4::ExecPtr parent = sg4::Exec::init();
sg4::CommPtr transfer = sg4::Comm::sendto_init();
sg4::ExecPtr child = sg4::Exec::init();
parent->add_successor(transfer);
transfer->add_successor(child);
// Set the parameters (the name is for logging purposes only)
// + parent and child end after 1 second
parent->set_name("parent")->set_flops_amount(tremblay->get_speed())->start();
transfer->set_name("transfer")->set_payload_size(125e6)->start();
child->set_name("child")->set_flops_amount(jupiter->get_speed())->start();
// Schedule the different activities
parent->set_host(tremblay);
transfer->set_source(tremblay);
child->set_host(jupiter);
transfer->set_destination(jupiter);
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
DAG with I/O
This is a little example showing how add I/O activities to your DAG, representing disk buffers.
View examples/cpp/dag-io/s4u-dag-io.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <simgrid/plugins/file_system.h>
#include <simgrid/s4u.hpp>
#include <vector>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
auto* bob = e.host_by_name("bob");
auto* carl = e.host_by_name("carl");
// Display the details on vetoed activities
sg4::Exec::on_veto_cb([](sg4::Exec const& exec) {
XBT_INFO("Exec '%s' vetoed. Dependencies: %s; Ressources: %s", exec.get_cname(),
(exec.dependencies_solved() ? "solved" : "NOT solved"), (exec.is_assigned() ? "assigned" : "NOT assigned"));
});
sg4::Io::on_veto_cb([](sg4::Io const& io) {
XBT_INFO("Io '%s' vetoed. Dependencies: %s; Ressources: %s", io.get_cname(),
(io.dependencies_solved() ? "solved" : "NOT solved"), (io.is_assigned() ? "assigned" : "NOT assigned"));
});
sg4::Exec::on_completion_cb([](sg4::Exec const& exec) {
XBT_INFO("Exec '%s' is complete (start time: %f, finish time: %f)", exec.get_cname(), exec.get_start_time(),
exec.get_finish_time());
});
// Create a small DAG: parent->write_output->read_input->child
sg4::ExecPtr parent = sg4::Exec::init();
sg4::IoPtr write_output = sg4::Io::init();
sg4::IoPtr read_input = sg4::Io::init();
sg4::ExecPtr child = sg4::Exec::init();
parent->add_successor(write_output);
write_output->add_successor(read_input);
read_input->add_successor(child);
// Set the parameters (the name is for logging purposes only)
// + parent and chile end after 1 second
parent->set_name("parent")->set_flops_amount(bob->get_speed());
write_output->set_name("write")->set_size(1e9)->set_op_type(sg4::Io::OpType::WRITE);
read_input->set_name("read")->set_size(1e9)->set_op_type(sg4::Io::OpType::READ);
child->set_name("child")->set_flops_amount(carl->get_speed());
// Schedule and try to start the different activities
parent->set_host(bob)->start();
write_output->set_disk(bob->get_disks().front())->start();
read_input->set_disk(carl->get_disks().front())->start();
child->set_host(carl)->start();
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
Scheduling activities
This example illustrates a simple scheduling algorithm, where the activities are placed on the “most adapted” host. Of course, there is many way to determine which host is the better fit for a given activity, and this example just uses a simple algorithm.
View examples/cpp/dag-scheduling/s4u-dag-scheduling.cpp
Download s4u-dag-scheduling.cpp
/* Copyright (c) 2009-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* simple test to schedule a DAX file with the Min-Min algorithm. */
#include <algorithm>
#include <simgrid/host.h>
#include <simgrid/s4u.hpp>
#include <string.h>
XBT_LOG_NEW_DEFAULT_CATEGORY(dag_scheduling, "Logging specific to this example");
namespace sg4 = simgrid::s4u;
static std::vector<sg4::Exec*> get_ready_tasks(const std::vector<sg4::ActivityPtr>& dax)
{
std::vector<sg4::Exec*> ready_tasks;
std::map<sg4::Exec*, unsigned int> candidate_execs;
for (const auto& a : dax) {
// Only look at activity that have their dependencies solved but are not assigned
if (a->dependencies_solved() && not a->is_assigned()) {
// if it is an exec, it's ready
if (auto* exec = dynamic_cast<sg4::Exec*>(a.get()))
ready_tasks.push_back(exec);
// if it a comm, we consider its successor as a candidate. If a candidate solves all its dependencies,
// i.e., get all its input data, it's ready
if (const auto* comm = dynamic_cast<sg4::Comm*>(a.get())) {
auto* next_exec = static_cast<sg4::Exec*>(comm->get_successors().front().get());
candidate_execs[next_exec]++;
if (next_exec->get_dependencies().size() == candidate_execs[next_exec])
ready_tasks.push_back(next_exec);
}
}
}
XBT_DEBUG("There are %zu ready tasks", ready_tasks.size());
return ready_tasks;
}
static sg4::Host* get_best_host(const sg4::ExecPtr exec, double* min_finish_time)
{
sg4::Host* best_host = nullptr;
*min_finish_time = std::numeric_limits<double>::max();
for (const auto& host : sg4::Engine::get_instance()->get_all_hosts()) {
double data_available = 0.;
double last_data_available = -1.0;
/* compute last_data_available */
for (const auto& parent : exec->get_dependencies()) {
/* normal case */
if (const auto* comm = dynamic_cast<sg4::Comm*>(parent.get())) {
const auto* source = comm->get_source();
XBT_DEBUG("transfer from %s to %s", source->get_cname(), host->get_cname());
/* Estimate the redistribution time from this parent */
double redist_time;
if (comm->get_remaining() <= 1e-6) {
redist_time = 0;
} else {
double bandwidth = std::numeric_limits<double>::max();
auto [links, latency] = source->route_to(host);
for (auto const& link : links)
bandwidth = std::min(bandwidth, link->get_bandwidth());
redist_time = latency + comm->get_remaining() / bandwidth;
}
// We use the user data field to store the finish time of the predecessor of the comm, i.e., its potential
// start time
data_available = *comm->get_data<double>() + redist_time;
}
/* no transfer, control dependency */
if (const auto* parent_exec = dynamic_cast<sg4::Exec*>(parent.get()))
data_available = parent_exec->get_finish_time();
if (last_data_available < data_available)
last_data_available = data_available;
}
double finish_time = std::max(*host->get_data<double>(), last_data_available) +
exec->get_remaining() / host->get_speed();
XBT_DEBUG("%s finishes on %s at %f", exec->get_cname(), host->get_cname(), finish_time);
if (finish_time < *min_finish_time) {
*min_finish_time = finish_time;
best_host = host;
}
}
return best_host;
}
static void schedule_on(sg4::ExecPtr exec, sg4::Host* host, double busy_until = 0.0)
{
exec->set_host(host);
// We use the user data field to store up to when the host is busy
delete host->get_data<double>(); // In case we're erasing a previous value
host->set_data(new double(busy_until));
// we can also set the destination of all the input comms of this exec
for (const auto& pred : exec->get_dependencies()) {
auto* comm = dynamic_cast<sg4::Comm*>(pred.get());
if (comm != nullptr) {
comm->set_destination(host);
delete comm->get_data<double>();
}
}
// we can also set the source of all the output comms of this exec
for (const auto& succ : exec->get_successors()) {
auto* comm = dynamic_cast<sg4::Comm*>(succ.get());
if (comm != nullptr)
comm->set_source(host);
}
}
int main(int argc, char** argv)
{
sg4::Engine e(&argc, argv);
std::set<sg4::Activity*> vetoed;
e.track_vetoed_activities(&vetoed);
sg4::Exec::on_completion_cb([](sg4::Exec const& exec) {
// when an Exec completes, we need to set the potential start time of all its ouput comms
for (const auto& succ : exec.get_successors()) {
auto* comm = dynamic_cast<sg4::Comm*>(succ.get());
if (comm != nullptr) {
auto* finish_time = new double(exec.get_finish_time());
// We use the user data field to store the finish time of the predecessor of the comm, i.e., its potential start
// time
comm->set_data(finish_time);
}
}
});
e.load_platform(argv[1]);
/* Mark all hosts as sequential, as it ought to be in such a scheduling example.
*
* It means that the hosts can only compute one thing at a given time. If an execution already takes place on a given
* host, any subsequently started execution will be queued until after the first execution terminates */
for (auto const& host : e.get_all_hosts()) {
host->set_concurrency_limit(1);
host->set_data(new double(0.0));
}
/* load the DAX file */
auto dax = sg4::create_DAG_from_DAX(argv[2]);
/* Schedule the root first */
double root_finish_time;
auto* root = static_cast<sg4::Exec*>(dax.front().get());
auto* host = get_best_host(root, &root_finish_time);
schedule_on(root, host);
e.run();
while (not vetoed.empty()) {
XBT_DEBUG("Start new scheduling round");
/* Get the set of ready tasks */
auto ready_tasks = get_ready_tasks(dax);
vetoed.clear();
if (ready_tasks.empty()) {
/* there is no ready exec, let advance the simulation */
e.run();
continue;
}
/* For each ready exec:
* get the host that minimizes the completion time.
* select the exec that has the minimum completion time on its best host.
*/
double min_finish_time = std::numeric_limits<double>::max();
sg4::Exec* selected_task = nullptr;
sg4::Host* selected_host = nullptr;
for (auto* exec : ready_tasks) {
XBT_DEBUG("%s is ready", exec->get_cname());
double finish_time;
host = get_best_host(exec, &finish_time);
if (finish_time < min_finish_time) {
min_finish_time = finish_time;
selected_task = exec;
selected_host = host;
}
}
XBT_INFO("Schedule %s on %s", selected_task->get_cname(), selected_host->get_cname());
schedule_on(selected_task, selected_host, min_finish_time);
ready_tasks.clear();
e.run();
}
/* Cleanup memory */
for (auto const* h : e.get_all_hosts())
delete h->get_data<double>();
XBT_INFO("Simulation Time: %f", simgrid_get_clock());
return 0;
}
Loading DAGs from file
There is currently two file formats that you can load directly in SimGrid, but writing another loader for your beloved format should not be difficult.
View examples/cpp/dag-from-dax/s4u-dag-from-dax.cpp
/* simple test trying to load a DAX file. */
/* Copyright (c) 2009-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <stdio.h>
#include <string.h>
XBT_LOG_NEW_DEFAULT_CATEGORY(dag_from_dax, "Logging specific to this example");
namespace sg4 = simgrid::s4u;
int main(int argc, char** argv)
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
std::vector<sg4::ActivityPtr> dag = sg4::create_DAG_from_DAX(argv[2]);
if (dag.empty()) {
XBT_ERROR("A problem occurred during DAX parsing (cycle or syntax). Do not continue this test");
exit(2);
}
XBT_INFO("--------- Display all activities of the loaded DAG -----------");
for (const auto& a : dag) {
std::string type = "an Exec";
std::string task = "flops to execute";
if (dynamic_cast<sg4::Comm*>(a.get()) != nullptr) {
type = "a Comm";
task = "bytes to transfer";
}
XBT_INFO("'%s' is %s: %.0f %s. Dependencies: %s; Ressources: %s", a->get_cname(), type.c_str(), a->get_remaining(),
task.c_str(), (a->dependencies_solved() ? "solved" : "NOT solved"),
(a->is_assigned() ? "assigned" : "NOT assigned"));
}
XBT_INFO("------------------- Schedule tasks ---------------------------");
auto hosts = e.get_all_hosts();
auto count = e.get_host_count();
int cursor = 0;
// Schedule end first
static_cast<sg4::Exec*>(dag.back().get())->set_host(hosts[0]);
for (const auto& a : dag) {
if (auto* exec = dynamic_cast<sg4::Exec*>(a.get()); exec != nullptr && exec->get_name() != "end") {
exec->set_host(hosts[cursor % count]);
cursor++;
}
if (auto* comm = dynamic_cast<sg4::Comm*>(a.get())) {
const auto* pred = dynamic_cast<sg4::Exec*>((*comm->get_dependencies().begin()).get());
const auto* succ = dynamic_cast<sg4::Exec*>(comm->get_successors().front().get());
comm->set_source(pred->get_host())->set_destination(succ->get_host());
}
}
XBT_INFO("------------------- Run the schedule -------------------------");
e.run();
XBT_INFO("-------------- Summary of executed schedule ------------------");
for (const auto& a : dag) {
if (const auto* exec = dynamic_cast<sg4::Exec*>(a.get())) {
XBT_INFO("[%f->%f] '%s' executed on %s", exec->get_start_time(), exec->get_finish_time(), exec->get_cname(),
exec->get_host()->get_cname());
}
if (const auto* comm = dynamic_cast<sg4::Comm*>(a.get())) {
XBT_INFO("[%f->%f] '%s' transferred from %s to %s", comm->get_start_time(), comm->get_finish_time(),
comm->get_cname(), comm->get_source()->get_cname(), comm->get_destination()->get_cname());
}
}
return 0;
}
View examples/cpp/dag-from-dax/smalldax.xml
<?xml version="1.0" encoding="UTF-8"?>
<adag xmlns="http://pegasus.isi.edu/schema/DAX" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pegasus.isi.edu/schema/DAX http://pegasus.isi.edu/schema/dax-2.1.xsd"
version="2.1" count="1" index="0" name="test" jobCount="3" fileCount="5" childCount="1">
<job id="1" namespace="SG" name="task1" version="1.0" runtime="10">
<uses file="i1" link="input" register="true" transfer="true" optional="false" type="data" size="1000000"/>
<uses file="o1" link="output" register="true" transfer="true" optional="false" type="data" size="1000000"/>
</job>
<job id="2" namespace="SG" name="task2" version="1.0" runtime="10">
<uses file="i2" link="input" register="true" transfer="true" optional="false" type="data" size="1000000"/>
<uses file="o2" link="output" register="true" transfer="true" optional="false" type="data" size="1000000"/>
</job>
<job id="3" namespace="SG" name="task1" version="1.0" runtime="10">
<uses file="o1" link="input" register="true" transfer="true" optional="false" type="data" size="304"/>
<uses file="o2" link="input" register="true" transfer="true" optional="false" type="data" size="304"/>
<uses file="o3" link="output" register="true" transfer="true" optional="false" type="data" size="4167312"/>
</job>
<child ref="3">
<parent ref="1"/>
<parent ref="2"/>
</child>
</adag>
View examples/cpp/dag-from-dot/s4u-dag-from-dot.cpp
/* simple test trying to load a DOT file. */
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <stdio.h>
XBT_LOG_NEW_DEFAULT_CATEGORY(dag_from_dot, "Logging specific to this example");
namespace sg4 = simgrid::s4u;
int main(int argc, char** argv)
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
std::vector<sg4::ActivityPtr> dag = sg4::create_DAG_from_dot(argv[2]);
if (dag.empty()) {
XBT_CRITICAL("No dot loaded. Do you have a cycle in your graph?");
exit(2);
}
XBT_INFO("--------- Display all activities of the loaded DAG -----------");
for (const auto& a : dag) {
std::string type = "an Exec";
std::string task = "flops to execute";
if (dynamic_cast<sg4::Comm*>(a.get()) != nullptr) {
type = "a Comm";
task = "bytes to transfer";
}
XBT_INFO("'%s' is %s: %.0f %s. Dependencies: %s; Ressources: %s", a->get_cname(), type.c_str(), a->get_remaining(),
task.c_str(), (a->dependencies_solved() ? "solved" : "NOT solved"),
(a->is_assigned() ? "assigned" : "NOT assigned"));
}
XBT_INFO("------------------- Schedule tasks ---------------------------");
auto hosts = e.get_all_hosts();
auto count = e.get_host_count();
int cursor = 0;
// Schedule end first
static_cast<sg4::Exec*>(dag.back().get())->set_host(hosts[0]);
for (const auto& a : dag) {
if (auto* exec = dynamic_cast<sg4::Exec*>(a.get()); exec != nullptr && exec->get_name() != "end") {
exec->set_host(hosts[cursor % count]);
cursor++;
}
if (auto* comm = dynamic_cast<sg4::Comm*>(a.get())) {
const auto* pred = dynamic_cast<sg4::Exec*>((*comm->get_dependencies().begin()).get());
const auto* succ = dynamic_cast<sg4::Exec*>(comm->get_successors().front().get());
comm->set_source(pred->get_host())->set_destination(succ->get_host());
}
}
XBT_INFO("------------------- Run the schedule -------------------------");
e.run();
XBT_INFO("-------------- Summary of executed schedule ------------------");
for (const auto& a : dag) {
if (const auto* exec = dynamic_cast<sg4::Exec*>(a.get())) {
XBT_INFO("[%f->%f] '%s' executed on %s", exec->get_start_time(), exec->get_finish_time(), exec->get_cname(),
exec->get_host()->get_cname());
}
if (const auto* comm = dynamic_cast<sg4::Comm*>(a.get())) {
XBT_INFO("[%f->%f] '%s' transferred from %s to %s", comm->get_start_time(), comm->get_finish_time(),
comm->get_cname(), comm->get_source()->get_cname(), comm->get_destination()->get_cname());
}
}
return 0;
}
View examples/cpp/dag-from-dot/dag.dot
digraph G {
end [size="10000000129"];
0 [size="10000000129"];
1 [size="10000000131"];
2 [size="10000000121"];
3 [size="10000000230"];
4 [size="10000000004"];
5 [size="10000000046"];
6 [size="10000000091"];
7 [size="10000000040"];
8 [size="10000000250"];
9 [size="10000000079"];
0->1 [size="10001"];
1->2 [size="10004"];
2->3 [size="10001"];
3->4 [size="-1"];
4->5 [size="10029"];
5->6 [size="0.0"];
6->7 [size="10004"];
7->8 [size="10000"];
8->9 ;
7->end [size="10014000"];
root->5 [size="10014000"];
}
Simulating a time slice
When you declare activities, simgrid::s4u::Engine::run()
runs up to the point of time where an activity completes.
Sometimes, you want to give a maximal duration to simulate up to a given date at most, for example to inject a new activity at that time.
This example shows how to do it.
View examples/cpp/engine-run-partial/s4u-engine-run-partial.cpp
Download s4u-engine-run-partial.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This example is to show how to use Engine::run(date) to simulate only up to that point in time */
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
/* This actor simply executes and waits the activity it got as a parameter. */
static void runner(sg4::ExecPtr activity)
{
activity->start();
activity->wait();
XBT_INFO("Goodbye now!");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Host* fafard = e.host_by_name("Fafard");
sg4::ExecPtr activity = sg4::this_actor::exec_init(fafard->get_speed() * 10.)->set_host(fafard);
sg4::Actor::create("runner", fafard, runner, activity);
while (activity->get_remaining() > 0) {
XBT_INFO("Remaining amount of flops: %g (%.0f%%)", activity->get_remaining(),
100 * activity->get_remaining_ratio());
e.run_until(sg4::Engine::get_clock() + 1);
}
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
DAG and failures
This example shows how to deal with host or network failures while scheduling DAGs of activities.
View examples/cpp/dag-failure/s4u-dag-failure.cpp
/* Copyright (c) 2006-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(dag_failure, "Logging specific to this example");
namespace sg4 = simgrid::s4u;
int main(int argc, char** argv)
{
sg4::Engine e(&argc, argv);
sg4::Engine::set_config("host/model:ptask_L07");
e.load_platform(argv[1]);
auto* faulty = e.host_by_name("Faulty Host");
auto* safe = e.host_by_name("Safe Host");
sg4::Exec::on_completion_cb([](sg4::Exec const& exec) {
if (exec.get_state() == sg4::Activity::State::FINISHED)
XBT_INFO("Activity '%s' is complete (start time: %f, finish time: %f)", exec.get_cname(), exec.get_start_time(),
exec.get_finish_time());
if (exec.get_state() == sg4::Activity::State::FAILED) {
if (exec.is_parallel())
XBT_INFO("Activity '%s' has failed. %.f %% remain to be done", exec.get_cname(),
100 * exec.get_remaining_ratio());
else
XBT_INFO("Activity '%s' has failed. %.f flops remain to be done", exec.get_cname(), exec.get_remaining());
}
});
/* creation of a single Exec that will poorly fail when the workstation will stop */
XBT_INFO("First test: sequential Exec activity");
sg4::ExecPtr exec = sg4::Exec::init()->set_name("Poor task")->set_flops_amount(2e10)->start();
XBT_INFO("Schedule Activity '%s' on 'Faulty Host'", exec->get_cname());
exec->set_host(faulty);
/* Add a child Exec that depends on the Poor task' */
sg4::ExecPtr child = sg4::Exec::init()->set_name("Child")->set_flops_amount(2e10)->set_host(safe);
exec->add_successor(child);
child->start();
XBT_INFO("Run the simulation");
e.run();
XBT_INFO("let's unschedule Activity '%s' and reschedule it on the 'Safe Host'", exec->get_cname());
exec->unset_host();
exec->set_host(safe);
XBT_INFO("Run the simulation again");
e.run();
XBT_INFO("Second test: parallel Exec activity");
exec = sg4::Exec::init()->set_name("Poor parallel task")->set_flops_amounts({2e10, 2e10})->start();
XBT_INFO("Schedule Activity '%s' on 'Safe Host' and 'Faulty Host'", exec->get_cname());
exec->set_hosts({safe, faulty});
/* Add a child Exec that depends on the Poor parallel task' */
child = sg4::Exec::init()->set_name("Child")->set_flops_amount(2e10)->set_host(safe);
exec->add_successor(child);
child->start();
XBT_INFO("Run the simulation");
e.run();
XBT_INFO("let's unschedule Activity '%s' and reschedule it only on the 'Safe Host'", exec->get_cname());
exec->unset_hosts();
exec->set_flops_amount(4e10)->set_host(safe);
XBT_INFO("Run the simulation again");
e.run();
return 0;
}
Classical synchronization objects
Barrier
Shows how to use simgrid::s4u::Barrier
synchronization objects.
View examples/cpp/synchro-barrier/s4u-synchro-barrier.cpp
Download s4u-synchro-barrier.cpp
/* Copyright (c) 2006-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "a sample log category");
namespace sg4 = simgrid::s4u;
/// Wait on the barrier then leave
static void worker(sg4::BarrierPtr barrier)
{
XBT_INFO("Waiting on the barrier");
barrier->wait();
XBT_INFO("Bye");
}
/// Spawn actor_count-1 workers and do a barrier with them
static void master(int actor_count)
{
sg4::BarrierPtr barrier = sg4::Barrier::create(actor_count);
XBT_INFO("Spawning %d workers", actor_count - 1);
for (int i = 0; i < actor_count - 1; i++) {
sg4::Actor::create("worker", sg4::Host::by_name("Jupiter"), worker, barrier);
}
XBT_INFO("Waiting on the barrier");
if (barrier->wait())
XBT_INFO("Bye from the last to enter");
else
XBT_INFO("Bye");
}
int main(int argc, char **argv)
{
sg4::Engine e(&argc, argv);
// Parameter: Number of actores in the barrier
xbt_assert(argc >= 2, "Usage: %s <actor-count>\n", argv[0]);
int actor_count = std::stoi(argv[1]);
xbt_assert(actor_count > 0, "<actor-count> must be greater than 0");
e.load_platform(argc > 2 ? argv[2] : "../../platforms/two_hosts.xml");
sg4::Actor::create("master", e.host_by_name("Tremblay"), master, actor_count);
e.run();
return 0;
}
View examples/python/synchro-barrier/synchro-barrier.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
from argparse import ArgumentParser
import sys
from simgrid import Actor, Barrier, Engine, Host, this_actor
def create_parser() -> ArgumentParser:
parser = ArgumentParser()
parser.add_argument(
'--platform',
type=str,
required=True,
help='path to the platform description'
)
parser.add_argument(
"--actors",
type=int,
required=True,
help="Number of actors to start"
)
return parser
def worker(barrier: Barrier):
""" Wait on the barrier and exits.
:param barrier: Barrier to be awaited
"""
this_actor.info("Waiting on the barrier")
barrier.wait()
this_actor.info("Bye")
def master(actor_count: int):
""" Create barrier with `actor_count` expected actors, spawns `actor_count - 1` workers, then wait on the barrier
and exits.
:param actor_count: Spawn actor_count-1 workers and do a barrier with them
"""
barrier = Barrier(actor_count)
workers_count = actor_count - 1
this_actor.info(f"Spawning {workers_count} workers")
for i in range(workers_count):
Actor.create(f"worker-{i}", Host.by_name("Jupiter"), worker, barrier)
this_actor.info("Waiting on the barrier")
barrier.wait()
this_actor.info("Bye")
def main():
settings = create_parser().parse_known_args()[0]
if settings.actors < 1:
raise ValueError("--actors must be greater than 0")
e = Engine(sys.argv)
e.load_platform(settings.platform)
Actor.create("master", Host.by_name("Tremblay"), master, settings.actors)
e.run()
if __name__ == "__main__":
main()
Condition variable: basic usage
Shows how to use simgrid::s4u::ConditionVariable
synchronization objects.
View examples/cpp/synchro-condition-variable/s4u-synchro-condition-variable.cpp
Download s4u-synchro-condition-variable.cpp
/* Copyright (c) 2006-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <mutex> /* std::mutex and std::scoped_lock */
#include <simgrid/s4u.hpp> /* All of S4U */
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "a sample log category");
namespace sg4 = simgrid::s4u;
static void worker_fun(sg4::ConditionVariablePtr cv, sg4::MutexPtr mutex, std::string& data, bool& done)
{
const std::scoped_lock lock(*mutex);
XBT_INFO("Start processing data which is '%s'.", data.c_str());
data += " after processing";
// Send data back to main()
XBT_INFO("Signal to master that the data processing is completed, and exit.");
done = true;
cv->notify_one();
}
static void master_fun()
{
auto mutex = sg4::Mutex::create();
auto cv = sg4::ConditionVariable::create();
std::string data = "Example data";
bool done = false;
auto worker = sg4::Actor::create("worker", sg4::Host::by_name("Jupiter"), worker_fun, cv, mutex, std::ref(data),
std::ref(done));
// wait for the worker
cv->wait(std::unique_lock(*mutex), [&done]() { return done; });
XBT_INFO("data is now '%s'.", data.c_str());
worker->join();
}
int main(int argc, char** argv)
{
sg4::Engine e(&argc, argv);
e.load_platform("../../platforms/two_hosts.xml");
sg4::Actor::create("main", e.host_by_name("Tremblay"), master_fun);
e.run();
return 0;
}
Condition variable: timeouts
Shows how to specify timeouts when blocking on condition variables.
View examples/cpp/synchro-condition-variable-waituntil/s4u-synchro-condition-variable-waituntil.cpp
Download s4u-synchro-condition-variable-waituntil.cpp
/* Copyright (c) 2006-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <mutex> /* std::mutex and std::scoped_lock */
#include <simgrid/s4u.hpp> /* All of S4U */
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "a sample log category");
namespace sg4 = simgrid::s4u;
static void competitor(int id, sg4::ConditionVariablePtr cv, sg4::MutexPtr mtx, std::shared_ptr<bool> ready)
{
XBT_INFO("Entering the race...");
std::unique_lock lock(*mtx);
while (not *ready) {
auto now = sg4::Engine::get_clock();
if (cv->wait_until(lock, now + (id + 1) * 0.25) == std::cv_status::timeout) {
XBT_INFO("Out of wait_until (timeout)");
} else {
XBT_INFO("Out of wait_until (YAY!)");
}
}
XBT_INFO("Running!");
}
static void go(sg4::ConditionVariablePtr cv, sg4::MutexPtr mtx, std::shared_ptr<bool> ready)
{
XBT_INFO("Are you ready? ...");
sg4::this_actor::sleep_for(3);
const std::scoped_lock lock(*mtx);
XBT_INFO("Go go go!");
*ready = true;
cv->notify_all();
}
static void main_actor()
{
auto mtx = sg4::Mutex::create();
auto cv = sg4::ConditionVariable::create();
auto ready = std::make_shared<bool>(false);
auto* host = sg4::this_actor::get_host();
for (int i = 0; i < 10; ++i)
sg4::Actor::create("competitor", host, competitor, i, cv, mtx, ready);
sg4::Actor::create("go", host, go, cv, mtx, ready);
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform("../../platforms/small_platform.xml");
sg4::Actor::create("main", e.host_by_name("Tremblay"), main_actor);
e.run();
return 0;
}
Mutex
Shows how to use simgrid::s4u::Mutex
synchronization objects.
View examples/cpp/synchro-mutex/s4u-synchro-mutex.cpp
Download s4u-synchro-mutex.cpp
/* Copyright (c) 2006-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp" /* All of S4U */
#include "xbt/config.hpp"
#include <mutex> /* std::mutex and std::scoped_lock */
namespace sg4 = simgrid::s4u;
static simgrid::config::Flag<int> cfg_actor_count("actors", "How many pairs of actors should be started?", 6);
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "a sample log category");
/* This worker uses a classical mutex */
static void worker(sg4::MutexPtr mutex, int& result)
{
// lock the mutex before enter in the critical section
mutex->lock();
XBT_INFO("Hello s4u, I'm ready to compute after a regular lock");
// And finally add it to the results
result += 1;
XBT_INFO("I'm done, good bye");
// You have to unlock the mutex if you locked it manually.
// Beware of exceptions preventing your unlock() from being executed!
mutex->unlock();
}
static void workerScopedLock(sg4::MutexPtr mutex, int& result)
{
// Simply use the std::scoped_lock like this
// It's like a lock() that would do the unlock() automatically when getting out of scope
const std::scoped_lock lock(*mutex);
// then you are in a safe zone
XBT_INFO("Hello s4u, I'm ready to compute after a scoped_lock");
// update the results
result += 1;
XBT_INFO("I'm done, good bye");
// Nothing specific here: the unlock will be automatic
}
int main(int argc, char** argv)
{
sg4::Engine e(&argc, argv);
e.load_platform(argc > 1 ? argv[1] : "../../platforms/two_hosts.xml");
/* Create the requested amount of actors pairs. Each pair has a specific mutex and cell in `result`. */
std::vector<int> result(cfg_actor_count.get());
for (int i = 0; i < cfg_actor_count; i++) {
sg4::MutexPtr mutex = sg4::Mutex::create();
sg4::Actor::create("worker", sg4::Host::by_name("Jupiter"), workerScopedLock, mutex, std::ref(result[i]));
sg4::Actor::create("worker", sg4::Host::by_name("Tremblay"), worker, mutex, std::ref(result[i]));
}
e.run();
for (int i = 0; i < cfg_actor_count; i++)
XBT_INFO("Result[%d] -> %d", i, result[i]);
return 0;
}
View examples/python/synchro-mutex/synchro-mutex.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
from argparse import ArgumentParser
from dataclasses import dataclass
import sys
from simgrid import Actor, Engine, Host, Mutex, this_actor
def create_parser() -> ArgumentParser:
parser = ArgumentParser()
parser.add_argument(
'--platform',
type=str,
required=True,
help='path to the platform description'
)
parser.add_argument(
'--actors',
type=int,
default=6,
help='how many pairs of actors should be started'
)
return parser
@dataclass
class ResultHolder:
value: int
def worker_context_manager(mutex: Mutex, result: ResultHolder):
# When using a context manager, the lock and the unlock are automatic. This is the easiest approach
with mutex:
this_actor.info("Hello simgrid, I'm ready to compute after acquiring the mutex from a context manager")
result.value += 1
this_actor.info("I'm done, good bye")
def worker(mutex: Mutex, result: ResultHolder):
# If you lock your mutex manually, you also have to unlock it.
# Beware of exceptions preventing your unlock() from being executed!
mutex.lock()
this_actor.info("Hello simgrid, I'm ready to compute after a regular lock")
result.value += 1
mutex.unlock()
this_actor.info("I'm done, good bye")
def main():
settings = create_parser().parse_known_args()[0]
e = Engine(sys.argv)
e.load_platform(settings.platform)
# Create the requested amount of actors pairs. Each pair has a specific mutex and cell in `result`
results = [ResultHolder(value=0) for _ in range(settings.actors)]
for i in range(settings.actors):
mutex = Mutex()
Actor.create(f"worker-{i}(mgr)", Host.by_name("Jupiter"), worker_context_manager, mutex, results[i])
Actor.create(f"worker-{i}", Host.by_name("Tremblay"), worker, mutex, results[i])
e.run()
for i in range(settings.actors):
this_actor.info(f"Result[{i}] -> {results[i].value}")
if __name__ == "__main__":
main()
Semaphore
Shows how to use simgrid::s4u::Semaphore
synchronization objects.
View examples/cpp/synchro-semaphore/s4u-synchro-semaphore.cpp
Download s4u-synchro-semaphore.cpp
/* Copyright (c) 2006-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
// This example implements a simple producer/consumer schema,
// passing a bunch of items from one to the other
#include "simgrid/s4u.hpp"
#include <memory>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(sem_test, "Simple test of the semaphore");
static void producer(std::string* buffer, sg4::SemaphorePtr sem_empty, sg4::SemaphorePtr sem_full,
const std::vector<std::string>& args)
{
for (auto const& str : args) {
sem_empty->acquire();
XBT_INFO("Pushing '%s'", str.c_str());
*buffer = str;
sem_full->release();
}
XBT_INFO("Bye!");
}
static void consumer(const std::string* buffer, sg4::SemaphorePtr sem_empty, sg4::SemaphorePtr sem_full)
{
std::string str;
do {
sem_full->acquire();
str = *buffer;
XBT_INFO("Receiving '%s'", str.c_str());
sem_empty->release();
} while (str != "");
XBT_INFO("Bye!");
}
int main(int argc, char **argv)
{
std::vector<std::string> args({"one", "two", "three", ""});
sg4::Engine e(&argc, argv);
e.load_platform(argc > 1 ? argv[1] : "../../platforms/two_hosts.xml");
std::string buffer; /* Where the data is exchanged */
auto sem_empty = sg4::Semaphore::create(1); /* indicates whether the buffer is empty */
auto sem_full = sg4::Semaphore::create(0); /* indicates whether the buffer is full */
sg4::Actor::create("producer", e.host_by_name("Tremblay"), producer, &buffer, sem_empty, sem_full, std::cref(args));
sg4::Actor::create("consumer", e.host_by_name("Jupiter"), consumer, &buffer, sem_empty, sem_full);
e.run();
return 0;
}
View examples/python/synchro-semaphore/synchro-semaphore.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
from typing import List, Optional
from dataclasses import dataclass
from argparse import ArgumentParser
import sys
from simgrid import Actor, Engine, Host, Semaphore, this_actor
@dataclass
class Shared:
buffer: str
END_MARKER = ""
shared = Shared("")
sem_empty = Semaphore(1) # indicates whether the buffer is empty
sem_full = Semaphore(0) # indicates whether the buffer is full
def create_parser():
parser = ArgumentParser()
parser.add_argument(
'--platform',
type=str,
required=True,
help='path to the platform description'
)
parser.add_argument(
'--words',
type=lambda raw_words: raw_words.split(","),
default=["one", "two", "three"],
help='Comma-delimited list of words sent by the producer to the consumer'
)
return parser
def producer(words: List[str]):
this_actor.info("starting consuming")
for word in words + [END_MARKER]:
sem_empty.acquire()
this_actor.sleep_for(1)
this_actor.info(f"Pushing '{word}'")
shared.buffer = word
sem_full.release()
this_actor.info("Bye!")
def consumer():
this_actor.info("starting producing")
word: Optional[str] = None
while word != END_MARKER:
sem_full.acquire()
word = str(shared.buffer)
sem_empty.release()
this_actor.info(f"Receiving '{word}'")
this_actor.info("Bye!")
def main():
settings = create_parser().parse_known_args()[0]
e = Engine(sys.argv)
e.load_platform(settings.platform)
Actor.create("producer", Host.by_name("Tremblay"), producer, settings.words)
Actor.create("consumer", Host.by_name("Jupiter"), consumer)
e.run()
if __name__ == "__main__":
main()
View examples/c/synchro-semaphore/synchro-semaphore.c
/* Copyright (c) 2013-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "simgrid/semaphore.h"
#include "xbt/log.h"
#include "xbt/str.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(semaphores, "Messages specific for this example");
sg_sem_t sem;
static void peer(int argc, char* argv[])
{
int i = 0;
while (i < argc) {
double wait_time = xbt_str_parse_double(argv[i], "Invalid wait time");
i++;
sg_actor_sleep_for(wait_time);
// cover the two cases: with and without timeout
if (i > 1) {
XBT_INFO("Trying for 1 sec to acquire #%d (that is %sfree)", i, sg_sem_would_block(sem) ? "not " : "");
while (sg_sem_acquire_timeout(sem, 1.)) {
XBT_INFO("Timeout.. Try #%d for another second.", i);
}
} else {
XBT_INFO("Acquire #%d (that is %sfree)", i, sg_sem_would_block(sem) ? "not " : "");
sg_sem_acquire(sem);
}
XBT_INFO("Acquired #%d", i);
wait_time = xbt_str_parse_double(argv[i], "Invalid wait time");
i++;
sg_actor_sleep_for(wait_time);
XBT_INFO("Releasing #%d", i);
sg_sem_release(sem);
XBT_INFO("Released #%d", i);
}
sg_actor_sleep_for(50);
XBT_INFO("Done");
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
simgrid_load_platform(argv[1]);
sg_host_t h = sg_host_by_name("Fafard");
sem = sg_sem_init(1);
XBT_INFO("Semaphore initialized with capacity = %d", sg_sem_get_capacity(sem));
const char* aliceTimes[] = {"0", "1", "3", "5", "1", "2", "5", "0"};
const char* bobTimes[] = {"0.9", "1", "1", "2", "2", "0", "0", "5"};
sg_actor_create_("Alice", h, peer, 8, aliceTimes);
sg_actor_create_("Bob", h, peer, 8, bobTimes);
simgrid_run();
sg_sem_destroy(sem);
XBT_INFO("Finished\n");
return 0;
}
Interacting with the Platform
User-defined properties
You can attach arbitrary information to most platform elements from the XML file, and then interact with these values from your program. Note that the changes are not written permanently on disk, in the XML file nor anywhere else. They only last until the end of your simulation.
simgrid::s4u::Actor::get_property()
andsimgrid::s4u::Actor::set_property()
simgrid::s4u::Host::get_property()
andsimgrid::s4u::Host::set_property()
simgrid::s4u::Link::get_property()
andsimgrid::s4u::Link::set_property()
simgrid::s4u::NetZone::get_property()
andsimgrid::s4u::NetZone::set_property()
View examples/cpp/platform-properties/s4u-platform-properties.cpp
Download s4u-platform-properties.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
// TODO: also test the properties attached to links
#include <algorithm>
#include <simgrid/s4u.hpp>
#include <string>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Property test");
namespace sg4 = simgrid::s4u;
static void test_host(const std::string& hostname)
{
sg4::Host* thehost = sg4::Host::by_name(hostname);
const std::unordered_map<std::string, std::string>* hostprops = thehost->get_properties();
const char* noexist = "Unknown";
const char* exist = "Hdd";
const char* value;
XBT_INFO("== Print the properties of the host '%s'", hostname.c_str());
// Sort the properties before displaying them, so that the tests are perfectly reproducible
std::vector<std::string> keys;
for (auto const& [key, _] : *hostprops)
keys.push_back(key);
std::sort(keys.begin(), keys.end());
for (const std::string& key : keys)
XBT_INFO(" Host property: '%s' -> '%s'", key.c_str(), hostprops->at(key).c_str());
XBT_INFO("== Try to get a host property that does not exist");
value = thehost->get_property(noexist);
xbt_assert(not value, "The key exists (it's not supposed to)");
XBT_INFO("== Try to get a host property that does exist");
value = thehost->get_property(exist);
xbt_assert(value, "\tProperty %s is undefined (where it should)", exist);
xbt_assert(strcmp(value, "180") == 0, "\tValue of property %s is defined to %s (where it should be 180)", exist,
value);
XBT_INFO(" Property: %s old value: %s", exist, value);
XBT_INFO("== Trying to modify a host property");
thehost->set_property(exist, "250");
/* Test if we have changed the value */
value = thehost->get_property(exist);
xbt_assert(value, "Property %s is undefined (where it should)", exist);
xbt_assert(strcmp(value, "250") == 0, "Value of property %s is defined to %s (where it should be 250)", exist, value);
XBT_INFO(" Property: %s old value: %s", exist, value);
/* Restore the value for the next test */
thehost->set_property(exist, "180");
const auto* thezone = thehost->get_englobing_zone();
XBT_INFO("== Print the properties of the zone '%s' that contains '%s'", thezone->get_cname(), hostname.c_str());
const std::unordered_map<std::string, std::string>* zoneprops = thezone->get_properties();
keys.clear();
for (auto const& [key, _] : *zoneprops)
keys.push_back(key);
std::sort(keys.begin(), keys.end());
for (const std::string& key : keys)
XBT_INFO(" Zone property: '%s' -> '%s'", key.c_str(), zoneprops->at(key).c_str());
}
static void alice()
{
/* Dump what we have on the current host */
test_host("host1");
}
static void carole()
{
/* Dump what we have on a remote host */
sg4::this_actor::sleep_for(1); // Wait for alice to be done with its experiment
test_host("host1");
}
static void david()
{
/* Dump what we have on a remote host */
sg4::this_actor::sleep_for(2); // Wait for alice and carole to be done with its experiment
test_host("node-0.simgrid.org");
}
static void bob()
{
/* this host also tests the properties of the AS*/
const sg4::NetZone* root = sg4::Engine::get_instance()->get_netzone_root();
XBT_INFO("== Print the properties of the root zone");
XBT_INFO(" Zone property: filename -> %s", root->get_property("filename"));
XBT_INFO(" Zone property: date -> %s", root->get_property("date"));
XBT_INFO(" Zone property: author -> %s", root->get_property("author"));
/* Get the property list of current bob actor */
const std::unordered_map<std::string, std::string>* props = sg4::Actor::self()->get_properties();
const char* noexist = "UnknownProcessProp";
XBT_INFO("== Print the properties of the actor");
for (const auto& [key, value] : *props)
XBT_INFO(" Actor property: %s -> %s", key.c_str(), value.c_str());
XBT_INFO("== Try to get an actor property that does not exist");
const char* value = sg4::Actor::self()->get_property(noexist);
xbt_assert(not value, "The property is defined (it should not)");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
auto* host1 = e.host_by_name("host1");
auto* host2 = e.host_by_name("host2");
size_t totalHosts = e.get_host_count();
XBT_INFO("There are %zu hosts in the environment", totalHosts);
std::vector<sg4::Host*> hosts = e.get_all_hosts();
for (sg4::Host const* host : hosts)
XBT_INFO("Host '%s' runs at %.0f flops/s", host->get_cname(), host->get_speed());
sg4::Actor::create("alice", host1, alice);
sg4::Actor::create("bob", host1, bob)->set_property("SomeProp", "SomeValue");
sg4::Actor::create("carole", host2, carole);
sg4::Actor::create("david", host2, david);
e.run();
return 0;
}
sg_host_get_property_value()
and :cpp:func:sg_host_set_property_value()`sg_zone_get_property_value()
andsg_zone_set_property_value()
View examples/c/platform-properties/platform-properties.c
Download platform-properties.c
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "simgrid/zone.h"
#include "xbt/asserts.h"
#include "xbt/dict.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(test, "Property test");
static void test_host(const char* hostname)
{
sg_host_t thehost = sg_host_by_name(hostname);
xbt_dict_t props = sg_host_get_properties(thehost);
xbt_dict_cursor_t cursor = NULL;
char* key;
char* data;
const char* noexist = "Unknown";
const char* value;
char exist[] = "Hdd";
XBT_INFO("== Print the properties of the host '%s'", hostname);
xbt_dict_foreach (props, cursor, key, data)
XBT_INFO(" Host property: '%s' -> '%s'", key, data);
XBT_INFO("== Try to get a host property that does not exist");
value = sg_host_get_property_value(thehost, noexist);
xbt_assert(!value, "The key exists (it's not supposed to)");
XBT_INFO("== Try to get a host property that does exist");
value = sg_host_get_property_value(thehost, exist);
xbt_assert(value, "\tProperty %s is undefined (where it should)", exist);
xbt_assert(!strcmp(value, "180"), "\tValue of property %s is defined to %s (where it should be 180)", exist, value);
XBT_INFO(" Property: %s old value: %s", exist, value);
XBT_INFO("== Trying to modify a host property");
sg_host_set_property_value(thehost, exist, (char*)"250");
/* Test if we have changed the value */
value = sg_host_get_property_value(thehost, exist);
xbt_assert(value, "Property %s is undefined (where it should)", exist);
xbt_assert(!strcmp(value, "250"), "Value of property %s is defined to %s (where it should be 250)", exist, value);
XBT_INFO(" Property: %s old value: %s", exist, value);
/* Restore the value for the next test */
sg_host_set_property_value(thehost, exist, (char*)"180");
xbt_dict_free(&props);
}
static void alice(int argc, char* argv[])
{ /* Dump what we have on the current host */
test_host("host1");
}
static void carole(int argc, char* argv[])
{ /* Dump what we have on a remote host */
sg_actor_sleep_for(1); // Wait for alice to be done with its experiment
test_host("host1");
}
static void david(int argc, char* argv[])
{ /* Dump what we have on a remote host */
sg_actor_sleep_for(2); // Wait for alice and carole to be done with its experiment
test_host("node-0.simgrid.org");
}
static void bob(int argc, char* argv[])
{
/* this host also tests the properties of the NetZone*/
const_sg_netzone_t root = sg_zone_get_root();
XBT_INFO("== Print the properties of the NetZone");
XBT_INFO(" Actor property: filename -> %s", sg_zone_get_property_value(root, "filename"));
XBT_INFO(" Actor property: date -> %s", sg_zone_get_property_value(root, "date"));
XBT_INFO(" Actor property: author -> %s", sg_zone_get_property_value(root, "author"));
/* Get the property list of current bob actor */
xbt_dict_t props = sg_actor_get_properties(sg_actor_self());
xbt_dict_cursor_t cursor = NULL;
char* key;
char* data;
const char* noexist = "UnknownActorProp";
const char* value;
XBT_INFO("== Print the properties of the actor");
xbt_dict_foreach (props, cursor, key, data)
XBT_INFO(" Actor property: %s -> %s", key, data);
XBT_INFO("== Try to get an actor property that does not exist");
value = sg_actor_get_property_value(sg_actor_self(), noexist);
xbt_assert(!value, "The property is defined (it shouldn't)");
xbt_dict_free(&props);
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 2,
"Usage: %s platform_file deployment_file\n"
"\tExample: %s platform.xml deployment.xml\n",
argv[0], argv[0]);
simgrid_register_function("alice", alice);
simgrid_register_function("bob", bob);
simgrid_register_function("carole", carole);
simgrid_register_function("david", david);
simgrid_load_platform(argv[1]);
size_t host_count = sg_host_count();
XBT_INFO("There are %zu hosts in the environment", host_count);
sg_host_t* hosts = sg_host_list();
for (size_t i = 0; i < host_count; i++)
XBT_INFO("Host '%s' runs at %.0f flops/s", sg_host_get_name(hosts[i]), sg_host_get_speed(hosts[i]));
free(hosts);
simgrid_load_deployment(argv[2]);
simgrid_run();
return 0;
}
Platform file:
View examples/platforms/prop.xml
<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
<platform version="4.1">
<!-- This file describe a super simple platform which main interest is to define some properties on hosts,
actors and links. It is used in several regression cases on properties -->
<zone id="AS0" routing="Full">
<prop id="filename" value="prop.xml"/>
<prop id="date" value="31-08-12"/>
<prop id="author" value="pnavarro"/>
<zone id="AS3" routing="Full">
<zone id="AS1" routing="None">
<prop id="name" value="AS1"/>
</zone>
<zone id="AS2" routing="None">
<prop id="name" value="AS2"/>
</zone>
</zone>
<cluster id="acme" prefix="node-" suffix=".simgrid.org" radical="0-4" speed="1Gf"
bw="125MBps" lat="50us" bb_bw="2.25GBps" bb_lat="500us">
<!-- these props will be attached to the network zone constituting the cluster -->
<prop id="bla" value="acme cluster"/>
<prop id="Hdd" value="180"/>
<prop id="mem" value="42"/>
</cluster>
<zone id="AS4" routing="Full">
<prop id="bla" value="bli"/>
<host id="host1" speed="1Gf">
<prop id="Hdd" value="180"/>
<prop id="mem" value="4"/>
</host>
<host id="host2" speed="1Gf">
<prop id="Hdd" value="120"/>
</host>
<link id="l1" bandwidth="125MBps" latency="100us">
<prop id="type" value="Ethernet"/>
</link>
<link id="l2" bandwidth="125MBps" latency="100us">
<prop id="type" value="ethernet"/>
</link>
<route src="host1" dst="host2">
<link_ctn id="l1"/>
<link_ctn id="l2"/>
</route>
</zone>
</zone>
</platform>
Element filtering
Retrieving the netzones matching given criteria
Shows how to filter the cluster netzones.
View examples/cpp/routing-get-clusters/s4u-routing-get-clusters.cpp
Download s4u-routing-get-clusters.cpp
/* Copyright (c) 2009-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/kernel/routing/ClusterZone.hpp"
#include "simgrid/kernel/routing/DragonflyZone.hpp"
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
std::vector<simgrid::kernel::routing::ClusterZone*> clusters =
e.get_filtered_netzones<simgrid::kernel::routing::ClusterZone>();
for (auto const* c : clusters) {
XBT_INFO("%s", c->get_cname());
std::vector<sg4::Host*> hosts = c->get_all_hosts();
for (auto const* h : hosts)
XBT_INFO(" %s", h->get_cname());
}
std::vector<simgrid::kernel::routing::DragonflyZone*> dragonfly_clusters =
e.get_filtered_netzones<simgrid::kernel::routing::DragonflyZone>();
for (auto const* d : dragonfly_clusters) {
XBT_INFO("%s' dragonfly topology:", d->get_cname());
for (size_t i = 0; i < d->get_host_count(); i++) {
const simgrid::kernel::routing::DragonflyZone::Coords coords = d->rankId_to_coords(i);
XBT_INFO(" %zu: (%lu, %lu, %lu, %lu)", i, coords.group, coords.chassis, coords.blade, coords.node);
}
}
return 0;
}
Retrieving the list of hosts matching given criteria
Shows how to filter the actors that match given criteria.
View examples/cpp/engine-filtering/s4u-engine-filtering.cpp
Download s4u-engine-filtering.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <simgrid/s4u.hpp>
/* This example shows how to use sg4::Engine::get_filtered_hosts() to retrieve
* all hosts that match a given criteria. This criteria can be specified either with:
* - an inlined callback
* - a boolean function, such as filter_speed_more_than_50Mf() below
* - a functor (= function object), that can either be stateless such as filter::SingleCore below, or that can save
* state such as filter::FrequencyChanged below
*
* This file provides examples for each of these categories. You should implement your own filters in your code.
*/
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_engine_filtering, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
namespace filter {
/* First example of thing that we can use as a filtering criteria: a simple boolean function */
static bool filter_speed_more_than_50Mf(const sg4::Host* host)
{
return host->get_speed() > 50E6;
}
/* Second kind of thing that we can use as a filtering criteria: a functor (=function object).
* This one is a bit stupid: it's a lot of boilerplate over a dummy boolean function.
*/
class SingleCore {
public:
bool operator()(const sg4::Host* host) const { return host->get_core_count() == 1; }
};
/* This functor is a bit more complex, as it saves the current state when created.
* Then, it allows one to easily retrieve the hosts which frequency changed since the functor creation.
*/
class FrequencyChanged {
std::map<sg4::Host*, unsigned long> host_list;
public:
explicit FrequencyChanged(const sg4::Engine& e)
{
std::vector<sg4::Host*> list = e.get_all_hosts();
for (auto& host : list) {
host_list.insert({host, host->get_pstate()});
}
}
bool operator()(sg4::Host* host) { return host->get_pstate() != host_list.at(host); }
unsigned long get_old_speed_state(sg4::Host* host) { return host_list.at(host); }
};
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
/* Use a lambda function to filter hosts: We only want multicore hosts */
XBT_INFO("Hosts currently registered with this engine: %zu", e.get_host_count());
std::vector<sg4::Host*> list = e.get_filtered_hosts([](const sg4::Host* host) { return host->get_core_count() > 1; });
for (auto& host : list)
XBT_INFO("The following hosts have more than one core: %s", host->get_cname());
xbt_assert(list.size() == 1);
/* Use a function object (functor) without memory */
list = e.get_filtered_hosts(filter::SingleCore());
for (auto& host : list)
XBT_INFO("The following hosts are SingleCore: %s", host->get_cname());
/* Use a function object that uses memory to filter */
XBT_INFO("A simple example: Let's retrieve all hosts that changed their frequency");
filter::FrequencyChanged filter(e);
e.host_by_name("MyHost2")->set_pstate(2);
list = e.get_filtered_hosts(filter);
for (auto& host : list)
XBT_INFO("The following hosts changed their frequency: %s (from %.1ff to %.1ff)", host->get_cname(),
host->get_pstate_speed(filter.get_old_speed_state(host)), host->get_speed());
/* You can also just use any regular function (namespaced on need) to filter */
list = e.get_filtered_hosts(filter::filter_speed_more_than_50Mf);
for (auto& host : list)
XBT_INFO("The following hosts have a frequency > 50Mf: %s", host->get_cname());
return 0;
}
Profiles
Specifying state profiles
Shows how to specify when the resources must be turned off and on again, and how to react to such failures in your code. See also Modeling churn (e.g., in P2P), this example on how to react to communication failures, and that example on how to react to host failures.
View examples/cpp/platform-failures/s4u-platform-failures.cpp
Download s4u-platform-failures.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This example shows how to work with the state profile of a host or a link,
* specifying when the resource must be turned on or off.
*
* To set such a profile, the first way is to use a file in the XML, while the second is to use the programmatic
* interface, as exemplified in the main() below. Once this profile is in place, the resource will automatically
* be turned on and off.
*
* The actors running on a host that is turned off are forcefully killed
* once their on_exit callbacks are executed. They cannot avoid this fate.
* Since we specified on_failure="RESTART" for each actors in the XML file,
* they will be automatically restarted when the host starts again.
*
* Communications using failed links will .. fail.
*/
#include "simgrid/kernel/ProfileBuilder.hpp"
#include "simgrid/s4u.hpp"
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
static void master(std::vector<std::string> args)
{
xbt_assert(args.size() == 5, "Expecting one parameter");
sg4::Mailbox* mailbox;
long number_of_tasks = std::stol(args[1]);
double comp_size = std::stod(args[2]);
long comm_size = std::stol(args[3]);
long workers_count = std::stol(args[4]);
XBT_INFO("Got %ld workers and %ld tasks to process", workers_count, number_of_tasks);
for (int i = 0; i < number_of_tasks; i++) {
mailbox = sg4::Mailbox::by_name("worker-" + std::to_string(i % workers_count));
auto* payload = new double(comp_size);
try {
XBT_INFO("Send a message to %s", mailbox->get_cname());
mailbox->put(payload, comm_size, 10.0);
XBT_INFO("Send to %s completed", mailbox->get_cname());
} catch (const simgrid::TimeoutException&) {
delete payload;
XBT_INFO("Mmh. Got timeouted while speaking to '%s'. Nevermind. Let's keep going!", mailbox->get_cname());
} catch (const simgrid::NetworkFailureException&) {
delete payload;
XBT_INFO("Mmh. The communication with '%s' failed. Nevermind. Let's keep going!", mailbox->get_cname());
}
}
XBT_INFO("All tasks have been dispatched. Let's tell everybody the computation is over.");
for (int i = 0; i < workers_count; i++) {
/* - Eventually tell all the workers to stop by sending a "finalize" task */
mailbox = sg4::Mailbox::by_name("worker-" + std::to_string(i));
auto* payload = new double(-1.0);
try {
mailbox->put(payload, 0, 1.0);
} catch (const simgrid::TimeoutException&) {
delete payload;
XBT_INFO("Mmh. Got timeouted while speaking to '%s'. Nevermind. Let's keep going!", mailbox->get_cname());
} catch (const simgrid::NetworkFailureException&) {
delete payload;
XBT_INFO("Mmh. Something went wrong with '%s'. Nevermind. Let's keep going!", mailbox->get_cname());
}
}
XBT_INFO("Goodbye now!");
}
static void worker(std::vector<std::string> args)
{
xbt_assert(args.size() == 2, "Expecting one parameter");
long id = std::stol(args[1]);
sg4::Mailbox* mailbox = sg4::Mailbox::by_name("worker-" + std::to_string(id));
while (true) {
try {
XBT_INFO("Waiting a message on %s", mailbox->get_cname());
auto payload = mailbox->get_unique<double>();
xbt_assert(payload != nullptr, "mailbox->get() failed");
double comp_size = *payload;
if (comp_size < 0) { /* - Exit when -1.0 is received */
XBT_INFO("I'm done. See you!");
break;
}
/* - Otherwise, process the task */
XBT_INFO("Start execution...");
sg4::this_actor::execute(comp_size);
XBT_INFO("Execution complete.");
} catch (const simgrid::NetworkFailureException&) {
XBT_INFO("Mmh. Something went wrong. Nevermind. Let's keep going!");
}
}
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
// This is how to attach a profile to an host that is created from the XML file.
// This should be done before calling load_platform(), as the on_creation() event is fired when loading the platform.
// You can never set a new profile to a resource that already have one.
sg4::Host::on_creation_cb([](sg4::Host& h) {
if (h.get_name() == "Bourrassa") {
h.set_state_profile(simgrid::kernel::profile::ProfileBuilder::from_string("bourassa_profile", "67 0\n70 1\n", 0));
}
});
e.load_platform(argv[1]);
e.register_function("master", master);
e.register_function("worker", worker);
e.load_deployment(argv[2]);
// Add a new host programatically, and attach a state profile to it
auto* root = e.get_netzone_root();
auto* lilibeth = root->create_host("Lilibeth", 1e15);
auto link = e.link_by_name("10");
root->add_route(e.host_by_name("Tremblay"), lilibeth, {link});
lilibeth->set_state_profile(simgrid::kernel::profile::ProfileBuilder::from_string("lilibeth_profile", R"(
4 0
5 1
)",
10));
lilibeth->seal();
// Create an actor on that new host, to monitor its own state
auto actor = sg4::Actor::create("sleeper", lilibeth, []() {
XBT_INFO("Start sleeping...");
sg4::this_actor::sleep_for(1);
XBT_INFO("done sleeping.");
});
actor->set_auto_restart(true);
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
View examples/c/platform-failures/platform-failures.c
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/comm.h"
#include "simgrid/engine.h"
#include "simgrid/exec.h"
#include "simgrid/host.h"
#include "simgrid/mailbox.h"
#include "xbt/log.h"
#include "xbt/str.h"
#include "xbt/sysdep.h"
#include <stdio.h> /* snprintf */
#define FINALIZE 221297 /* a magic number to tell people to stop working */
XBT_LOG_NEW_DEFAULT_CATEGORY(platform_failures, "Messages specific for this example");
static void master(int argc, char* argv[])
{
xbt_assert(argc == 5);
long number_of_tasks = xbt_str_parse_int(argv[1], "Invalid amount of tasks");
double task_comp_size = xbt_str_parse_double(argv[2], "Invalid computational size");
long task_comm_size = xbt_str_parse_int(argv[3], "Invalid communication size");
long workers_count = xbt_str_parse_int(argv[4], "Invalid amount of workers");
XBT_INFO("Got %ld workers and %ld tasks to process", workers_count, number_of_tasks);
for (int i = 0; i < number_of_tasks; i++) {
char mailbox_name[256];
snprintf(mailbox_name, 255, "worker-%ld", i % workers_count);
sg_mailbox_t mailbox = sg_mailbox_by_name(mailbox_name);
XBT_INFO("Send a message to %s", mailbox_name);
double* payload = (double*)xbt_malloc(sizeof(double));
*payload = task_comp_size;
sg_comm_t comm = sg_mailbox_put_async(mailbox, payload, task_comm_size);
switch (sg_comm_wait_for(comm, 10.0)) {
case SG_OK:
XBT_INFO("Send to %s completed", mailbox_name);
break;
case SG_ERROR_NETWORK:
XBT_INFO("Mmh. The communication with '%s' failed. Nevermind. Let's keep going!", mailbox_name);
xbt_free(payload);
break;
case SG_ERROR_TIMEOUT:
XBT_INFO("Mmh. Got timeouted while speaking to '%s'. Nevermind. Let's keep going!", mailbox_name);
xbt_free(payload);
break;
default:
xbt_die("Unexpected behavior");
}
}
XBT_INFO("All tasks have been dispatched. Let's tell everybody the computation is over.");
for (int i = 0; i < workers_count; i++) {
char mailbox_name[256];
snprintf(mailbox_name, 255, "worker-%ld", i % workers_count);
sg_mailbox_t mailbox = sg_mailbox_by_name(mailbox_name);
double* payload = (double*)xbt_malloc(sizeof(double));
*payload = FINALIZE;
sg_comm_t comm = sg_mailbox_put_async(mailbox, payload, 0);
switch (sg_comm_wait_for(comm, 1.0)) {
case SG_ERROR_NETWORK:
XBT_INFO("Mmh. Can't reach '%s'! Nevermind. Let's keep going!", mailbox_name);
xbt_free(payload);
break;
case SG_ERROR_TIMEOUT:
XBT_INFO("Mmh. Got timeouted while speaking to '%s'. Nevermind. Let's keep going!", mailbox_name);
xbt_free(payload);
break;
case SG_OK:
/* nothing */
break;
default:
xbt_die("Unexpected behavior with '%s'", mailbox_name);
}
}
XBT_INFO("Goodbye now!");
}
static void worker(int argc, char* argv[])
{
xbt_assert(argc == 2);
char mailbox_name[80];
long id = xbt_str_parse_int(argv[1], "Invalid argument");
snprintf(mailbox_name, 79, "worker-%ld", id);
sg_mailbox_t mailbox = sg_mailbox_by_name(mailbox_name);
while (1) {
XBT_INFO("Waiting a message on %s", mailbox_name);
double* payload;
sg_comm_t comm = sg_mailbox_get_async(mailbox, (void**)&payload);
sg_error_t retcode = sg_comm_wait(comm);
if (retcode == SG_OK) {
if (*payload == FINALIZE) {
xbt_free(payload);
break;
} else {
double comp_size = *payload;
xbt_free(payload);
XBT_INFO("Start execution...");
sg_actor_execute(comp_size);
XBT_INFO("Execution complete.");
}
} else if (retcode == SG_ERROR_NETWORK) {
XBT_INFO("Mmh. Something went wrong. Nevermind. Let's keep going!");
}
}
XBT_INFO("I'm done. See you!");
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 2,
"Usage: %s platform_file deployment_file\n"
"\tExample: %s platform.xml deployment.xml\n",
argv[0], argv[0]);
simgrid_load_platform(argv[1]);
simgrid_register_function("master", master);
simgrid_register_function("worker", worker);
simgrid_load_deployment(argv[2]);
simgrid_run();
XBT_INFO("Simulation time %g", simgrid_get_clock());
return 0;
}
View examples/python/platform-failures/platform-failures.py
## Copyright (c) 2007-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
This example shows how to work with the state profile of a host or a link,
specifying when the resource must be turned on or off.
To set such a profile, the first way is to use a file in the XML, while the second is to use the programmatic
interface, as exemplified in the main() below. Once this profile is in place, the resource will automatically
be turned on and off.
The actors running on a host that is turned off are forcefully killed
once their on_exit callbacks are executed. They cannot avoid this fate.
Since we specified on_failure="RESTART" for each actors in the XML file,
they will be automatically restarted when the host starts again.
Communications using failed links will .. fail.
"""
import sys
from simgrid import Actor, Engine, Host, Mailbox, this_actor, NetworkFailureException, TimeoutException
def master(* args):
assert len(args) == 4, f"Actor master requires 4 parameters, but got {len(args)} ones."
tasks_count = int(args[0])
comp_size = int(args[1])
comm_size = int(args[2])
workers_count = int(args[3])
this_actor.info(f"Got {workers_count} workers and {tasks_count} tasks to process")
for i in range(tasks_count): # For each task to be executed:
# - Select a worker in a round-robin way
mailbox = Mailbox.by_name(f"worker-{i % workers_count}")
try:
this_actor.info(f"Send a message to {mailbox.name}")
mailbox.put(comp_size, comm_size, 10.0)
this_actor.info(f"Send to {mailbox.name} completed")
except TimeoutException:
this_actor.info(f"Mmh. Got timeouted while speaking to '{mailbox.name}'. Nevermind. Let's keep going!")
except NetworkFailureException:
this_actor.info(f"Mmh. The communication with '{mailbox.name}' failed. Nevermind. Let's keep going!")
this_actor.info("All tasks have been dispatched. Let's tell everybody the computation is over.")
for i in range(workers_count):
# - Eventually tell all the workers to stop by sending a "finalize" task
mailbox = Mailbox.by_name(f"worker-{i % workers_count}")
try:
mailbox.put(-1.0, 0, 1.0)
except TimeoutException:
this_actor.info(f"Mmh. Got timeouted while speaking to '{mailbox.name}'. Nevermind. Let's keep going!")
except NetworkFailureException:
this_actor.info(f"Mmh. The communication with '{mailbox.name}' failed. Nevermind. Let's keep going!")
this_actor.info("Goodbye now!")
def worker(* args):
assert len(args) == 1, "Expecting one parameter"
my_id = int(args[0])
mailbox = Mailbox.by_name(f"worker-{my_id}")
done = False
while not done:
try:
this_actor.info(f"Waiting a message on {mailbox.name}")
compute_cost = mailbox.get()
if compute_cost > 0: # If compute_cost is valid, execute a computation of that cost
this_actor.info("Start execution...")
this_actor.execute(compute_cost)
this_actor.info("Execution complete.")
else: # Stop when receiving an invalid compute_cost
this_actor.info("I'm done. See you!")
done = True
except NetworkFailureException:
this_actor.info("Mmh. Something went wrong. Nevermind. Let's keep going!")
def sleeper():
this_actor.info("Start sleeping...")
this_actor.sleep_for(1)
this_actor.info("done sleeping.")
if __name__ == '__main__':
assert len(sys.argv) > 2, "Usage: python app-masterworkers.py platform_file deployment_file"
e = Engine(sys.argv)
# This is how to attach a profile to an host that is created from the XML file.
# This should be done before calling load_platform(), as the on_creation() event is fired when loading the platform.
# You can never set a new profile to a resource that already have one.
def on_creation(host):
if host.name == "Bourrassa":
host.set_state_profile("67 0\n70 1\n", 0)
Host.on_creation_cb(on_creation)
e.load_platform(sys.argv[1])
e.register_actor("master", master)
e.register_actor("worker", worker)
e.load_deployment(sys.argv[2])
# Add a new host programatically, and attach a state profile to it
lili = e.netzone_root.create_host("Lilibeth", 1e15)
lili.set_state_profile("4 0\n5 1\n", 10)
lili.seal()
# Create an actor on that new host, to monitor its own state
actor = Actor.create("sleeper", lili, sleeper)
actor.set_auto_restart(True)
e.run()
this_actor.info(f"Simulation time {e.clock:.4f}")
View examples/platforms/small_platform_failures.xml
Download small_platform_failures.xml
<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
<platform version="4.1">
<zone id="AS0" routing="Full">
<host id="Tremblay" speed="25Mf"/>
<host id="Jupiter" speed="25Mf" state_file="profiles/jupiter_state.profile"/>
<host id="Fafard" speed="25Mf" state_file="profiles/fafard_state.profile" />
<host id="Ginette" speed="25Mf" state_file="profiles/ginette_state.profile"/>
<host id="Bourassa" speed="25Mf"/>
<link id="1" bandwidth="1MBps" latency="0"/>
<link id="2" bandwidth="1MBps" latency="0"/>
<link id="3" bandwidth="1MBps" latency="0" state_file="profiles/link3_state.profile" />
<link id="4" bandwidth="1MBps" latency="0" state_file="profiles/link4_state.profile" />
<link id="5" bandwidth="1MBps" latency="0"/>
<link id="6" bandwidth="1MBps" latency="0"/>
<link id="7" bandwidth="1MBps" latency="0"/>
<link id="8" bandwidth="1MBps" latency="0"/>
<link id="9" bandwidth="1MBps" latency="0"/>
<link id="10" bandwidth="1MBps" latency="0"/>
<link id="loopback_FATPIPE" bandwidth="10MBps" latency="0" sharing_policy="FATPIPE"/>
<link id="loopback" bandwidth="100MBps" latency="0"/>
<route src="Tremblay" dst="Tremblay">
<link_ctn id="loopback"/>
</route>
<route src="Jupiter" dst="Jupiter">
<link_ctn id="loopback"/>
</route>
<route src="Fafard" dst="Fafard">
<link_ctn id="loopback"/>
</route>
<route src="Ginette" dst="Ginette">
<link_ctn id="loopback"/>
</route>
<route src="Bourassa" dst="Bourassa">
<link_ctn id="loopback"/>
</route>
<route src="Tremblay" dst="Jupiter">
<link_ctn id="1"/>
</route>
<route src="Tremblay" dst="Fafard">
<link_ctn id="2"/>
</route>
<route src="Tremblay" dst="Ginette">
<link_ctn id="3"/>
</route>
<route src="Tremblay" dst="Bourassa">
<link_ctn id="4"/>
</route>
<route src="Jupiter" dst="Fafard">
<link_ctn id="5"/>
</route>
<route src="Jupiter" dst="Ginette">
<link_ctn id="6"/>
</route>
<route src="Jupiter" dst="Bourassa">
<link_ctn id="7"/>
</route>
<route src="Fafard" dst="Ginette">
<link_ctn id="8"/>
</route>
<route src="Fafard" dst="Bourassa">
<link_ctn id="9"/>
</route>
<route src="Ginette" dst="Bourassa">
<link_ctn id="10"/>
</route>
</zone>
</platform>
View examples/platforms/profiles/jupiter_state.profile
Download jupiter_state.profile
1 0
2 1
View examples/platforms/profiles/fafard_state.profile
0 0
1 1
2 0
Specifying speed profiles
Shows how to specify an external load to resources, variating their peak speed over time.
View examples/cpp/platform-profile/s4u-platform-profile.cpp
Download s4u-platform-profile.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */ /* This program is free software; you can redistribute it and/or modify it * under the terms of the license (GNU LGPL) which comes with this package. */ #include "simgrid/kernel/ProfileBuilder.hpp" #include "simgrid/s4u.hpp" /* This example demonstrates how to attach a profile to a host or a link, to specify external changes to the resource * speed. The first way to do so is to use a file in the XML, while the second is to use the programmatic interface. */ XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_platform_profile, "Messages specific for this s4u example"); namespace sg4 = simgrid::s4u; static void watcher() { const auto* jupiter = sg4::Host::by_name("Jupiter"); const auto* fafard = sg4::Host::by_name("Fafard"); const auto* lilibeth = sg4::Host::by_name("Lilibeth"); const auto* link1 = sg4::Link::by_name("1"); const auto* link2 = sg4::Link::by_name("2"); std::vector<sg4::Link*> links; double lat = 0; jupiter->route_to(fafard, links, &lat); std::string path; for (const auto* l : links) path += std::string(path.empty() ? "" : ", ") + "link '" + l->get_name() + "'"; XBT_INFO("Path from Jupiter to Fafard: %s (latency: %fs).", path.c_str(), lat); for (int i = 0; i < 10; i++) { XBT_INFO("Fafard: %.0fMflops, Jupiter: %4.0fMflops, Lilibeth: %3.1fMflops, Link1: (%.2fMB/s %.0fms), Link2: " "(%.2fMB/s %.0fms)", fafard->get_speed() * fafard->get_available_speed() / 1000000, jupiter->get_speed() * jupiter->get_available_speed() / 1000000, lilibeth->get_speed() * lilibeth->get_available_speed() / 1000000, link1->get_bandwidth() / 1000, link1->get_latency() * 1000, link2->get_bandwidth() / 1000, link2->get_latency() * 1000); sg4::this_actor::sleep_for(1); } } int main(int argc, char* argv[]) { sg4::Engine e(&argc, argv); xbt_assert(argc > 1, "Usage: %s platform_file\n\tExample: %s platform.xml\n", argv[0], argv[0]); e.load_platform(argv[1]); // Add a new host programmatically, and attach a simple speed profile to it (alternate between full and half speed // every two seconds e.get_netzone_root() ->create_host("Lilibeth", 25e6) ->set_speed_profile(simgrid::kernel::profile::ProfileBuilder::from_string("lilibeth_profile", R"( 0 1.0 2 0.5 )", 4)) ->seal(); // Add a watcher of the changes sg4::Actor::create("watcher", e.host_by_name("Fafard"), watcher); e.run(); return 0; }View examples/python/platform-profile/platform-profile.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of the license (GNU LGPL) which comes with this package. """ This example demonstrates how to attach a profile to a host or a link, to specify external changes to the resource speed. The first way to do so is to use a file in the XML, while the second is to use the programmatic interface. """ import sys from simgrid import Actor, Engine, Host, Link, this_actor def watcher(): jupiter = Host.by_name("Jupiter") fafard = Host.by_name("Fafard") lilibeth = Host.by_name("Lilibeth") link1 = Link.by_name("1") link2 = Link.by_name("2") (links, lat) = jupiter.route_to(fafard) path = "" for l in links: path += ("" if not path else ", ") + "link '" + l.name + "'" this_actor.info(f"Path from Jupiter to Fafard: {path} (latency: {lat:.6f}s).") for _ in range(10): this_actor.info("Fafard: %.0fMflops, Jupiter: %4.0fMflops, Lilibeth: %3.1fMflops, \ Link1: (%.2fMB/s %.0fms), Link2: (%.2fMB/s %.0fms)" % (fafard.speed * fafard.available_speed / 1000000, jupiter.speed * jupiter.available_speed / 1000000, lilibeth.speed * lilibeth.available_speed / 1000000, link1.bandwidth / 1000, link1.latency * 1000, link2.bandwidth / 1000, link2.latency * 1000)) this_actor.sleep_for(1) if __name__ == '__main__': e = Engine(sys.argv) # Load the platform description e.load_platform(sys.argv[1]) # Add a new host programmatically, and attach a simple speed profile to it (alternate between full and half speed # every two seconds lili = e.netzone_root.create_host("Lilibeth", 25e6) lili.set_speed_profile("""0 1.0 2 0.5""", 4) lili.seal() # Add a watcher of the changes Actor.create("watcher", Host.by_name("Fafard"), watcher) e.run()View examples/platforms/small_platform_profile.xml
Download small_platform_profile.xml
<?xml version='1.0'?> <!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd"> <platform version="4.1"> <zone id="AS0" routing="Full"> <host id="Jupiter" speed="25Mf" speed_file="profiles/jupiter_speed.profile" /> <host id="Fafard" speed="25Mf"/> <link id="1" bandwidth="1MBps" latency="10ms" bandwidth_file="profiles/link1_bandwidth.profile" latency_file="profiles/link1_latency.profile" /> <link id="2" bandwidth="1MBps" latency="10ms"/> <route src="Fafard" dst="Jupiter" symmetrical="NO"> <link_ctn id="1"/> </route> <route src="Jupiter" dst="Fafard" symmetrical="NO"> <link_ctn id="1"/> <link_ctn id="2"/> </route> </zone> </platform>View examples/platforms/profiles/jupiter_speed.profile
Download jupiter_speed.profile
0 0.5 2 1.0 4 0.7 6 0.1 8 4 LOOPAFTER 10View examples/platforms/profiles/link1_bandwidth.profile
Download link1_bandwidth.profile
2 2000000 4 3000000 LOOPAFTER 6View examples/platforms/profiles/link1_latency.profile
Download link1_latency.profile
1 0.003 3 0.015 LOOPAFTER 6
Modifying the platform
Serializing communications
This example shows how to limit the amount of communications going through a given link.
It is very similar to the other asynchronous communication examples, but messages get serialized by the platform.
Without this call to Link::set_concurrency_limit(2)
, all messages would be received at the exact same timestamp since
they are initiated at the same instant and are of the same size. But with this extra configuration to the link, at most 2
messages can travel through the link at the same time.
See also simgrid::s4u::Link::set_concurrency_limit()
.
View examples/cpp/platform-comm-serialize/s4u-platform-comm-serialize.cpp
Download s4u-platform-comm-serialize.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This example shows how to serialize a set of communications going through a link using Link::set_concurrency_limit()
*
* This example is very similar to the other asynchronous communication examples, but messages get serialized by the platform.
* Without this call to Link::set_concurrency_limit(2) in main, all messages would be received at the exact same timestamp since
* they are initiated at the same instant and are of the same size. But with this extra configuration to the link, at most 2
* messages can travel through the link at the same time.
*/
#include "simgrid/s4u.hpp"
#include <string>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_async_serialize, "Messages specific for this s4u example");
class Sender {
int messages_count; /* - number of messages */
int msg_size; /* - message size in bytes */
public:
explicit Sender(int size, int count) : messages_count(count), msg_size(size) {}
void operator()() const
{
// sphinx-doc: init-begin (this line helps the doc to build; ignore it)
/* ActivitySet in which we store all ongoing communications */
sg4::ActivitySet pending_comms;
/* Mailbox to use */
sg4::Mailbox* mbox = sg4::Mailbox::by_name("receiver");
// sphinx-doc: init-end
/* Start dispatching all messages to receiver */
for (int i = 0; i < messages_count; i++) {
std::string msg_content = "Message " + std::to_string(i);
// Copy the data we send: the 'msg_content' variable is not a stable storage location.
// It will be destroyed when this actor leaves the loop, ie before the receiver gets it
auto* payload = new std::string(msg_content);
XBT_INFO("Send '%s' to '%s'", msg_content.c_str(), mbox->get_cname());
/* Create a communication representing the ongoing communication, and store it in pending_comms */
sg4::CommPtr comm = mbox->put_async(payload, msg_size);
pending_comms.push(comm);
}
XBT_INFO("Done dispatching all messages");
/* Now that all message exchanges were initiated, wait for their completion in one single call */
pending_comms.wait_all();
// sphinx-doc: put-end
XBT_INFO("Goodbye now!");
}
};
/* Receiver actor expects 1 argument: number of messages to be received */
class Receiver {
sg4::Mailbox* mbox;
int messages_count = 10; /* - number of messages */
public:
explicit Receiver(int count) : messages_count(count) { mbox = sg4::Mailbox::by_name("receiver"); }
void operator()()
{
/* Where we store all incoming msgs */
std::unordered_map<sg4::CommPtr, std::shared_ptr<std::string*>> pending_msgs;
sg4::ActivitySet pending_comms;
XBT_INFO("Wait for %d messages asynchronously", messages_count);
for (int i = 0; i < messages_count; i++) {
auto msg = std::make_shared<std::string*>();
auto comm = mbox->get_async<std::string>(msg.get());
pending_comms.push(comm);
pending_msgs.insert({comm, msg});
}
while (not pending_comms.empty()) {
auto completed_one = pending_comms.wait_any();
if (completed_one != nullptr){
auto comm = boost::dynamic_pointer_cast<sg4::Comm>(completed_one);
auto msg = *pending_msgs[comm];
XBT_INFO("I got '%s'.", msg->c_str());
/* cleanup memory and remove from map */
delete msg;
pending_msgs.erase(comm);
}
}
}
};
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
/* Creates the platform
* ________ __________
* | Sender |===============| Receiver |
* |________| Link1 |__________|
*/
auto* zone = sg4::create_full_zone("Zone1");
auto* sender = zone->create_host("sender", 1)->seal();
auto* receiver = zone->create_host("receiver", 1)->seal();
/* create split-duplex link1 (UP/DOWN), limiting the number of concurrent flows in it for 2 */
const auto* link =
zone->create_split_duplex_link("link1", 10e9)->set_latency(10e-6)->set_concurrency_limit(2)->seal();
/* create routes between nodes */
zone->add_route(sender, receiver, {link});
zone->seal();
/* create actors Sender/Receiver */
sg4::Actor::create("receiver", receiver, Receiver(10));
sg4::Actor::create("sender", sender, Sender(1e6, 10));
e.run();
return 0;
}
See also simgrid.Link.set_concurrency_limit()
.
View examples/python/platform-comm-serialize/platform-comm-serialize.py
Download platform-comm-serialize.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
from typing import List, Tuple
import sys
from simgrid import Engine, Actor, ActivitySet, Comm, Host, LinkInRoute, Mailbox, NetZone, this_actor
RECEIVER_MAILBOX_NAME = "receiver"
class Sender(object):
def __init__(self, message_size: int, messages_count: int):
self.message_size = message_size
self.messages_count = messages_count
def __call__(self) -> None:
# List in which we store all ongoing communications
pending_comms = ActivitySet()
# Make a vector of the mailboxes to use
receiver_mailbox: Mailbox = Mailbox.by_name(RECEIVER_MAILBOX_NAME)
for i in range(self.messages_count):
message_content = f"Message {i}"
this_actor.info(f"Send '{message_content}' to '{receiver_mailbox.name}'")
# Create a communication representing the ongoing communication, and store it in pending_comms
pending_comms.push(receiver_mailbox.put_async(message_content, self.message_size))
this_actor.info("Done dispatching all messages")
# Now that all message exchanges were initiated, wait for their completion in one single call
pending_comms.wait_all()
this_actor.info("Goodbye now!")
class Receiver(object):
def __init__(self, messages_count: int):
self.mailbox: Mailbox = Mailbox.by_name(RECEIVER_MAILBOX_NAME)
self.messages_count = messages_count
def __call__(self):
# List in which we store all incoming msgs
pending_comms = ActivitySet()
this_actor.info(f"Wait for {self.messages_count} messages asynchronously")
for _ in range(self.messages_count):
pending_comms.push(self.mailbox.get_async())
while not pending_comms.empty():
comm = pending_comms.wait_any()
this_actor.info(f"I got '{comm.get_payload()}'.")
def main():
e = Engine(sys.argv)
# Creates the platform
# ________ __________
# | Sender |===============| Receiver |
# |________| Link1 |__________|
#
zone: NetZone = NetZone.create_full_zone("Zone1")
sender_host: Host = zone.create_host("sender", 1).seal()
receiver_host: Host = zone.create_host("receiver", 1).seal()
# create split-duplex link1 (UP/DOWN), limiting the number of concurrent flows in it for 2
link = zone.create_split_duplex_link("link1", 10e9).set_latency(10e-6).set_concurrency_limit(2).seal()
# create routes between nodes
zone.add_route(sender_host, receiver_host, [link])
zone.seal()
# create actors Sender/Receiver
messages_count = 10
Actor.create("receiver", receiver_host, Receiver(messages_count=messages_count))
Actor.create("sender", sender_host, Sender(messages_count=messages_count, message_size=int(1e6)))
e.run()
if __name__ == "__main__":
main()
Energy Simulation
Setup
Describing the energy profiles in the platform
The first platform file contains the energy profile of each link and host for a wired network, which is necessary to get energy consumption predictions. The second platform file is the equivalent for a wireless network. As usual, you should not trust our example, and you should strive to double-check that your instantiation matches your target platform.
View examples/platforms/energy_platform.xml
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
<platform version="4.1">
<zone id="AS0" routing="Full">
<!-- Multiple pstate processor capacities can be defined as a list of powers specified for a given host -->
<!-- Attribute 'pstate' specifies the initially selected pstate (here, the lowest pstate corresponds to the highest
processor speed) -->
<host core="4" id="MyHost1" pstate="0" speed="100.0Mf,50.0Mf,20.0Mf">
<!-- List of Idle:Epsilon:AllCores (in Watts) corresponding to the speed consumed when the processor is idle,
when all cores have a tiny epsilon load, and when all cores are fully loaded -->
<!-- The list must contain one energetic profile for each previously defined pstate-->
<prop id="wattage_per_state" value="100.0:93.33333333333333:200.0, 93.0:90.0:170.0, 90.0:90.0:150.0" />
<prop id="wattage_off" value="10" />
</host>
<host core="1" id="MyHost2" pstate="0" speed="100.0Mf,50.0Mf,20.0Mf">
<!-- This host is mono-core and its consumption is either idle or full load (Epsilon=AllCores) -->
<prop id="wattage_per_state" value="100.0:200.0:200.0, 93.0:170.0:170.0, 90.0:150.0:150.0" />
<prop id="wattage_off" value="10" />
</host>
<host core="1" id="MyHost3" pstate="0" speed="100.0Mf,50.0Mf,20.0Mf">
<!-- This host is mono-core and its consumption is either idle or full load (Epsilon=AllCores) -->
<prop id="wattage_per_state" value="100.0:200.0:200.0, 93.0:170.0:170.0, 90.0:150.0:150.0" />
<prop id="wattage_off" value="10" />
</host>
<link bandwidth="100kBps" id="bus" latency="0" sharing_policy="SHARED">
<!-- REALISTIC VALUES <prop id="wattage_range" value="10.3581:10.7479" /> -->
<!-- IREALISTIC VALUES FOR THE TEST --> <prop id="wattage_range" value="1:3" />
</link>
<route dst="MyHost2" src="MyHost1">
<link_ctn id="bus" />
</route>
<route dst="MyHost3" src="MyHost1">
<link_ctn id="bus" />
</route>
<route dst="MyHost3" src="MyHost2">
<link_ctn id="bus" />
</route>
</zone>
</platform>
View examples/platforms/wifi_energy.xml
<?xml version = '1.0'?>
<!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
<platform version = "4.1">
<zone id="WIFI zone" routing = "Wifi">
<prop id = "access_point" value = "router" />
<host id = "Station 1" speed = "100.0Mf,50.0Mf,20.0Mf" />
<host id = "Station 2" speed = "100.0Mf,50.0Mf,20.0Mf" />
<router id = "router"/>
<link id = "AP1" sharing_policy = "WIFI" bandwidth = "54Mbps" latency="0ms">
<prop id = "wifi_watt_values" value = "0:1:1:0"/>
<prop id = "control_duration" value = "0"/>
</link>
</zone>
</platform>
Usage
CPU energy consumption
This example shows how to retrieve the amount of energy consumed by the CPU during computations, and the impact of the pstate.
View examples/cpp/energy-exec/s4u-energy-exec.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include "simgrid/plugins/energy.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void dvfs()
{
sg4::Host* host1 = sg4::Host::by_name("MyHost1");
sg4::Host* host2 = sg4::Host::by_name("MyHost2");
XBT_INFO("Energetic profile: %s", host1->get_property("wattage_per_state"));
XBT_INFO("Initial peak speed=%.0E flop/s; Energy dissipated =%.0E J", host1->get_speed(),
sg_host_get_consumed_energy(host1));
double start = sg4::Engine::get_clock();
XBT_INFO("Sleep for 10 seconds");
sg4::this_actor::sleep_for(10);
XBT_INFO("Done sleeping (duration: %.2f s). Current peak speed=%.0E; Energy dissipated=%.2f J",
sg4::Engine::get_clock() - start, host1->get_speed(), sg_host_get_consumed_energy(host1));
// Execute something
start = sg4::Engine::get_clock();
double flopAmount = 100E6;
XBT_INFO("Run a computation of %.0E flops", flopAmount);
sg4::this_actor::execute(flopAmount);
XBT_INFO(
"Computation done (duration: %.2f s). Current peak speed=%.0E flop/s; Current consumption: from %.0fW to %.0fW"
" depending on load; Energy dissipated=%.0f J",
sg4::Engine::get_clock() - start, host1->get_speed(), sg_host_get_wattmin_at(host1, host1->get_pstate()),
sg_host_get_wattmax_at(host1, host1->get_pstate()), sg_host_get_consumed_energy(host1));
// ========= Change power peak =========
int pstate = 2;
host1->set_pstate(pstate);
XBT_INFO("========= Requesting pstate %d (speed should be of %.0E flop/s and is of %.0E flop/s)", pstate,
host1->get_pstate_speed(pstate), host1->get_speed());
// Run another computation
start = sg4::Engine::get_clock();
XBT_INFO("Run a computation of %.0E flops", flopAmount);
sg4::this_actor::execute(flopAmount);
XBT_INFO("Computation done (duration: %.2f s). Current peak speed=%.0E flop/s; Energy dissipated=%.0f J",
sg4::Engine::get_clock() - start, host1->get_speed(), sg_host_get_consumed_energy(host1));
start = sg4::Engine::get_clock();
XBT_INFO("Sleep for 4 seconds");
sg4::this_actor::sleep_for(4);
XBT_INFO("Done sleeping (duration: %.2f s). Current peak speed=%.0E flop/s; Energy dissipated=%.0f J",
sg4::Engine::get_clock() - start, host1->get_speed(), sg_host_get_consumed_energy(host1));
// =========== Turn the other host off ==========
XBT_INFO("Turning MyHost2 off, and sleeping another 10 seconds. MyHost2 dissipated %.0f J so far.",
sg_host_get_consumed_energy(host2));
host2->turn_off();
start = sg4::Engine::get_clock();
sg4::this_actor::sleep_for(10);
XBT_INFO("Done sleeping (duration: %.2f s). Current peak speed=%.0E flop/s; Energy dissipated=%.0f J",
sg4::Engine::get_clock() - start, host1->get_speed(), sg_host_get_consumed_energy(host1));
}
int main(int argc, char* argv[])
{
sg_host_energy_plugin_init();
sg4::Engine e(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s ../platforms/energy_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
sg4::Actor::create("dvfs_test", e.host_by_name("MyHost1"), dvfs);
e.run();
XBT_INFO("End of simulation.");
return 0;
}
View examples/c/energy-exec/energy-exec.c
/* Copyright (c) 2007-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "simgrid/plugins/energy.h"
#include "xbt/asserts.h"
#include "xbt/config.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(energy_exec, "Messages specific for this example");
static void dvfs(int argc, char* argv[])
{
sg_host_t host = sg_host_by_name("MyHost1");
XBT_INFO("Energetic profile: %s", sg_host_get_property_value(host, "wattage_per_state"));
XBT_INFO("Initial peak speed=%.0E flop/s; Energy dissipated =%.0E J", sg_host_get_speed(host),
sg_host_get_consumed_energy(host));
double start = simgrid_get_clock();
XBT_INFO("Sleep for 10 seconds");
sg_actor_sleep_for(10);
XBT_INFO("Done sleeping (duration: %.2f s). Current peak speed=%.0E; Energy dissipated=%.2f J",
simgrid_get_clock() - start, sg_host_get_speed(host), sg_host_get_consumed_energy(host));
// Run a task
start = simgrid_get_clock();
XBT_INFO("Run a task of %.0E flops", 100E6);
sg_actor_execute(100E6);
XBT_INFO("Task done (duration: %.2f s). Current peak speed=%.0E flop/s; Current consumption: from %.0fW to %.0fW"
" depending on load; Energy dissipated=%.0f J",
simgrid_get_clock() - start, sg_host_get_speed(host), sg_host_get_wattmin_at(host, sg_host_get_pstate(host)),
sg_host_get_wattmax_at(host, sg_host_get_pstate(host)), sg_host_get_consumed_energy(host));
// ========= Change power peak =========
int pstate = 2;
sg_host_set_pstate(host, pstate);
XBT_INFO("========= Requesting pstate %d (speed should be of %.0E flop/s and is of %.0E flop/s)", pstate,
sg_host_get_pstate_speed(host, pstate), sg_host_get_speed(host));
// Run a second task
start = simgrid_get_clock();
XBT_INFO("Run a task of %.0E flops", 100E6);
sg_actor_execute(100E6);
XBT_INFO("Task done (duration: %.2f s). Current peak speed=%.0E flop/s; Energy dissipated=%.0f J",
simgrid_get_clock() - start, sg_host_get_speed(host), sg_host_get_consumed_energy(host));
start = simgrid_get_clock();
XBT_INFO("Sleep for 4 seconds");
sg_actor_sleep_for(4);
XBT_INFO("Done sleeping (duration: %.2f s). Current peak speed=%.0E flop/s; Energy dissipated=%.0f J",
simgrid_get_clock() - start, sg_host_get_speed(host), sg_host_get_consumed_energy(host));
// =========== Turn the other host off ==========
XBT_INFO("Turning MyHost2 off, and sleeping another 10 seconds. MyHost2 dissipated %.0f J so far.",
sg_host_get_consumed_energy(sg_host_by_name("MyHost2")));
sg_host_turn_off(sg_host_by_name("MyHost2"));
start = simgrid_get_clock();
sg_actor_sleep_for(10);
XBT_INFO("Done sleeping (duration: %.2f s). Current peak speed=%.0E flop/s; Energy dissipated=%.0f J",
simgrid_get_clock() - start, sg_host_get_speed(host), sg_host_get_consumed_energy(host));
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
sg_cfg_set_string("plugin", "host_energy");
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s platform.xml\n", argv[0], argv[0]);
simgrid_load_platform(argv[1]);
sg_actor_create("dvfs_test", sg_host_by_name("MyHost1"), &dvfs, 0, NULL);
simgrid_run();
XBT_INFO("End of simulation");
return 0;
}
Virtual machines consumption
This example is very similar to the previous one, adding VMs to the picture.
View examples/cpp/energy-vm/s4u-energy-vm.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include "simgrid/plugins/energy.h"
#include "simgrid/s4u/VirtualMachine.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(energy_vm, "Messages of this example");
namespace sg4 = simgrid::s4u;
static void executor()
{
sg4::this_actor::execute(300E6);
XBT_INFO("This worker is done.");
}
static void dvfs()
{
sg4::Host* host1 = sg4::Host::by_name("MyHost1");
sg4::Host* host2 = sg4::Host::by_name("MyHost2");
sg4::Host* host3 = sg4::Host::by_name("MyHost3");
/* Host 1 */
XBT_INFO("Creating and starting two VMs");
auto* vm_host1 = host1->create_vm("vm_host1", 1);
vm_host1->start();
auto* vm_host2 = host2->create_vm("vm_host2", 1);
vm_host2->start();
XBT_INFO("Create two activities on Host1: both inside a VM");
sg4::Actor::create("p11", vm_host1, executor);
sg4::Actor::create("p12", vm_host1, executor);
XBT_INFO("Create two activities on Host2: one inside a VM, the other directly on the host");
sg4::Actor::create("p21", vm_host2, executor);
sg4::Actor::create("p22", host2, executor);
XBT_INFO("Create two activities on Host3: both directly on the host");
sg4::Actor::create("p31", host3, executor);
sg4::Actor::create("p32", host3, executor);
XBT_INFO("Wait 5 seconds. The activities are still running (they run for 3 seconds, but 2 activities are co-located, "
"so they run for 6 seconds)");
sg4::this_actor::sleep_for(5);
XBT_INFO("Wait another 5 seconds. The activities stop at some point in between");
sg4::this_actor::sleep_for(5);
vm_host1->destroy();
vm_host2->destroy();
}
int main(int argc, char* argv[])
{
sg_host_energy_plugin_init();
sg4::Engine e(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform_file\n\tExample: %s ../platforms/energy_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
sg4::Actor::create("dvfs", e.host_by_name("MyHost1"), dvfs);
e.run();
XBT_INFO("Total simulation time: %.2f; Host2 and Host3 must have the exact same energy consumption; Host1 is "
"multi-core and will differ.",
sg4::Engine::get_clock());
return 0;
}
View examples/c/energy-vm/energy-vm.c
/* Copyright (c) 2007-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "simgrid/plugins/energy.h"
#include "simgrid/vm.h"
#include "xbt/asserts.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(energy_vm, "Messages of this example");
static void worker_func(int argc, char* argv[])
{
sg_actor_execute(300E6);
XBT_INFO("This worker is done.");
}
static void dvfs(int argc, char* argv[])
{
sg_host_t host1 = sg_host_by_name("MyHost1");
sg_host_t host2 = sg_host_by_name("MyHost2");
sg_host_t host3 = sg_host_by_name("MyHost3");
/* Host 1 */
XBT_INFO("Creating and starting two VMs");
sg_vm_t vm_host1 = sg_vm_create_core(host1, "vm_host1");
sg_vm_start(vm_host1);
sg_vm_t vm_host2 = sg_vm_create_core(host2, "vm_host2");
sg_vm_start(vm_host2);
XBT_INFO("Create two tasks on Host1: both inside a VM");
sg_actor_create("p11", (sg_host_t)vm_host1, &worker_func, 0, NULL);
sg_actor_create("p12", (sg_host_t)vm_host1, &worker_func, 0, NULL);
XBT_INFO("Create two tasks on Host2: one inside a VM, the other directly on the host");
sg_actor_create("p21", (sg_host_t)vm_host2, &worker_func, 0, NULL);
sg_actor_create("p22", host2, &worker_func, 0, NULL);
XBT_INFO("Create two tasks on Host3: both directly on the host");
sg_actor_create("p31", host3, &worker_func, 0, NULL);
sg_actor_create("p32", host3, &worker_func, 0, NULL);
XBT_INFO("Wait 5 seconds. The tasks are still running (they run for 3 seconds, but 2 tasks are co-located, "
"so they run for 6 seconds)");
sg_actor_sleep_for(5);
XBT_INFO("Wait another 5 seconds. The tasks stop at some point in between");
sg_actor_sleep_for(5);
sg_vm_destroy(vm_host1);
sg_vm_destroy(vm_host2);
}
int main(int argc, char* argv[])
{
sg_host_energy_plugin_init();
simgrid_init(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform_file\n\tExample: %s platform.xml\n", argv[0], argv[0]);
simgrid_load_platform(argv[1]);
sg_actor_create("dvfs", sg_host_by_name("MyHost1"), &dvfs, 0, NULL);
simgrid_run();
XBT_INFO("Total simulation time: %.2f; Host2 and Host3 must have the exact same energy consumption; Host1 is "
"multi-core and will differ.",
simgrid_get_clock());
return 0;
}
Wired network energy consumption
This example shows how to retrieve and display the energy consumed by the wired network during communications.
View examples/cpp/energy-link/s4u-energy-link.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/plugins/energy.h"
#include "xbt/log.h"
#include "xbt/random.hpp"
#include <simgrid/s4u.hpp>
/* Parameters of the random generation of the flow size */
static const int min_size = 1e6;
static const int max_size = 1e9;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_app_energyconsumption, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void sender(std::vector<std::string> args)
{
xbt_assert(args.size() == 2, "The master function expects 2 arguments.");
int flow_amount = std::stoi(args.at(0));
long comm_size = std::stol(args.at(1));
XBT_INFO("Send %ld bytes, in %d flows", comm_size, flow_amount);
sg4::Mailbox* mailbox = sg4::Mailbox::by_name("message");
/* Sleep a while before starting the example */
sg4::this_actor::sleep_for(10);
if (flow_amount == 1) {
/* - Send the task to the @ref worker */
char* payload = bprintf("%ld", comm_size);
mailbox->put(payload, comm_size);
} else {
// Start all comms in parallel, and wait for all completions in one shot
sg4::ActivitySet comms;
for (int i = 0; i < flow_amount; i++)
comms.push(mailbox->put_async(bprintf("%d", i), comm_size));
comms.wait_all();
}
XBT_INFO("sender done.");
}
static void receiver(std::vector<std::string> args)
{
int flow_amount = std::stoi(args.at(0));
XBT_INFO("Receiving %d flows ...", flow_amount);
sg4::Mailbox* mailbox = sg4::Mailbox::by_name("message");
if (flow_amount == 1) {
char* res = mailbox->get<char>();
xbt_free(res);
} else {
std::vector<char*> data(flow_amount);
// Start all comms in parallel, and wait for their completion in one shot
sg4::ActivitySet comms;
for (int i = 0; i < flow_amount; i++)
comms.push(mailbox->get_async<char>(&data[i]));
comms.wait_all();
for (int i = 0; i < flow_amount; i++)
xbt_free(data[i]);
}
XBT_INFO("receiver done.");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
XBT_INFO("Activating the SimGrid link energy plugin");
sg_link_energy_plugin_init();
xbt_assert(argc > 1, "\nUsage: %s platform_file [flowCount [datasize]]\n"
"\tExample: %s s4uplatform.xml \n",
argv[0], argv[0]);
e.load_platform(argv[1]);
/* prepare to launch the actors */
std::vector<std::string> argSender;
std::vector<std::string> argReceiver;
if (argc > 2) {
argSender.emplace_back(argv[2]); // Take the amount of flows from the command line
argReceiver.emplace_back(argv[2]);
} else {
argSender.emplace_back("1"); // Default value
argReceiver.emplace_back("1");
}
if (argc > 3) {
if (strcmp(argv[3], "random") == 0) { // We're asked to get a random size
std::string size = std::to_string(simgrid::xbt::random::uniform_int(min_size, max_size));
argSender.push_back(size);
} else { // Not "random" ? Then it should be the size to use
argSender.emplace_back(argv[3]); // Take the datasize from the command line
}
} else { // No parameter at all? Then use the default value
argSender.emplace_back("25000");
}
sg4::Actor::create("sender", e.host_by_name("MyHost1"), sender, argSender);
sg4::Actor::create("receiver", e.host_by_name("MyHost2"), receiver, argReceiver);
/* And now, launch the simulation */
e.run();
return 0;
}
WiFi network energy consumption
This example shows how to retrieve and display the energy consumed by the wireless network during communications.
View examples/cpp/energy-wifi/s4u-energy-wifi.cpp
/* Copyright (c) 2020-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/**
* Test the wifi energy plugin
* Desactivate cross-factor to get round values
* Launch with: ./test test_platform_2STA.xml --cfg=plugin:link_energy_wifi --cfg=network/crosstraffic:0
*/
#include "simgrid/plugins/energy.h"
#include "simgrid/s4u/Activity.hpp"
#include "simgrid/s4u/Actor.hpp"
#include "simgrid/s4u/Engine.hpp"
#include "simgrid/s4u/Host.hpp"
#include "simgrid/s4u/Link.hpp"
#include "simgrid/s4u/Mailbox.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(test_wifi, "Wifi energy demo");
namespace sg4 = simgrid::s4u;
static void sender()
{
// start sending after 5 seconds
sg4::this_actor::sleep_until(5);
std::string mbName = "MailBoxRCV";
sg4::Mailbox* dst = sg4::Mailbox::by_name(mbName);
int size = 6750000;
XBT_INFO("SENDING 1 msg of size %d to %s", size, mbName.c_str());
static std::string message = "message";
dst->put(&message, size);
XBT_INFO("finished sending");
}
static void receiver()
{
std::string mbName = "MailBoxRCV";
XBT_INFO("RECEIVING on mb %s", mbName.c_str());
sg4::Mailbox* myBox = sg4::Mailbox::by_name(mbName);
myBox->get<std::string>();
XBT_INFO("received all messages");
}
int main(int argc, char** argv)
{
sg4::Engine engine(&argc, argv);
sg_wifi_energy_plugin_init();
engine.load_platform(argv[1]);
// setup WiFi bandwidths
const auto* l = engine.link_by_name("AP1");
l->set_host_wifi_rate(engine.host_by_name("Station 1"), 0);
l->set_host_wifi_rate(engine.host_by_name("Station 2"), 0);
// create the two actors for the test
sg4::Actor::create("act0", engine.host_by_name("Station 1"), sender);
sg4::Actor::create("act1", engine.host_by_name("Station 2"), receiver);
engine.run();
return 0;
}
Modeling the shutdown and boot of hosts
Simple example of a model for the energy consumption during the host boot and shutdown periods.
View examples/platforms/energy_boot.xml
<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
<platform version="4.1">
<zone id="AS0" routing="Full">
<!-- Use the pstate mechanism to encode the boot/shutdown time and energy.
-
- See the C++ file in the same directory for more information.
-->
<!-- pstate values:
* 0-2: real pstates
0: p1 100 MFlops/s, [idle: 95W -> full burning: 200W]
1: p2 50 MFlops/s, [idle: 93W -> full burning: 170W]
2: p3 20 MFlops/s, [idel: 90W -> full burning: 150W]
* 3: booting up was measured to take 150s and 18000J.
So we create a pstate 3 consuming 18000J/150s=120W, and a boot remains at this pstate for 150s.
Speed is set at 0 flop/s so that nothing progresses during the boot.
* 4: shutting down was measured to take 7 s and 770 J
So we create a pstate 4 consuming 770J/7s=110W, and a shutdown remains at this pstate for 7s.
Please note that even if these values are educated guesses, you should still challenge them.
If you want a realistic simulation, you must use values coming from a real benchmark of your platform.
-->
<host id="MyHost1" speed="100.0Mf,50.0Mf,20.0Mf, 0f,0f" pstate="0" >
<prop id="wattage_per_state" value="95.0:200.0,93.0:170.0,90.0:150.0, 120:120,110:110" />
<prop id="wattage_off" value="10" />
</host>
<host id="MyHost2" speed="100.0Mf" >
<prop id="wattage_per_state" value="100.0:200.0" />
<prop id="wattage_off" value="10" />
</host>
<link id="link1" bandwidth="100kBps" latency="0"/>
<route src="MyHost1" dst="MyHost2">
<link_ctn id="link1"/>
</route>
</zone>
</platform>
View examples/cpp/energy-boot/s4u-energy-boot.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This is an example of how the bootup and shutdown periods can be modeled
* with SimGrid, taking both the time and overall consumption into account.
*
* The main idea is to augment the platform description to declare fake
* pstate that represent these states. The CPU speed of these state is zero
* (the CPU delivers 0 flop per second when booting) while the energy
* consumption is the one measured on average on the modeled machine.
*
* When you want to bootup the machine, you set it into the pstate encoding
* the boot (3 in this example), and leave it so for the right time using a
* sleep_for(). During that time no other execution can progress since the
* resource speed is set at 0 flop/s in this fake pstate. Once this is over,
* the boot is done and we switch back to the regular pstate. Conversely,
* the fake pstate 4 is used to encode the shutdown delay.
*
* Some people don't like the idea to add fake pstates for the boot time, and
* would like SimGrid to provide a "cleaner" model for that. But the "right"
* model depends on the study you want to conduct. If you want to study the
* instantaneous consumption of a rebooting data center, the model used here
* is not enough since it considers only the average consumption over the boot,
* while the instantaneous consumption changes dramatically. Conversely, a
* model taking the instantaneous changes into account will be very difficult
* to instantiate correctly (which values will you use?), so it's not adapted
* to most studies. At least, fake pstates allow you to do exactly what you
* need for your very study.
*/
#include "simgrid/s4u.hpp"
#include "simgrid/plugins/energy.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this example");
namespace sg4 = simgrid::s4u;
static void simulate_bootup(sg4::Host* host)
{
unsigned long previous_pstate = host->get_pstate();
XBT_INFO("Switch to virtual pstate 3, that encodes the 'booting up' state in that platform");
host->set_pstate(3);
XBT_INFO("Actually start the host");
host->turn_on();
XBT_INFO("Wait 150s to simulate the boot time.");
sg4::this_actor::sleep_for(150);
XBT_INFO("The host is now up and running. Switch back to previous pstate %lu", previous_pstate);
host->set_pstate(previous_pstate);
}
static void simulate_shutdown(sg4::Host* host)
{
unsigned long previous_pstate = host->get_pstate();
XBT_INFO("Switch to virtual pstate 4, that encodes the 'shutting down' state in that platform");
host->set_pstate(4);
XBT_INFO("Wait 7 seconds to simulate the shutdown time.");
sg4::this_actor::sleep_for(7);
XBT_INFO("Switch back to previous pstate %lu, that will be used on reboot.", previous_pstate);
host->set_pstate(previous_pstate);
XBT_INFO("Actually shutdown the host");
host->turn_off();
}
static void monitor()
{
sg4::Host* host1 = sg4::Host::by_name("MyHost1");
XBT_INFO("Initial pstate: %lu; Energy dissipated so far:%.0E J", host1->get_pstate(),
sg_host_get_consumed_energy(host1));
XBT_INFO("Sleep for 10 seconds");
sg4::this_actor::sleep_for(10);
XBT_INFO("Done sleeping. Current pstate: %lu; Energy dissipated so far: %.2f J", host1->get_pstate(),
sg_host_get_consumed_energy(host1));
simulate_shutdown(host1);
XBT_INFO("Host1 is now OFF. Current pstate: %lu; Energy dissipated so far: %.2f J", host1->get_pstate(),
sg_host_get_consumed_energy(host1));
XBT_INFO("Sleep for 10 seconds");
sg4::this_actor::sleep_for(10);
XBT_INFO("Done sleeping. Current pstate: %lu; Energy dissipated so far: %.2f J", host1->get_pstate(),
sg_host_get_consumed_energy(host1));
simulate_bootup(host1);
XBT_INFO("Host1 is now ON again. Current pstate: %lu; Energy dissipated so far: %.2f J", host1->get_pstate(),
sg_host_get_consumed_energy(host1));
}
int main(int argc, char* argv[])
{
sg_host_energy_plugin_init();
sg4::Engine e(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
sg4::Actor::create("Boot Monitor", e.host_by_name("MyHost2"), monitor);
e.run();
XBT_INFO("End of simulation.");
return 0;
}
Tracing and Visualizing
Tracing can be activated by various configuration options which are illustrated in these examples. See also the full list of options related to tracing. The following introduces some option sets of interest that you may want to pass to your simulators.
Todo
These tracing examples should be integrated in the examples to not duplicate the C++ files. A full command line to see the result in the right tool (vite/FrameSoc) should be given along with some screenshots.
Platform Tracing
Basic example
This program is a toy example just loading the platform so that you can play with the platform visualization. Recommended options:
--cfg=tracing:yes --cfg=tracing/categorized:yes
View examples/cpp/trace-platform/s4u-trace-platform.cpp
Download s4u-trace-platform.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This source code simply loads the platform. This is only useful to play
* with the tracing module. See the tesh file to see how to generate the
* traces.
*/
#include "simgrid/s4u.hpp"
int main(int argc, char* argv[])
{
simgrid::s4u::Engine e(&argc, argv);
e.load_platform(argv[1]);
e.run();
return 0;
}
Setting Categories
This example declares several tracing categories that are used to
classify its tasks. When the program is executed, the tracing mechanism
registers the resource utilization of hosts and links according to these
categories. Recommended options:
--cfg=tracing:yes --cfg=tracing/categorized:yes --cfg=tracing/uncategorized:yes
View examples/cpp/trace-categories/s4u-trace-categories.cpp
Download s4u-trace-categories.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This source code simply loads the platform. This is only useful to play
* with the tracing module. See the tesh file to see how to generate the
* traces.
*/
#include "simgrid/instr.h"
#include "simgrid/s4u.hpp"
namespace sg4 = simgrid::s4u;
struct Task {
std::string name;
std::string category;
double flops;
uint64_t bytes;
};
static void master()
{
auto* mbox = sg4::Mailbox::by_name("master_mailbox");
for (int i = 0; i < 10; i++) {
Task task;
if (i % 2)
task = {"task_compute", "compute", 10000000, 0};
else if (i % 3)
task = {"task_request", "request", 10, 10};
else
task = {"task_data", "data", 10, 10000000};
mbox->put(new Task(task), task.bytes);
}
Task finalize = {"finalize", "finalize", 0, 1000};
mbox->put(new Task(finalize), finalize.bytes);
}
static void worker()
{
auto* mbox = sg4::Mailbox::by_name("master_mailbox");
while (true) {
auto task = mbox->get_unique<Task>();
if (task->name == "finalize") {
break;
}
// creating task and setting its category
sg4::this_actor::exec_init(task->flops)->set_name(task->name)->set_tracing_category(task->category)->wait();
}
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform_file\n \tExample: %s small_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
// declaring user categories with RGB colors
simgrid::instr::declare_tracing_category("compute", "1 0 0"); // red
simgrid::instr::declare_tracing_category("request", "0 1 0"); // green
simgrid::instr::declare_tracing_category("data", "0 0 1"); // blue
simgrid::instr::declare_tracing_category("finalize", "0 0 0"); // black
sg4::Actor::create("master", e.host_by_name("Tremblay"), master);
sg4::Actor::create("worker", e.host_by_name("Fafard"), worker);
e.run();
return 0;
}
Master Workers tracing
This is an augmented version of our basic master/worker example using
several tracing features. It traces resource usage, sorted out in several
categories; Trace marks and user variables are also used. Recommended
options: --cfg=tracing/categorized:yes --cfg=tracing/uncategorized:yes
View examples/cpp/trace-masterworkers/s4u-trace-masterworkers.cpp
Download s4u-trace-masterworkers.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <simgrid/instr.h>
#include <simgrid/s4u.hpp>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_trace_masterworker, "Messages specific for this example");
namespace sg4 = simgrid::s4u;
struct Task {
std::string name;
std::string category;
double flops;
};
static void master(std::vector<std::string> args)
{
xbt_assert(args.size() > 4, "The master function expects at least 3 arguments");
long tasks_count = std::stol(args[1]);
double compute_cost = std::stod(args[2]);
long communication_cost = std::stol(args[3]);
size_t workers_count = args.size() - 4;
const auto& my_host = sg4::this_actor::get_host()->get_name();
auto* mailbox = sg4::Mailbox::by_name("master_mailbox");
XBT_DEBUG("Got %zu workers and %ld tasks to process", workers_count, tasks_count);
// setting the variable "is_master" (previously declared) to value 1
simgrid::instr::set_host_variable(my_host, "is_master", 1);
simgrid::instr::mark("msmark", "start_send_tasks");
for (int i = 0; i < tasks_count; i++) {
// setting the variable "task_creation" to value i
simgrid::instr::set_host_variable(my_host, "task_creation", i);
// setting the category of task to "compute"
Task task = {"task", "compute", compute_cost};
mailbox->put(new Task(task), communication_cost);
}
simgrid::instr::mark("msmark", "finish_send_tasks");
XBT_DEBUG("All tasks have been dispatched. Request all workers to stop.");
for (unsigned int i = 0; i < workers_count; i++) {
Task finalize = {"finalize", "finalize", 0};
mailbox->put(new Task(finalize), 0);
}
}
static void worker(std::vector<std::string> args)
{
xbt_assert(args.size() == 1, "The worker expects no argument");
const auto& my_host = sg4::this_actor::get_host()->get_name();
auto* mailbox = sg4::Mailbox::by_name("master_mailbox");
simgrid::instr::set_host_variable(my_host, "is_worker", 1);
simgrid::instr::set_host_variable(my_host, "task_computation", 0);
while (true) {
auto task = mailbox->get_unique<Task>();
if (task->name == "finalize") {
break;
}
// adding the task's cost to the variable "task_computation"
simgrid::instr::add_host_variable(my_host, "task_computation", task->flops);
sg4::this_actor::exec_init(task->flops)->set_name(task->name)->set_tracing_category(task->category)->wait();
}
XBT_DEBUG("Exiting now.");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 2, "Usage: %s platform_file deployment_file\n", argv[0]);
e.load_platform(argv[1]);
// declaring user variables
simgrid::instr::declare_host_variable("is_worker");
simgrid::instr::declare_host_variable("is_master");
simgrid::instr::declare_host_variable("task_creation");
simgrid::instr::declare_host_variable("task_computation");
// declaring user markers and values
simgrid::instr::declare_mark("msmark");
simgrid::instr::declare_mark_value("msmark", "start_send_tasks");
simgrid::instr::declare_mark_value("msmark", "finish_send_tasks");
// declaring user categories with RGB colors (values from 0 to 1)
simgrid::instr::declare_tracing_category("compute", "1 0 0"); // compute is red
simgrid::instr::declare_tracing_category("finalize", "0 1 0"); // finalize is green
// categories without user-defined colors receive random colors generated by the tracing system
simgrid::instr::declare_tracing_category("request");
simgrid::instr::declare_tracing_category("report");
e.register_function("master", &master);
e.register_function("worker", &worker);
e.load_deployment(argv[2]);
e.run();
XBT_DEBUG("Simulation is over");
if (const auto& categories = simgrid::instr::get_tracing_categories(); not categories.empty()) {
XBT_INFO("Declared tracing categories:");
for (const auto& category : categories)
XBT_INFO("%s", category.c_str());
}
if (const auto& marks = simgrid::instr::get_marks(); not marks.empty()) {
XBT_INFO("Declared marks:");
for (const auto& mark : marks)
XBT_INFO("%s", mark.c_str());
}
return 0;
}
View examples/python/app-masterworkers/app-masterworkers.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
# ##################################################################################
# Take this tutorial online: https://simgrid.org/doc/latest/Tutorial_Algorithms.html
# ##################################################################################
"""
import sys
from simgrid import Engine, Mailbox, this_actor
# master-begin
def master(*args):
assert len(args) > 3, f"Actor master requires 3 parameters plus the workers' names, but got {len(args)}"
tasks_count = int(args[0])
compute_cost = int(args[1])
communicate_cost = int(args[2])
workers = []
for i in range(3, len(args)):
workers.append(Mailbox.by_name(args[i]))
this_actor.info(f"Got {len(workers)} workers and {tasks_count} tasks to process")
for i in range(tasks_count): # For each task to be executed:
# - Select a worker in a round-robin way
mailbox = workers[i % len(workers)]
# - Send the computation amount to the worker
if (tasks_count < 10000 or (tasks_count < 100000 and i % 10000 == 0) or i % 100000 == 0):
this_actor.info(f"Sending task {i} of {tasks_count} to mailbox '{mailbox.name}'")
mailbox.put(compute_cost, communicate_cost)
this_actor.info("All tasks have been dispatched. Request all workers to stop.")
for mailbox in workers:
# The workers stop when receiving a negative compute_cost
mailbox.put(-1, 0)
# master-end
# worker-begin
def worker(*args):
assert not args, "The worker expects to not get any argument"
mailbox = Mailbox.by_name(this_actor.get_host().name)
done = False
while not done:
compute_cost = mailbox.get()
if compute_cost > 0: # If compute_cost is valid, execute a computation of that cost
this_actor.execute(compute_cost)
else: # Stop when receiving an invalid compute_cost
done = True
this_actor.info("Exiting now.")
# worker-end
# main-begin
if __name__ == '__main__':
assert len(sys.argv) > 2, "Usage: python app-masterworkers.py platform_file deployment_file"
e = Engine(sys.argv)
# Register the classes representing the actors
e.register_actor("master", master)
e.register_actor("worker", worker)
# Load the platform description and then deploy the application
e.load_platform(sys.argv[1])
e.load_deployment(sys.argv[2])
# Run the simulation
e.run()
this_actor.info("Simulation is over")
# main-end
Process migration tracing
This version is enhanced so that the process migrations can be displayed
as arrows in a Gantt-chart visualization. Recommended options to that
extend: --cfg=tracing:yes --cfg=tracing/actor:yes
View examples/cpp/trace-process-migration/s4u-trace-process-migration.cpp
Download s4u-trace-process-migration.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This source code simply loads the platform. This is only useful to play
* with the tracing module. See the tesh file to see how to generate the
* traces.
*/
#include "simgrid/instr.h"
#include "simgrid/s4u.hpp"
#include <memory>
namespace sg4 = simgrid::s4u;
/* The guy we will move from host to host. It move alone and then is moved by policeman back */
static void emigrant()
{
auto* mailbox = sg4::Mailbox::by_name("master_mailbox");
sg4::this_actor::sleep_for(2);
while (true) { // I am an eternal emigrant
auto destination = mailbox->get_unique<std::string>();
if (destination->empty())
break; // there is no destination, die
sg4::this_actor::set_host(sg4::Host::by_name(*destination));
sg4::this_actor::sleep_for(2); // I am tired, have to sleep for 2 seconds
}
}
static void policeman()
{
// I am the master of emigrant actor,
// I tell it where it must emigrate to.
auto destinations = {"Tremblay", "Jupiter", "Fafard", "Ginette", "Bourassa", "Fafard", "Tremblay", "Ginette", ""};
auto* mailbox = sg4::Mailbox::by_name("master_mailbox");
for (auto const& destination : destinations) {
mailbox->put_init(new std::string(destination), 0)->set_tracing_category("migration_order")->wait();
}
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform_file\n \tExample: %s small_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
simgrid::instr::declare_tracing_category("migration_order");
sg4::Actor::create("emigrant", e.host_by_name("Fafard"), emigrant);
sg4::Actor::create("policeman", e.host_by_name("Tremblay"), policeman);
e.run();
return 0;
}
Tracing user variables
You can also attach your own variables to any resource described in the platform
file. The following examples illustrate this feature. They have to be run with
the following options: --cfg=tracing:yes --cfg=tracing/platform:yes
Attaching variables to Hosts
View examples/cpp/trace-host-user-variables/s4u-trace-host-user-variables.cpp
Download s4u-trace-host-user-variables.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This source code simply loads the platform. This is only useful to play
* with the tracing module. See the tesh file to see how to generate the
* traces.
*/
#include "simgrid/instr.h"
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void trace_fun()
{
const auto host = sg4::this_actor::get_host()->get_name();
// the hostname has an empty HDD with a capacity of 100000 (bytes)
simgrid::instr::set_host_variable(host, "HDD_capacity", 100000);
simgrid::instr::set_host_variable(host, "HDD_utilization", 0);
for (int i = 0; i < 10; i++) {
// create and execute a task just to make the simulated time advance
sg4::this_actor::execute(1e4);
// ADD: after the execution of this task, the HDD utilization increases by 100 (bytes)
simgrid::instr::add_host_variable(host, "HDD_utilization", 100);
}
for (int i = 0; i < 10; i++) {
// create and execute a task just to make the simulated time advance
sg4::this_actor::execute(1e4);
// SUB: after the execution of this task, the HDD utilization decreases by 100 (bytes)
simgrid::instr::sub_host_variable(host, "HDD_utilization", 100);
}
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform_file\n \tExample: %s small_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
// declaring user variables
simgrid::instr::declare_host_variable("HDD_capacity");
simgrid::instr::declare_host_variable("HDD_utilization", "1 0 0"); // red color
sg4::Actor::create("master", e.host_by_name("Tremblay"), trace_fun);
e.run();
// get user declared variables
if (const auto& host_variables = simgrid::instr::get_host_variables(); not host_variables.empty()) {
XBT_INFO("Declared host variables:");
for (const auto& var : host_variables)
XBT_INFO("%s", var.c_str());
}
const auto& link_variables = simgrid::instr::get_link_variables();
xbt_assert(link_variables.empty(), "Should not have any declared link variable!");
return 0;
}
Attaching variables to Links
The tricky part is that you have to know the name of the link you want to enhance with a variable.
View examples/cpp/trace-link-user-variables/s4u-trace-link-user-variables.cpp
Download s4u-trace-link-user-variables.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This source code simply loads the platform. This is only useful to play
* with the tracing module. See the tesh file to see how to generate the
* traces.
*/
#include "simgrid/instr.h"
#include "simgrid/s4u.hpp"
namespace sg4 = simgrid::s4u;
static void trace_fun()
{
// set initial values for the link user variables this example only shows for links identified by "6" and "3" in the
// platform file
// Set the Link_Capacity variable
simgrid::instr::set_link_variable("6", "Link_Capacity", 12.34);
simgrid::instr::set_link_variable("3", "Link_Capacity", 56.78);
// Set the Link_Utilization variable
simgrid::instr::set_link_variable("3", "Link_Utilization", 1.2);
simgrid::instr::set_link_variable("6", "Link_Utilization", 3.4);
// run the simulation, update my variables accordingly
for (int i = 0; i < 10; i++) {
sg4::this_actor::execute(1e6);
// Add to link user variables
simgrid::instr::add_link_variable("3", "Link_Utilization", 5.6);
simgrid::instr::add_link_variable("6", "Link_Utilization", 7.8);
}
for (int i = 0; i < 10; i++) {
sg4::this_actor::execute(1e6);
// Subtract from link user variables
simgrid::instr::sub_link_variable("3", "Link_Utilization", 3.4);
simgrid::instr::sub_link_variable("6", "Link_Utilization", 5.6);
}
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform_file\n \tExample: %s small_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
// declaring link user variables (one without, another with an RGB color)
simgrid::instr::declare_link_variable("Link_Capacity");
simgrid::instr::declare_link_variable("Link_Utilization", "0.9 0.1 0.1");
sg4::Actor::create("master", e.host_by_name("Tremblay"), trace_fun);
sg4::Actor::create("worker", e.host_by_name("Tremblay"), trace_fun);
sg4::Actor::create("worker", e.host_by_name("Jupiter"), trace_fun);
sg4::Actor::create("worker", e.host_by_name("Fafard"), trace_fun);
sg4::Actor::create("worker", e.host_by_name("Ginette"), trace_fun);
sg4::Actor::create("worker", e.host_by_name("Bourassa"), trace_fun);
e.run();
return 0;
}
Attaching variables to network routes
It is often easier to update a given variable for all links of a given network path (identified by its source and destination hosts) instead of knowing the name of each specific link.
View examples/cpp/trace-route-user-variables/s4u-trace-route-user-variables.cpp
Download s4u-trace-route-user-variables.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* This source code simply loads the platform. This is only useful to play
* with the tracing module. See the tesh file to see how to generate the
* traces.
*/
#include "simgrid/instr.h"
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void trace_fun()
{
// Set initial values for the link user variables
// This example uses source and destination where source and destination are the name of hosts in the platform file.
// The functions will set/change the value of the variable for all links in the route between source and destination.
// Set the Link_Capacity variable
simgrid::instr::set_link_variable("Tremblay", "Bourassa", "Link_Capacity", 12.34);
simgrid::instr::set_link_variable("Fafard", "Ginette", "Link_Capacity", 56.78);
// Set the Link_Utilization variable
simgrid::instr::set_link_variable("Tremblay", "Bourassa", "Link_Utilization", 1.2);
simgrid::instr::set_link_variable("Fafard", "Ginette", "Link_Utilization", 3.4);
// run the simulation, update my variables accordingly
for (int i = 0; i < 10; i++) {
sg4::this_actor::execute(1e6);
// Add to link user variables
simgrid::instr::add_link_variable("Tremblay", "Bourassa", "Link_Utilization", 5.6);
simgrid::instr::add_link_variable("Fafard", "Ginette", "Link_Utilization", 7.8);
}
for (int i = 0; i < 10; i++) {
sg4::this_actor::execute(1e6);
// Subtract from link user variables
simgrid::instr::sub_link_variable("Tremblay", "Bourassa", "Link_Utilization", 3.4);
simgrid::instr::sub_link_variable("Fafard", "Ginette", "Link_Utilization", 5.6);
}
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform_file\n \tExample: %s small_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
// declaring link user variables (one without, another with an RGB color)
simgrid::instr::declare_link_variable("Link_Capacity");
simgrid::instr::declare_link_variable("Link_Utilization", "0.9 0.1 0.1");
sg4::Actor::create("master", e.host_by_name("Tremblay"), trace_fun);
sg4::Actor::create("worker", e.host_by_name("Tremblay"), trace_fun);
sg4::Actor::create("worker", e.host_by_name("Jupiter"), trace_fun);
sg4::Actor::create("worker", e.host_by_name("Fafard"), trace_fun);
sg4::Actor::create("worker", e.host_by_name("Ginette"), trace_fun);
sg4::Actor::create("worker", e.host_by_name("Bourassa"), trace_fun);
e.run();
return 0;
}
Larger SimGrid Exemplars
This section contains application examples that are somewhat larger than the previous examples.
Classical examples
Token ring
Shows how to implement a classical communication pattern, where a token is exchanged along a ring to reach every participant.
View examples/cpp/app-token-ring/s4u-app-token-ring.cpp
Download s4u-app-token-ring.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <simgrid/s4u.hpp>
#include <algorithm>
#include <string>
#include <map>
#include <vector>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_app_token_ring, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
class RelayRunner {
public:
explicit RelayRunner() = default;
void operator()() const
{
size_t token_size = 1000000; /* The token is 1MB long*/
sg4::Mailbox* my_mailbox;
sg4::Mailbox* neighbor_mailbox;
unsigned int rank = 0;
try {
rank = std::stoi(sg4::this_actor::get_name());
} catch (const std::invalid_argument& ia) {
throw std::invalid_argument(std::string("Actors of this example must have a numerical name, not ") + ia.what());
}
my_mailbox = sg4::Mailbox::by_name(std::to_string(rank));
if (rank + 1 == sg4::Engine::get_instance()->get_host_count())
/* The last actor sends the token back to rank 0 */
neighbor_mailbox = sg4::Mailbox::by_name("0");
else
/* The others actors send to their right neighbor (rank+1) */
neighbor_mailbox = sg4::Mailbox::by_name(std::to_string(rank + 1));
if (rank == 0) {
/* The root actor (rank 0) first sends the token then waits to receive it back */
XBT_INFO("Host \"%u\" send 'Token' to Host \"%s\"", rank, neighbor_mailbox->get_cname());
std::string msg = "Token";
neighbor_mailbox->put(&msg, token_size);
const auto* res = my_mailbox->get<std::string>();
XBT_INFO("Host \"%u\" received \"%s\"", rank, res->c_str());
} else {
auto* res = my_mailbox->get<std::string>();
XBT_INFO("Host \"%u\" received \"%s\"", rank, res->c_str());
XBT_INFO("Host \"%u\" send 'Token' to Host \"%s\"", rank, neighbor_mailbox->get_cname());
neighbor_mailbox->put(res, token_size);
}
}
};
int main(int argc, char** argv)
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform.xml\n", argv[0]);
e.load_platform(argv[1]);
XBT_INFO("Number of hosts '%zu'", e.get_host_count());
int id = 0;
std::vector<sg4::Host*> list = e.get_all_hosts();
for (auto const& host : list) {
/* - Give a unique rank to each host and create a @ref relay_runner actor on each */
sg4::Actor::create((std::to_string(id)).c_str(), host, RelayRunner());
id++;
}
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
View examples/c/app-token-ring/app-token-ring.c
/* Copyright (c) 2008-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "simgrid/mailbox.h"
#include "xbt/log.h"
#include "xbt/str.h"
#include "xbt/sysdep.h"
#include <stddef.h>
#include <stdio.h>
XBT_LOG_NEW_DEFAULT_CATEGORY(app_token_ring, "Messages specific for this example");
/* Main function of all actors used in this example */
static void relay_runner(int argc, char* argv[])
{
xbt_assert(argc == 0, "The relay_runner function does not accept any parameter from the XML deployment file");
const char* name = sg_actor_self_get_name();
unsigned rank = (unsigned)xbt_str_parse_int(name, "Any actor of this example must have a numerical name");
sg_mailbox_t my_mailbox = sg_mailbox_by_name(name);
/* The last actor sends the token back to rank 0, the others send to their right neighbor (rank+1) */
char neighbor_mailbox_name[256];
snprintf(neighbor_mailbox_name, 255, "%u", rank + 1 == sg_host_count() ? 0 : rank + 1);
sg_mailbox_t neighbor_mailbox = sg_mailbox_by_name(neighbor_mailbox_name);
char* res;
if (rank == 0) {
/* The root actor (rank 0) first sends the token then waits to receive it back */
XBT_INFO("Host \"%u\" send 'Token' to Host \"%s\"", rank, neighbor_mailbox_name);
sg_mailbox_put(neighbor_mailbox, xbt_strdup("Token"), 1000000);
res = (char*)sg_mailbox_get(my_mailbox);
XBT_INFO("Host \"%u\" received \"%s\"", rank, res);
} else {
/* The others actors receive from their left neighbor (rank-1) and send to their right neighbor (rank+1) */
res = (char*)sg_mailbox_get(my_mailbox);
XBT_INFO("Host \"%u\" received \"%s\"", rank, res);
XBT_INFO("Host \"%u\" send 'Token' to Host \"%s\"", rank, neighbor_mailbox_name);
sg_mailbox_put(neighbor_mailbox, xbt_strdup("Token"), 1000000);
}
free(res);
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform.xml\n", argv[0]);
simgrid_load_platform(argv[1]); /* - Load the platform description */
size_t host_count = sg_host_count();
sg_host_t* hosts = sg_host_list();
XBT_INFO("Number of hosts '%zu'", host_count);
for (size_t i = 0; i < host_count; i++) {
/* - Give a unique rank to each host and create a @ref relay_runner actor on each */
char* name_host = bprintf("%zu", i);
sg_actor_create(name_host, hosts[i], &relay_runner, 0, NULL);
free(name_host);
}
free(hosts);
simgrid_run();
XBT_INFO("Simulation time %g", simgrid_get_clock());
return 0;
}
Master Workers
Another good old example, where one Master actor has a bunch of tasks to dispatch to a set of several Worker actors. This example is used in the SimGrid tutorial.
This example comes in two equivalent variants, one where the actors are specified as simple functions (which is easier to understand for newcomers) and one where the actors are specified as classes (which is more powerful for the users wanting to build their own projects upon the example).
View examples/cpp/app-masterworkers/s4u-app-masterworkers-class.cpp
Download s4u-app-masterworkers-class.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* ************************************************************************* */
/* Take this tutorial online: https://simgrid.org/doc/latest/Tutorial_Algorithms.html */
/* ************************************************************************* */
#include <simgrid/s4u.hpp>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_app_masterworker, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
class Master {
long tasks_count = 0;
double compute_cost = 0;
long communicate_cost = 0;
std::vector<sg4::Mailbox*> workers;
public:
explicit Master(std::vector<std::string> args)
{
xbt_assert(args.size() > 4, "The master function expects 3 arguments plus the workers' names");
tasks_count = std::stol(args[1]);
compute_cost = std::stod(args[2]);
communicate_cost = std::stol(args[3]);
for (unsigned int i = 4; i < args.size(); i++)
workers.push_back(sg4::Mailbox::by_name(args[i]));
XBT_INFO("Got %zu workers and %ld tasks to process", workers.size(), tasks_count);
}
void operator()()
{
for (int i = 0; i < tasks_count; i++) { /* For each task to be executed: */
/* - Select a worker in a round-robin way */
sg4::Mailbox* mailbox = workers[i % workers.size()];
/* - Send the computation amount to the worker */
if (tasks_count < 10000 || (tasks_count < 100000 && i % 10000 == 0) || i % 100000 == 0)
XBT_INFO("Sending task %d of %ld to mailbox '%s'", i, tasks_count, mailbox->get_cname());
mailbox->put(new double(compute_cost), communicate_cost);
}
XBT_INFO("All tasks have been dispatched. Request all workers to stop.");
for (unsigned int i = 0; i < workers.size(); i++) {
/* The workers stop when receiving a negative compute_cost */
sg4::Mailbox* mailbox = workers[i % workers.size()];
mailbox->put(new double(-1.0), 0);
}
}
};
class Worker {
sg4::Mailbox* mailbox = nullptr;
public:
explicit Worker(std::vector<std::string> args)
{
xbt_assert(args.size() == 1, "The worker expects to not get any argument");
mailbox = sg4::Mailbox::by_name(sg4::this_actor::get_host()->get_name());
}
void operator()()
{
double compute_cost;
do {
auto msg = mailbox->get_unique<double>();
compute_cost = *msg;
if (compute_cost > 0) /* If compute_cost is valid, execute a computation of that cost */
sg4::this_actor::execute(compute_cost);
} while (compute_cost > 0); /* Stop when receiving an invalid compute_cost */
XBT_INFO("Exiting now.");
}
};
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 2, "Usage: %s platform_file deployment_file\n", argv[0]);
/* Register the classes representing the actors */
e.register_actor<Master>("master");
e.register_actor<Worker>("worker");
/* Load the platform description and then deploy the application */
e.load_platform(argv[1]);
e.load_deployment(argv[2]);
/* Run the simulation */
e.run();
XBT_INFO("Simulation is over");
return 0;
}
View examples/cpp/app-masterworkers/s4u-app-masterworkers-fun.cpp
Download s4u-app-masterworkers-fun.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* ************************************************************************* */
/* Take this tutorial online: https://simgrid.org/doc/latest/Tutorial_Algorithms.html */
/* ************************************************************************* */
#include <simgrid/s4u.hpp>
namespace sg4 = simgrid::s4u;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_app_masterworker, "Messages specific for this example");
// master-begin
static void master(std::vector<std::string> args)
{
xbt_assert(args.size() > 4, "The master function expects at least 3 arguments");
long tasks_count = std::stol(args[1]);
double compute_cost = std::stod(args[2]);
long communication_cost = std::stol(args[3]);
std::vector<sg4::Mailbox*> workers;
for (unsigned int i = 4; i < args.size(); i++)
workers.push_back(sg4::Mailbox::by_name(args[i]));
XBT_INFO("Got %zu workers and %ld tasks to process", workers.size(), tasks_count);
for (int i = 0; i < tasks_count; i++) { /* For each task to be executed: */
/* - Select a worker in a round-robin way */
sg4::Mailbox* mailbox = workers[i % workers.size()];
/* - Send the computation cost to that worker */
XBT_INFO("Sending task %d of %ld to mailbox '%s'", i, tasks_count, mailbox->get_cname());
mailbox->put(new double(compute_cost), communication_cost);
}
XBT_INFO("All tasks have been dispatched. Request all workers to stop.");
for (unsigned int i = 0; i < workers.size(); i++) {
/* The workers stop when receiving a negative compute_cost */
sg4::Mailbox* mailbox = workers[i % workers.size()];
mailbox->put(new double(-1.0), 0);
}
}
// master-end
// worker-begin
static void worker(std::vector<std::string> args)
{
xbt_assert(args.size() == 1, "The worker expects no argument");
const sg4::Host* my_host = sg4::this_actor::get_host();
sg4::Mailbox* mailbox = sg4::Mailbox::by_name(my_host->get_name());
double compute_cost;
do {
auto msg = mailbox->get_unique<double>();
compute_cost = *msg;
if (compute_cost > 0) /* If compute_cost is valid, execute a computation of that cost */
sg4::this_actor::execute(compute_cost);
} while (compute_cost > 0); /* Stop when receiving an invalid compute_cost */
XBT_INFO("Exiting now.");
}
// worker-end
// main-begin
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 2, "Usage: %s platform_file deployment_file\n", argv[0]);
/* Register the functions representing the actors */
e.register_function("master", &master);
e.register_function("worker", &worker);
/* Load the platform description and then deploy the application */
e.load_platform(argv[1]);
e.load_deployment(argv[2]);
/* Run the simulation */
e.run();
XBT_INFO("Simulation is over");
return 0;
}
// main-end
View examples/c/app-masterworker/app-masterworker.c
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/forward.h"
#include "simgrid/mailbox.h"
#include "xbt/log.h"
#include "xbt/str.h"
#include "xbt/sysdep.h"
#define FINALIZE 221297 /* a magic number to tell people to stop working */
#include <stdio.h> /* snprintf */
XBT_LOG_NEW_DEFAULT_CATEGORY(app_masterworker, "Messages specific for this example");
/* Main function of the master actor */
static void master(int argc, char* argv[])
{
xbt_assert(argc == 5, "The master function expects 4 arguments from the XML deployment file");
long number_of_tasks = xbt_str_parse_int(argv[1], "Invalid amount of tasks"); /* - Number of tasks */
double comp_size = xbt_str_parse_double(argv[2], "Invalid computational size"); /* - Compute cost */
long comm_size = xbt_str_parse_int(argv[3], "Invalid communication size"); /* - Communication size */
long workers_count = xbt_str_parse_int(argv[4], "Invalid amount of workers"); /* - Number of workers */
XBT_INFO("Got %ld workers and %ld tasks to process", workers_count, number_of_tasks);
for (int i = 0; i < number_of_tasks; i++) { /* For each task to be executed: */
char mailbox_name[80];
char task_name[80];
double* payload = xbt_malloc(sizeof(double));
snprintf(mailbox_name, 79, "worker-%ld", i % workers_count); /* - Select a @ref worker in a round-robin way */
snprintf(task_name, 79, "Task_%d", i);
sg_mailbox_t mailbox = sg_mailbox_by_name(mailbox_name);
*payload = comp_size;
if (number_of_tasks < 10000 || i % 10000 == 0)
XBT_INFO("Sending \"%s\" (of %ld) to mailbox \"%s\"", task_name, number_of_tasks, mailbox_name);
sg_mailbox_put(mailbox, payload, comm_size); /* - Send the amount of flops to compute to the @ref worker */
}
XBT_INFO("All tasks have been dispatched. Let's tell everybody the computation is over.");
for (int i = 0; i < workers_count; i++) { /* - Eventually tell all the workers to stop by sending a "finalize" task */
char mailbox_name[80];
snprintf(mailbox_name, 79, "worker-%ld", i % workers_count);
double* payload = xbt_malloc(sizeof(double));
sg_mailbox_t mailbox = sg_mailbox_by_name(mailbox_name);
*payload = FINALIZE;
sg_mailbox_put(mailbox, payload, 0);
}
}
/* Main functions of the Worker actors */
static void worker(int argc, char* argv[])
{
xbt_assert(argc == 2,
"The worker expects a single argument from the XML deployment file: its worker ID (its numerical rank)");
char mailbox_name[80];
long id = xbt_str_parse_int(argv[1], "Invalid argument");
snprintf(mailbox_name, 79, "worker-%ld", id);
sg_mailbox_t mailbox = sg_mailbox_by_name(mailbox_name);
while (1) { /* The worker wait in an infinite loop for tasks sent by the @ref master */
double* payload = (double*)sg_mailbox_get(mailbox);
if (*payload == FINALIZE) {
xbt_free(payload); /* - Exit if 'finalize' is received */
break;
}
sg_actor_execute(*payload); /* - Otherwise, process the received number of flops*/
xbt_free(payload);
}
XBT_INFO("I'm done. See you!");
}
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 2,
"Usage: %s platform_file deployment_file\n"
"\tExample: %s platform.xml deployment.xml\n",
argv[0], argv[0]);
simgrid_load_platform(argv[1]); /* - Load the platform description */
simgrid_register_function("master", master); /* - Register the function to be executed by the actors */
simgrid_register_function("worker", worker);
simgrid_load_deployment(argv[2]); /* - Deploy the application */
simgrid_run(); /* - Run the simulation */
XBT_INFO("Simulation time %g", simgrid_get_clock());
return 0;
}
View examples/python/app-masterworkers/app-masterworkers.py
# Copyright (c) 2010-2024. The SimGrid Team. All rights reserved.
# This program is free software; you can redistribute it and/or modify it
# under the terms of the license (GNU LGPL) which comes with this package.
"""
# ##################################################################################
# Take this tutorial online: https://simgrid.org/doc/latest/Tutorial_Algorithms.html
# ##################################################################################
"""
import sys
from simgrid import Engine, Mailbox, this_actor
# master-begin
def master(*args):
assert len(args) > 3, f"Actor master requires 3 parameters plus the workers' names, but got {len(args)}"
tasks_count = int(args[0])
compute_cost = int(args[1])
communicate_cost = int(args[2])
workers = []
for i in range(3, len(args)):
workers.append(Mailbox.by_name(args[i]))
this_actor.info(f"Got {len(workers)} workers and {tasks_count} tasks to process")
for i in range(tasks_count): # For each task to be executed:
# - Select a worker in a round-robin way
mailbox = workers[i % len(workers)]
# - Send the computation amount to the worker
if (tasks_count < 10000 or (tasks_count < 100000 and i % 10000 == 0) or i % 100000 == 0):
this_actor.info(f"Sending task {i} of {tasks_count} to mailbox '{mailbox.name}'")
mailbox.put(compute_cost, communicate_cost)
this_actor.info("All tasks have been dispatched. Request all workers to stop.")
for mailbox in workers:
# The workers stop when receiving a negative compute_cost
mailbox.put(-1, 0)
# master-end
# worker-begin
def worker(*args):
assert not args, "The worker expects to not get any argument"
mailbox = Mailbox.by_name(this_actor.get_host().name)
done = False
while not done:
compute_cost = mailbox.get()
if compute_cost > 0: # If compute_cost is valid, execute a computation of that cost
this_actor.execute(compute_cost)
else: # Stop when receiving an invalid compute_cost
done = True
this_actor.info("Exiting now.")
# worker-end
# main-begin
if __name__ == '__main__':
assert len(sys.argv) > 2, "Usage: python app-masterworkers.py platform_file deployment_file"
e = Engine(sys.argv)
# Register the classes representing the actors
e.register_actor("master", master)
e.register_actor("worker", worker)
# Load the platform description and then deploy the application
e.load_platform(sys.argv[1])
e.load_deployment(sys.argv[2])
# Run the simulation
e.run()
this_actor.info("Simulation is over")
# main-end
Data diffusion
Bit Torrent
Classical protocol for Peer-to-Peer data diffusion.
View examples/cpp/app-bittorrent/s4u-bittorrent.cpp
/* Copyright (c) 2012-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "s4u-bittorrent.hpp"
#include "s4u-peer.hpp"
#include "s4u-tracker.hpp"
int main(int argc, char* argv[])
{
simgrid::s4u::Engine e(&argc, argv);
/* Check the arguments */
xbt_assert(argc > 2, "Usage: %s platform_file deployment_file", argv[0]);
e.load_platform(argv[1]);
e.register_actor<Tracker>("tracker");
e.register_actor<Peer>("peer");
e.load_deployment(argv[2]);
e.run();
return 0;
}
View examples/cpp/app-bittorrent/s4u-peer.cpp
/* Copyright (c) 2012-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include <algorithm>
#include <array>
#include <climits>
#include "s4u-peer.hpp"
#include "s4u-tracker.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_bt_peer, "Messages specific for the peers");
namespace sg4 = simgrid::s4u;
/*
* User parameters for transferred file data. For the test, the default values are :
* File size: 10 pieces * 5 blocks/piece * 16384 bytes/block = 819200 bytes
*/
constexpr unsigned long FILE_PIECES = 10UL;
constexpr unsigned long PIECES_BLOCKS = 5UL;
constexpr int BLOCK_SIZE = 16384;
/** Number of blocks asked by each request */
constexpr unsigned long BLOCKS_REQUESTED = 2UL;
constexpr double SLEEP_DURATION = 1.0;
#define BITS_TO_BYTES(x) (((x) / 8 + (x) % 8) ? 1 : 0)
/** Message sizes
* Sizes based on report by A. Legout et al, Understanding BitTorrent: An Experimental Perspective
* http://hal.inria.fr/inria-00000156/en
*/
constexpr unsigned message_size(MessageType type)
{
constexpr std::array<unsigned, 10> sizes{{/* HANDSHAKE */ 68,
/* CHOKE */ 5,
/* UNCHOKE */ 5,
/* INTERESTED */ 5,
/* NOTINTERESTED */ 5,
/* HAVE */ 9,
/* BITFIELD */ 5,
/* REQUEST */ 17,
/* PIECE */ 13,
/* CANCEL */ 17}};
return sizes.at(static_cast<int>(type));
}
constexpr const char* message_name(MessageType type)
{
constexpr std::array<const char*, 10> names{{"HANDSHAKE", "CHOKE", "UNCHOKE", "INTERESTED", "NOTINTERESTED", "HAVE",
"BITFIELD", "REQUEST", "PIECE", "CANCEL"}};
return names.at(static_cast<int>(type));
}
Peer::Peer(std::vector<std::string> args)
{
// Check arguments
xbt_assert(args.size() == 3 || args.size() == 4, "Wrong number of arguments");
try {
id = std::stoi(args[1]);
mailbox_ = sg4::Mailbox::by_name(std::to_string(id));
} catch (const std::invalid_argument&) {
throw std::invalid_argument("Invalid ID:" + args[1]);
}
random.set_seed(id);
try {
deadline = std::stod(args[2]);
} catch (const std::invalid_argument&) {
throw std::invalid_argument("Invalid deadline:" + args[2]);
}
xbt_assert(deadline > 0, "Wrong deadline supplied");
if (args.size() == 4 && args[3] == "1") {
bitfield_ = (1U << FILE_PIECES) - 1U;
bitfield_blocks = (1ULL << (FILE_PIECES * PIECES_BLOCKS)) - 1ULL;
}
pieces_count.resize(FILE_PIECES);
XBT_INFO("Hi, I'm joining the network with id %d", id);
}
/** Peer main function */
void Peer::operator()()
{
// Getting peer data from the tracker.
if (getPeersFromTracker()) {
XBT_DEBUG("Got %zu peers from the tracker. Current status is: %s", connected_peers.size(), getStatus().c_str());
begin_receive_time = sg4::Engine::get_clock();
mailbox_->set_receiver(sg4::Actor::self());
if (hasFinished()) {
sendHandshakeToAllPeers();
} else {
leech();
}
seed();
} else {
XBT_INFO("Couldn't contact the tracker.");
}
XBT_INFO("Here is my current status: %s", getStatus().c_str());
}
bool Peer::getPeersFromTracker()
{
sg4::Mailbox* tracker_mailbox = sg4::Mailbox::by_name(TRACKER_MAILBOX);
// Build the task to send to the tracker
auto* peer_request = new TrackerQuery(id, mailbox_);
try {
XBT_DEBUG("Sending a peer request to the tracker.");
tracker_mailbox->put(peer_request, TRACKER_COMM_SIZE, GET_PEERS_TIMEOUT);
} catch (const simgrid::TimeoutException&) {
XBT_DEBUG("Timeout expired when requesting peers to tracker");
delete peer_request;
return false;
}
try {
auto answer = mailbox_->get_unique<TrackerAnswer>(GET_PEERS_TIMEOUT);
// Add the peers the tracker gave us to our peer list.
for (auto const& peer_id : answer->getPeers())
if (id != peer_id)
connected_peers.try_emplace(peer_id, peer_id);
} catch (const simgrid::TimeoutException&) {
XBT_DEBUG("Timeout expired when requesting peers to tracker");
return false;
}
return true;
}
void Peer::sendHandshakeToAllPeers()
{
for (auto const& [_, remote_peer] : connected_peers) {
auto* handshake = new Message(MessageType::HANDSHAKE, id, mailbox_);
remote_peer.mailbox_->put_init(handshake, message_size(MessageType::HANDSHAKE))->detach();
XBT_DEBUG("Sending a HANDSHAKE to %d", remote_peer.id);
}
}
void Peer::sendMessage(sg4::Mailbox* mailbox, MessageType type, uint64_t size)
{
XBT_DEBUG("Sending %s to %s", message_name(type), mailbox->get_cname());
mailbox->put_init(new Message(type, id, bitfield_, mailbox_), size)->detach();
}
void Peer::sendBitfield(sg4::Mailbox* mailbox)
{
XBT_DEBUG("Sending a BITFIELD to %s", mailbox->get_cname());
mailbox
->put_init(new Message(MessageType::BITFIELD, id, bitfield_, mailbox_),
message_size(MessageType::BITFIELD) + BITS_TO_BYTES(FILE_PIECES))
->detach();
}
void Peer::sendPiece(sg4::Mailbox* mailbox, unsigned int piece, int block_index, int block_length)
{
xbt_assert(not hasNotPiece(piece), "Tried to send a unavailable piece.");
XBT_DEBUG("Sending the PIECE %u (%d,%d) to %s", piece, block_index, block_length, mailbox->get_cname());
mailbox->put_init(new Message(MessageType::PIECE, id, mailbox_, piece, block_index, block_length), BLOCK_SIZE)
->detach();
}
void Peer::sendHaveToAllPeers(unsigned int piece)
{
XBT_DEBUG("Sending HAVE message to all my peers");
for (auto const& [_, remote_peer] : connected_peers) {
remote_peer.mailbox_->put_init(new Message(MessageType::HAVE, id, mailbox_, piece), message_size(MessageType::HAVE))
->detach();
}
}
void Peer::sendRequestTo(Connection* remote_peer, unsigned int piece)
{
remote_peer->current_piece = piece;
xbt_assert(remote_peer->hasPiece(piece));
int block_index = getFirstMissingBlockFrom(piece);
if (block_index != -1) {
int block_length = static_cast<int>(std::min(BLOCKS_REQUESTED, PIECES_BLOCKS - block_index));
XBT_DEBUG("Sending a REQUEST to %s for piece %u (%d,%d)", remote_peer->mailbox_->get_cname(), piece, block_index,
block_length);
remote_peer->mailbox_
->put_init(new Message(MessageType::REQUEST, id, mailbox_, piece, block_index, block_length),
message_size(MessageType::REQUEST))
->detach();
}
}
std::string Peer::getStatus() const
{
std::string res;
for (unsigned i = 0; i < FILE_PIECES; i++)
res += (bitfield_ & (1U << i)) ? '1' : '0';
return res;
}
bool Peer::hasFinished() const
{
return bitfield_ == (1U << FILE_PIECES) - 1U;
}
/** Indicates if the remote peer has a piece not stored by the local peer */
bool Peer::isInterestedBy(const Connection* remote_peer) const
{
return remote_peer->bitfield & (bitfield_ ^ ((1 << FILE_PIECES) - 1));
}
bool Peer::isInterestedByFree(const Connection* remote_peer) const
{
for (unsigned int i = 0; i < FILE_PIECES; i++)
if (hasNotPiece(i) && remote_peer->hasPiece(i) && isNotDownloadingPiece(i))
return true;
return false;
}
void Peer::updatePiecesCountFromBitfield(unsigned int bitfield)
{
for (unsigned int i = 0; i < FILE_PIECES; i++)
if (bitfield & (1U << i))
pieces_count[i]++;
}
unsigned int Peer::countPieces(unsigned int bitfield) const
{
unsigned int count = 0U;
unsigned int n = bitfield;
while (n) {
count += n & 1U;
n >>= 1U;
}
return count;
}
int Peer::nbInterestedPeers() const
{
return static_cast<int>(std::count_if(connected_peers.begin(), connected_peers.end(),
[](const auto& kv) { return kv.second.interested; }));
}
void Peer::leech()
{
double next_choked_update = sg4::Engine::get_clock() + UPDATE_CHOKED_INTERVAL;
XBT_DEBUG("Start downloading.");
/* Send a "handshake" message to all the peers it got (since it couldn't have gotten more than 50 peers) */
sendHandshakeToAllPeers();
XBT_DEBUG("Starting main leech loop listening on mailbox: %s", mailbox_->get_cname());
while (sg4::Engine::get_clock() < deadline && countPieces(bitfield_) < FILE_PIECES) {
if (comm_received == nullptr) {
comm_received = mailbox_->get_async<Message>(&message);
}
if (comm_received->test()) {
handleMessage();
delete message;
comm_received = nullptr;
} else {
// We don't execute the choke algorithm if we don't already have a piece
if (sg4::Engine::get_clock() >= next_choked_update && countPieces(bitfield_) > 0) {
updateChokedPeers();
next_choked_update += UPDATE_CHOKED_INTERVAL;
} else {
sg4::this_actor::sleep_for(SLEEP_DURATION);
}
}
}
if (hasFinished())
XBT_DEBUG("%d becomes a seeder", id);
}
void Peer::seed()
{
double next_choked_update = sg4::Engine::get_clock() + UPDATE_CHOKED_INTERVAL;
XBT_DEBUG("Start seeding.");
// start the main seed loop
while (sg4::Engine::get_clock() < deadline) {
if (comm_received == nullptr) {
comm_received = mailbox_->get_async<Message>(&message);
}
if (comm_received->test()) {
handleMessage();
delete message;
comm_received = nullptr;
} else {
if (sg4::Engine::get_clock() >= next_choked_update) {
updateChokedPeers();
// TODO: Change the choked peer algorithm when seeding.
next_choked_update += UPDATE_CHOKED_INTERVAL;
} else {
sg4::this_actor::sleep_for(SLEEP_DURATION);
}
}
}
}
void Peer::updateActivePeersSet(Connection* remote_peer)
{
if (remote_peer->interested && not remote_peer->choked_upload)
active_peers.insert(remote_peer);
else
active_peers.erase(remote_peer);
}
void Peer::handleMessage()
{
XBT_DEBUG("Received a %s message from %s", message_name(message->type), message->return_mailbox->get_cname());
auto known_peer = connected_peers.find(message->peer_id);
Connection* remote_peer = (known_peer == connected_peers.end()) ? nullptr : &known_peer->second;
xbt_assert(remote_peer != nullptr || message->type == MessageType::HANDSHAKE,
"The impossible did happened: A not-in-our-list peer sent us a message.");
switch (message->type) {
case MessageType::HANDSHAKE:
// Check if the peer is in our connection list.
if (remote_peer == nullptr) {
XBT_DEBUG("This peer %d was unknown, answer to its handshake", message->peer_id);
connected_peers.try_emplace(message->peer_id, message->peer_id);
sendMessage(message->return_mailbox, MessageType::HANDSHAKE, message_size(MessageType::HANDSHAKE));
}
// Send our bitfield to the peer
sendBitfield(message->return_mailbox);
break;
case MessageType::BITFIELD:
// Update the pieces list
updatePiecesCountFromBitfield(message->bitfield);
// Store the bitfield
remote_peer->bitfield = message->bitfield;
xbt_assert(not remote_peer->am_interested, "Should not be interested at first");
if (isInterestedBy(remote_peer)) {
remote_peer->am_interested = true;
sendMessage(message->return_mailbox, MessageType::INTERESTED, message_size(MessageType::INTERESTED));
}
break;
case MessageType::INTERESTED:
// Update the interested state of the peer.
remote_peer->interested = true;
updateActivePeersSet(remote_peer);
break;
case MessageType::NOTINTERESTED:
remote_peer->interested = false;
updateActivePeersSet(remote_peer);
break;
case MessageType::UNCHOKE:
xbt_assert(remote_peer->choked_download);
remote_peer->choked_download = false;
// Send requests to the peer, since it has unchoked us
if (remote_peer->am_interested)
requestNewPieceTo(remote_peer);
break;
case MessageType::CHOKE:
xbt_assert(not remote_peer->choked_download);
remote_peer->choked_download = true;
if (remote_peer->current_piece != -1)
removeCurrentPiece(remote_peer, remote_peer->current_piece);
break;
case MessageType::HAVE:
XBT_DEBUG("\t for piece %d", message->piece);
xbt_assert((message->piece >= 0 && static_cast<unsigned int>(message->piece) < FILE_PIECES),
"Wrong HAVE message received");
remote_peer->bitfield = remote_peer->bitfield | (1U << static_cast<unsigned int>(message->piece));
pieces_count[message->piece]++;
// If the piece is in our pieces, we tell the peer that we are interested.
if (not remote_peer->am_interested && hasNotPiece(message->piece)) {
remote_peer->am_interested = true;
sendMessage(message->return_mailbox, MessageType::INTERESTED, message_size(MessageType::INTERESTED));
if (not remote_peer->choked_download)
requestNewPieceTo(remote_peer);
}
break;
case MessageType::REQUEST:
xbt_assert(remote_peer->interested);
xbt_assert((message->piece >= 0 && static_cast<unsigned int>(message->piece) < FILE_PIECES),
"Wrong HAVE message received");
if (not remote_peer->choked_upload) {
XBT_DEBUG("\t for piece %d (%d,%d)", message->piece, message->block_index,
message->block_index + message->block_length);
if (not hasNotPiece(message->piece)) {
sendPiece(message->return_mailbox, message->piece, message->block_index, message->block_length);
}
} else {
XBT_DEBUG("\t for piece %d but he is choked.", message->peer_id);
}
break;
case MessageType::PIECE:
XBT_DEBUG(" \t for piece %d (%d,%d)", message->piece, message->block_index,
message->block_index + message->block_length);
xbt_assert(not remote_peer->choked_download);
xbt_assert(not remote_peer->choked_download, "Can't received a piece if I'm choked !");
xbt_assert((message->piece >= 0 && static_cast<unsigned int>(message->piece) < FILE_PIECES),
"Wrong piece received");
// TODO: Execute a computation.
if (hasNotPiece(static_cast<unsigned int>(message->piece))) {
updateBitfieldBlocks(message->piece, message->block_index, message->block_length);
if (hasCompletedPiece(static_cast<unsigned int>(message->piece))) {
// Removing the piece from our piece list
removeCurrentPiece(remote_peer, message->piece);
// Setting the fact that we have the piece
bitfield_ = bitfield_ | (1U << static_cast<unsigned int>(message->piece));
XBT_DEBUG("My status is now %s", getStatus().c_str());
// Sending the information to all the peers we are connected to
sendHaveToAllPeers(message->piece);
// sending UNINTERESTED to peers that do not have what we want.
updateInterestedAfterReceive();
} else { // piece not completed
sendRequestTo(remote_peer, message->piece); // ask for the next block
}
} else {
XBT_DEBUG("However, we already have it");
requestNewPieceTo(remote_peer);
}
break;
case MessageType::CANCEL:
break;
default:
THROW_IMPOSSIBLE;
}
// Update the peer speed.
if (remote_peer) {
remote_peer->addSpeedValue(1.0 / (sg4::Engine::get_clock() - begin_receive_time));
}
begin_receive_time = sg4::Engine::get_clock();
}
/** Selects the appropriate piece to download and requests it to the remote_peer */
void Peer::requestNewPieceTo(Connection* remote_peer)
{
int piece = selectPieceToDownload(remote_peer);
if (piece != -1) {
current_pieces |= (1U << (unsigned int)piece);
sendRequestTo(remote_peer, piece);
}
}
void Peer::removeCurrentPiece(Connection* remote_peer, unsigned int current_piece)
{
current_pieces &= ~(1U << current_piece);
remote_peer->current_piece = -1;
}
/** @brief Return the piece to be downloaded
* There are two cases (as described in "Bittorrent Architecture Protocol", Ryan Toole :
* If a piece is partially downloaded, this piece will be selected prioritarily
* If the peer has strictly less than 4 pieces, he chooses a piece at random.
* If the peer has more than pieces, he downloads the pieces that are the less replicated (rarest policy).
* If all pieces have been downloaded or requested, we select a random requested piece (endgame mode).
* @param remote_peer: information about the connection
* @return the piece to download if possible. -1 otherwise
*/
int Peer::selectPieceToDownload(const Connection* remote_peer)
{
int piece = partiallyDownloadedPiece(remote_peer);
// strict priority policy
if (piece != -1)
return piece;
// end game mode
if (countPieces(current_pieces) >= (FILE_PIECES - countPieces(bitfield_)) && isInterestedBy(remote_peer)) {
int nb_interesting_pieces = 0;
// compute the number of interesting pieces
for (unsigned int i = 0; i < FILE_PIECES; i++)
if (remotePeerHasMissingPiece(remote_peer, i))
nb_interesting_pieces++;
xbt_assert(nb_interesting_pieces != 0);
// get a random interesting piece
int random_piece_index = random.uniform_int(0, nb_interesting_pieces - 1);
int current_index = 0;
for (unsigned int i = 0; i < FILE_PIECES; i++) {
if (remotePeerHasMissingPiece(remote_peer, i)) {
if (random_piece_index == current_index) {
piece = i;
break;
}
current_index++;
}
}
xbt_assert(piece != -1);
return piece;
}
// Random first policy
if (countPieces(bitfield_) < 4 && isInterestedByFree(remote_peer)) {
int nb_interesting_pieces = 0;
// compute the number of interesting pieces
for (unsigned int i = 0; i < FILE_PIECES; i++)
if (remotePeerHasMissingPiece(remote_peer, i) && isNotDownloadingPiece(i))
nb_interesting_pieces++;
xbt_assert(nb_interesting_pieces != 0);
// get a random interesting piece
int random_piece_index = random.uniform_int(0, nb_interesting_pieces - 1);
int current_index = 0;
for (unsigned int i = 0; i < FILE_PIECES; i++) {
if (remotePeerHasMissingPiece(remote_peer, i) && isNotDownloadingPiece(i)) {
if (random_piece_index == current_index) {
piece = i;
break;
}
current_index++;
}
}
xbt_assert(piece != -1);
return piece;
} else { // Rarest first policy
short min = SHRT_MAX;
int nb_min_pieces = 0;
int current_index = 0;
// compute the smallest number of copies of available pieces
for (unsigned int i = 0; i < FILE_PIECES; i++) {
if (pieces_count[i] < min && remotePeerHasMissingPiece(remote_peer, i) && isNotDownloadingPiece(i))
min = pieces_count[i];
}
xbt_assert(min != SHRT_MAX || not isInterestedByFree(remote_peer));
// compute the number of rarest pieces
for (unsigned int i = 0; i < FILE_PIECES; i++)
if (pieces_count[i] == min && remotePeerHasMissingPiece(remote_peer, i) && isNotDownloadingPiece(i))
nb_min_pieces++;
xbt_assert(nb_min_pieces != 0 || not isInterestedByFree(remote_peer));
// get a random rarest piece
int random_rarest_index = 0;
if (nb_min_pieces > 0) {
random_rarest_index = random.uniform_int(0, nb_min_pieces - 1);
}
for (unsigned int i = 0; i < FILE_PIECES; i++)
if (pieces_count[i] == min && remotePeerHasMissingPiece(remote_peer, i) && isNotDownloadingPiece(i)) {
if (random_rarest_index == current_index) {
piece = i;
break;
}
current_index++;
}
xbt_assert(piece != -1 || not isInterestedByFree(remote_peer));
return piece;
}
}
void Peer::updateChokedPeers()
{
if (nbInterestedPeers() == 0)
return;
XBT_DEBUG("(%d) update_choked peers %zu active peers", id, active_peers.size());
// update the current round
round_ = (round_ + 1) % 3;
Connection* chosen_peer = nullptr;
// select first active peer and remove it from the set
Connection* choked_peer;
if (active_peers.empty()) {
choked_peer = nullptr;
} else {
choked_peer = *active_peers.begin();
active_peers.erase(choked_peer);
}
/**If we are currently seeding, we unchoke the peer which has been unchoked the last time.*/
if (hasFinished()) {
double unchoke_time = sg4::Engine::get_clock() + 1;
for (auto& [_, remote_peer] : connected_peers) {
if (remote_peer.last_unchoke < unchoke_time && remote_peer.interested && remote_peer.choked_upload) {
unchoke_time = remote_peer.last_unchoke;
chosen_peer = &remote_peer;
}
}
} else {
// Random optimistic unchoking
if (round_ == 0) {
int j = 0;
do {
// We choose a random peer to unchoke.
auto chosen_peer_it = connected_peers.begin();
std::advance(chosen_peer_it, random.uniform_int(0, static_cast<int>(connected_peers.size() - 1)));
chosen_peer = &chosen_peer_it->second;
if (not chosen_peer->interested || not chosen_peer->choked_upload)
chosen_peer = nullptr;
else
XBT_DEBUG("Nothing to do, keep going");
j++;
} while (chosen_peer == nullptr && j < MAXIMUM_PEERS);
} else {
// Use the "fastest download" policy.
double fastest_speed = 0.0;
for (auto& [_, remote_peer] : connected_peers) {
if (remote_peer.peer_speed > fastest_speed && remote_peer.choked_upload && remote_peer.interested) {
fastest_speed = remote_peer.peer_speed;
chosen_peer = &remote_peer;
}
}
}
}
if (chosen_peer != nullptr)
XBT_DEBUG("(%d) update_choked peers unchoked (%d) ; int (%d) ; choked (%d) ", id, chosen_peer->id,
chosen_peer->interested, chosen_peer->choked_upload);
if (choked_peer != chosen_peer) {
if (choked_peer != nullptr) {
xbt_assert(not choked_peer->choked_upload, "Tries to choked a choked peer");
choked_peer->choked_upload = true;
updateActivePeersSet(choked_peer);
XBT_DEBUG("(%d) Sending a CHOKE to %d", id, choked_peer->id);
sendMessage(choked_peer->mailbox_, MessageType::CHOKE, message_size(MessageType::CHOKE));
}
if (chosen_peer != nullptr) {
xbt_assert((chosen_peer->choked_upload), "Tries to unchoked an unchoked peer");
chosen_peer->choked_upload = false;
active_peers.insert(chosen_peer);
chosen_peer->last_unchoke = sg4::Engine::get_clock();
XBT_DEBUG("(%d) Sending a UNCHOKE to %d", id, chosen_peer->id);
updateActivePeersSet(chosen_peer);
sendMessage(chosen_peer->mailbox_, MessageType::UNCHOKE, message_size(MessageType::UNCHOKE));
}
}
}
/** @brief Update "interested" state of peers: send "not interested" to peers that don't have any more pieces we want.*/
void Peer::updateInterestedAfterReceive()
{
for (auto& [_, remote_peer] : connected_peers) {
if (remote_peer.am_interested) {
bool interested = false;
// Check if the peer still has a piece we want.
for (unsigned int i = 0; i < FILE_PIECES; i++)
if (remotePeerHasMissingPiece(&remote_peer, i)) {
interested = true;
break;
}
if (not interested) { // no more piece to download from connection
remote_peer.am_interested = false;
sendMessage(remote_peer.mailbox_, MessageType::NOTINTERESTED, message_size(MessageType::NOTINTERESTED));
}
}
}
}
void Peer::updateBitfieldBlocks(int piece, int block_index, int block_length)
{
xbt_assert((piece >= 0 && static_cast<unsigned int>(piece) <= FILE_PIECES), "Wrong piece.");
xbt_assert((block_index >= 0 && static_cast<unsigned int>(block_index) <= PIECES_BLOCKS), "Wrong block : %d.",
block_index);
for (int i = block_index; i < (block_index + block_length); i++)
bitfield_blocks |= (1ULL << static_cast<unsigned int>(piece * PIECES_BLOCKS + i));
}
bool Peer::hasCompletedPiece(unsigned int piece) const
{
for (unsigned int i = 0; i < PIECES_BLOCKS; i++)
if (not(bitfield_blocks & 1ULL << (piece * PIECES_BLOCKS + i)))
return false;
return true;
}
int Peer::getFirstMissingBlockFrom(int piece) const
{
for (unsigned int i = 0; i < PIECES_BLOCKS; i++)
if (not(bitfield_blocks & 1ULL << (piece * PIECES_BLOCKS + i)))
return i;
return -1;
}
/** Returns a piece that is partially downloaded and stored by the remote peer if any -1 otherwise. */
int Peer::partiallyDownloadedPiece(const Connection* remote_peer) const
{
for (unsigned int i = 0; i < FILE_PIECES; i++)
if (remotePeerHasMissingPiece(remote_peer, i) && isNotDownloadingPiece(i) && getFirstMissingBlockFrom(i) > 0)
return i;
return -1;
}
View examples/cpp/app-bittorrent/s4u-tracker.cpp
/* Copyright (c) 2012-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "s4u-tracker.hpp"
#include <algorithm>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_bt_tracker, "Messages specific for the tracker");
namespace sg4 = simgrid::s4u;
Tracker::Tracker(std::vector<std::string> args)
{
// Checking arguments
xbt_assert(args.size() == 2, "Wrong number of arguments for the tracker.");
// Retrieving end time
try {
deadline = std::stod(args[1]);
} catch (const std::invalid_argument&) {
throw std::invalid_argument("Invalid deadline:" + args[1]);
}
xbt_assert(deadline > 0, "Wrong deadline supplied");
mailbox = sg4::Mailbox::by_name(TRACKER_MAILBOX);
XBT_INFO("Tracker launched.");
}
void Tracker::operator()()
{
sg4::CommPtr comm = nullptr;
TrackerQuery* query = nullptr;
while (sg4::Engine::get_clock() < deadline) {
if (comm == nullptr)
comm = mailbox->get_async<TrackerQuery>(&query);
if (comm->test()) {
// Retrieve the data sent by the peer.
xbt_assert(query != nullptr);
// Add the peer to our peer list, if not already known.
known_peers.emplace(query->getPeerId());
// Sending back peers to the requesting peer
auto* answer = new TrackerAnswer(TRACKER_QUERY_INTERVAL);
std::set<int>::iterator next_peer;
int nb_known_peers = static_cast<int>(known_peers.size());
int max_tries = std::min(MAXIMUM_PEERS, nb_known_peers);
int tried = 0;
while (tried < max_tries) {
do {
next_peer = known_peers.begin();
std::advance(next_peer, random.uniform_int(0, nb_known_peers - 1));
} while (answer->getPeers().find(*next_peer) != answer->getPeers().end());
answer->addPeer(*next_peer);
tried++;
}
query->getReturnMailbox()->put_init(answer, TRACKER_COMM_SIZE)->detach();
delete query;
comm = nullptr;
} else {
sg4::this_actor::sleep_for(1);
}
}
XBT_INFO("Tracker is leaving");
}
View examples/c/app-bittorrent/app-bittorrent.c
/* Copyright (c) 2012-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "app-bittorrent.h"
#include "bittorrent-peer.h"
#include "tracker.h"
#include <simgrid/engine.h>
#include <xbt/asserts.h>
/** Bittorrent example launcher */
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
/* Check the arguments */
xbt_assert(argc > 2, "Usage: %s platform_file deployment_file", argv[0]);
simgrid_load_platform(argv[1]);
simgrid_register_function("tracker", tracker_run);
simgrid_register_function("peer", peer_run);
simgrid_load_deployment(argv[2]);
simgrid_run();
return 0;
}
View examples/c/app-bittorrent/bittorrent-peer.c
/* Copyright (c) 2012-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "bittorrent-peer.h"
#include "tracker.h"
#include <simgrid/forward.h>
#include <xbt/ex.h>
#include <limits.h>
#include <stdio.h> /* snprintf */
XBT_LOG_NEW_DEFAULT_CATEGORY(bittorrent_peers, "Messages specific for the peers");
/*
* User parameters for transferred file data. For the test, the default values are :
* File size: 10 pieces * 5 blocks/piece * 16384 bytes/block = 819200 bytes
*/
#define FILE_PIECES 10UL
#define PIECES_BLOCKS 5UL
#define BLOCK_SIZE 16384
/** Number of blocks asked by each request */
#define BLOCKS_REQUESTED 2UL
#define SLEEP_DURATION 1
#define BITS_TO_BYTES(x) (((x) / 8 + (x) % 8) ? 1 : 0)
const char* const message_type_names[10] = {"HANDSHAKE", "CHOKE", "UNCHOKE", "INTERESTED", "NOTINTERESTED",
"HAVE", "BITFIELD", "REQUEST", "PIECE", "CANCEL"};
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
static peer_t peer_init(int id, int seed)
{
peer_t peer = xbt_new(s_peer_t, 1);
peer->id = id;
char mailbox_name[MAILBOX_SIZE];
snprintf(mailbox_name, MAILBOX_SIZE - 1, "%d", id);
peer->mailbox = sg_mailbox_by_name(mailbox_name);
peer->connected_peers = xbt_dict_new_homogeneous(NULL);
peer->active_peers = xbt_dict_new_homogeneous(NULL);
if (seed) {
peer->bitfield = (1U << FILE_PIECES) - 1U;
peer->bitfield_blocks = (1ULL << (FILE_PIECES * PIECES_BLOCKS)) - 1ULL;
} else {
peer->bitfield = 0;
peer->bitfield_blocks = 0;
}
peer->current_pieces = 0;
peer->pieces_count = xbt_new0(short, FILE_PIECES);
peer->comm_received = NULL;
peer->round = 0;
return peer;
}
static void peer_free(peer_t peer)
{
char* key;
connection_t connection;
xbt_dict_cursor_t cursor;
xbt_dict_foreach (peer->connected_peers, cursor, key, connection)
xbt_free(connection);
xbt_dict_free(&peer->connected_peers);
xbt_dict_free(&peer->active_peers);
xbt_free(peer->pieces_count);
xbt_free(peer);
}
/** Peer main function */
void peer_run(int argc, char* argv[])
{
// Check arguments
xbt_assert(argc == 3 || argc == 4, "Wrong number of arguments");
// Build peer object
peer_t peer = peer_init((int)xbt_str_parse_int(argv[1], "Invalid ID"), argc == 4 ? 1 : 0);
// Retrieve deadline
peer->deadline = xbt_str_parse_double(argv[2], "Invalid deadline");
xbt_assert(peer->deadline > 0, "Wrong deadline supplied");
char* status = xbt_malloc0(FILE_PIECES + 1);
get_status(status, peer->bitfield);
XBT_INFO("Hi, I'm joining the network with id %d", peer->id);
// Getting peer data from the tracker.
if (get_peers_from_tracker(peer)) {
XBT_DEBUG("Got %d peers from the tracker. Current status is: %s", xbt_dict_length(peer->connected_peers), status);
peer->begin_receive_time = simgrid_get_clock();
sg_mailbox_set_receiver(sg_mailbox_get_name(peer->mailbox));
if (has_finished(peer->bitfield)) {
send_handshake_to_all_peers(peer);
} else {
leech(peer);
}
seed(peer);
} else {
XBT_INFO("Couldn't contact the tracker.");
}
get_status(status, peer->bitfield);
XBT_INFO("Here is my current status: %s", status);
if (peer->comm_received) {
sg_comm_unref(peer->comm_received);
}
xbt_free(status);
peer_free(peer);
}
/** @brief Retrieves the peer list from the tracker */
int get_peers_from_tracker(const_peer_t peer)
{
sg_mailbox_t tracker_mailbox = sg_mailbox_by_name(TRACKER_MAILBOX);
// Build the task to send to the tracker
tracker_query_t peer_request = tracker_query_new(peer->id, peer->mailbox);
XBT_DEBUG("Sending a peer request to the tracker.");
sg_comm_t request = sg_mailbox_put_async(tracker_mailbox, peer_request, TRACKER_COMM_SIZE);
sg_error_t res = sg_comm_wait_for(request, GET_PEERS_TIMEOUT);
if (res == SG_ERROR_TIMEOUT) {
XBT_DEBUG("Timeout expired when requesting peers to tracker");
xbt_free(peer_request);
return 0;
}
void* message = NULL;
sg_comm_t comm_received = sg_mailbox_get_async(peer->mailbox, &message);
res = sg_comm_wait_for(comm_received, GET_PEERS_TIMEOUT);
if (res == SG_OK) {
const_tracker_answer_t ta = (const_tracker_answer_t)message;
// Add the peers the tracker gave us to our peer list.
unsigned i;
int peer_id;
// Add the peers the tracker gave us to our peer list.
xbt_dynar_foreach (ta->peers, i, peer_id) {
if (peer_id != peer->id)
xbt_dict_set_ext(peer->connected_peers, (char*)&peer_id, sizeof(int), connection_new(peer_id));
}
tracker_answer_free(message);
} else if (res == SG_ERROR_TIMEOUT) {
XBT_DEBUG("Timeout expired when requesting peers to tracker");
tracker_answer_free(message);
return 0;
}
return 1;
}
/** @brief Send a handshake message to all the peers the peer has. */
void send_handshake_to_all_peers(const_peer_t peer)
{
connection_t remote_peer;
xbt_dict_cursor_t cursor = NULL;
char* key;
xbt_dict_foreach (peer->connected_peers, cursor, key, remote_peer) {
message_t handshake = message_new(MESSAGE_HANDSHAKE, peer->id, peer->mailbox);
sg_comm_t comm = sg_mailbox_put_init(remote_peer->mailbox, handshake, MESSAGE_HANDSHAKE_SIZE);
sg_comm_detach(comm, NULL);
XBT_DEBUG("Sending a HANDSHAKE to %s", sg_mailbox_get_name(remote_peer->mailbox));
}
}
void send_message(const_peer_t peer, sg_mailbox_t mailbox, e_message_type type, uint64_t size)
{
XBT_DEBUG("Sending %s to %s", message_type_names[type], sg_mailbox_get_name(mailbox));
message_t message = message_other_new(type, peer->id, peer->mailbox, peer->bitfield);
sg_comm_t comm = sg_mailbox_put_init(mailbox, message, size);
sg_comm_detach(comm, NULL);
}
/** @brief Send a bitfield message to all the peers the peer has */
void send_bitfield(const_peer_t peer, sg_mailbox_t mailbox)
{
XBT_DEBUG("Sending a BITFIELD to %s", sg_mailbox_get_name(mailbox));
message_t message = message_other_new(MESSAGE_BITFIELD, peer->id, peer->mailbox, peer->bitfield);
sg_comm_t comm = sg_mailbox_put_init(mailbox, message, MESSAGE_BITFIELD_SIZE + BITS_TO_BYTES(FILE_PIECES));
sg_comm_detach(comm, NULL);
}
/** Send a "piece" message to a pair, containing a piece of the file */
void send_piece(const_peer_t peer, sg_mailbox_t mailbox, int piece, int block_index, int block_length)
{
XBT_DEBUG("Sending the PIECE %d (%d,%d) to %s", piece, block_index, block_length, sg_mailbox_get_name(mailbox));
xbt_assert(piece >= 0, "Tried to send a piece that doesn't exist.");
xbt_assert(!peer_has_not_piece(peer, piece), "Tried to send a piece that we doesn't have.");
message_t message = message_piece_new(peer->id, peer->mailbox, piece, block_index, block_length);
sg_comm_t comm = sg_mailbox_put_init(mailbox, message, BLOCK_SIZE);
sg_comm_detach(comm, NULL);
}
/** Send a "HAVE" message to all peers we are connected to */
void send_have_to_all_peers(const_peer_t peer, int piece)
{
XBT_DEBUG("Sending HAVE message to all my peers");
connection_t remote_peer;
xbt_dict_cursor_t cursor = NULL;
char* key;
xbt_dict_foreach (peer->connected_peers, cursor, key, remote_peer) {
message_t message = message_index_new(MESSAGE_HAVE, peer->id, peer->mailbox, piece);
sg_comm_t comm = sg_mailbox_put_init(remote_peer->mailbox, message, MESSAGE_HAVE_SIZE);
sg_comm_detach(comm, NULL);
}
}
/** @brief Send request messages to a peer that have unchoked us */
void send_request_to_peer(const_peer_t peer, connection_t remote_peer, int piece)
{
remote_peer->current_piece = piece;
xbt_assert(connection_has_piece(remote_peer, piece));
int block_index = get_first_missing_block_from(peer, piece);
if (block_index != -1) {
int block_length = MIN(BLOCKS_REQUESTED, PIECES_BLOCKS - block_index);
XBT_DEBUG("Sending a REQUEST to %s for piece %d (%d,%d)", sg_mailbox_get_name(remote_peer->mailbox), piece,
block_index, block_length);
message_t message = message_request_new(peer->id, peer->mailbox, piece, block_index, block_length);
sg_comm_t comm = sg_mailbox_put_init(remote_peer->mailbox, message, MESSAGE_REQUEST_SIZE);
sg_comm_detach(comm, NULL);
}
}
void get_status(char* status, unsigned int bitfield)
{
for (int i = FILE_PIECES - 1; i >= 0; i--)
status[i] = (bitfield & (1U << i)) ? '1' : '0';
status[FILE_PIECES] = '\0';
}
int has_finished(unsigned int bitfield)
{
return bitfield == (1U << FILE_PIECES) - 1U;
}
/** Indicates if the remote peer has a piece not stored by the local peer */
int is_interested(const_peer_t peer, const_connection_t remote_peer)
{
return remote_peer->bitfield & (peer->bitfield ^ ((1 << FILE_PIECES) - 1));
}
/** Indicates if the remote peer has a piece not stored by the local peer nor requested by the local peer */
int is_interested_and_free(const_peer_t peer, const_connection_t remote_peer)
{
for (unsigned int i = 0; i < FILE_PIECES; i++)
if (peer_has_not_piece(peer, i) && connection_has_piece(remote_peer, i) && peer_is_not_downloading_piece(peer, i))
return 1;
return 0;
}
/** @brief Updates the list of who has a piece from a bitfield */
void update_pieces_count_from_bitfield(const_peer_t peer, unsigned int bitfield)
{
for (unsigned int i = 0; i < FILE_PIECES; i++)
if (bitfield & (1U << i))
peer->pieces_count[i]++;
}
unsigned int count_pieces(unsigned int bitfield)
{
unsigned int count = 0;
unsigned int n = bitfield;
while (n) {
count += n & 1U;
n >>= 1U;
}
return count;
}
int nb_interested_peers(const_peer_t peer)
{
xbt_dict_cursor_t cursor = NULL;
char* key;
connection_t connection;
int nb = 0;
xbt_dict_foreach (peer->connected_peers, cursor, key, connection)
if (connection->interested)
nb++;
return nb;
}
/** @brief Peer main loop when it is leeching. */
void leech(peer_t peer)
{
double next_choked_update = simgrid_get_clock() + UPDATE_CHOKED_INTERVAL;
XBT_DEBUG("Start downloading.");
/* Send a "handshake" message to all the peers it got (since it couldn't have gotten more than 50 peers) */
send_handshake_to_all_peers(peer);
XBT_DEBUG("Starting main leech loop");
void* data = NULL;
while (simgrid_get_clock() < peer->deadline && count_pieces(peer->bitfield) < FILE_PIECES) {
if (peer->comm_received == NULL)
peer->comm_received = sg_mailbox_get_async(peer->mailbox, &data);
if (sg_comm_test(peer->comm_received)) {
peer->message = (message_t)data;
handle_message(peer, peer->message);
xbt_free(peer->message);
peer->comm_received = NULL;
} else {
// We don't execute the choke algorithm if we don't already have a piece
if (simgrid_get_clock() >= next_choked_update && count_pieces(peer->bitfield) > 0) {
update_choked_peers(peer);
next_choked_update += UPDATE_CHOKED_INTERVAL;
} else {
sg_actor_sleep_for(SLEEP_DURATION);
}
}
}
if (has_finished(peer->bitfield))
XBT_DEBUG("%d becomes a seeder", peer->id);
}
/** @brief Peer main loop when it is seeding */
void seed(peer_t peer)
{
double next_choked_update = simgrid_get_clock() + UPDATE_CHOKED_INTERVAL;
XBT_DEBUG("Start seeding.");
// start the main seed loop
void* data = NULL;
while (simgrid_get_clock() < peer->deadline) {
if (peer->comm_received == NULL)
peer->comm_received = sg_mailbox_get_async(peer->mailbox, &data);
if (sg_comm_test(peer->comm_received)) {
peer->message = (message_t)data;
handle_message(peer, peer->message);
xbt_free(peer->message);
peer->comm_received = NULL;
} else {
if (simgrid_get_clock() >= next_choked_update) {
update_choked_peers(peer);
// TODO: Change the choked peer algorithm when seeding.
next_choked_update += UPDATE_CHOKED_INTERVAL;
} else {
sg_actor_sleep_for(SLEEP_DURATION);
}
}
}
}
void update_active_peers_set(const s_peer_t* peer, connection_t remote_peer)
{
if ((remote_peer->interested != 0) && (remote_peer->choked_upload == 0)) {
// add in the active peers set
xbt_dict_set_ext(peer->active_peers, (char*)&remote_peer->id, sizeof(int), remote_peer);
} else if (xbt_dict_get_or_null_ext(peer->active_peers, (char*)&remote_peer->id, sizeof(int))) {
xbt_dict_remove_ext(peer->active_peers, (char*)&remote_peer->id, sizeof(int));
}
}
/** @brief Handle a received message sent by another peer */
void handle_message(peer_t peer, message_t message)
{
XBT_DEBUG("Received a %s message from %s", message_type_names[message->type],
sg_mailbox_get_name(message->return_mailbox));
connection_t remote_peer = xbt_dict_get_or_null_ext(peer->connected_peers, (char*)&message->peer_id, sizeof(int));
xbt_assert(remote_peer != NULL || message->type == MESSAGE_HANDSHAKE,
"The impossible did happened: A not-in-our-list peer sent us a message.");
switch (message->type) {
case MESSAGE_HANDSHAKE:
// Check if the peer is in our connection list.
if (remote_peer == 0) {
xbt_dict_set_ext(peer->connected_peers, (char*)&message->peer_id, sizeof(int),
connection_new(message->peer_id));
send_message(peer, message->return_mailbox, MESSAGE_HANDSHAKE, MESSAGE_HANDSHAKE_SIZE);
}
// Send our bitfield to the peer
send_bitfield(peer, message->return_mailbox);
break;
case MESSAGE_BITFIELD:
// Update the pieces list
update_pieces_count_from_bitfield(peer, message->bitfield);
// Store the bitfield
remote_peer->bitfield = message->bitfield;
xbt_assert(!remote_peer->am_interested, "Should not be interested at first");
if (is_interested(peer, remote_peer)) {
remote_peer->am_interested = 1;
send_message(peer, message->return_mailbox, MESSAGE_INTERESTED, MESSAGE_INTERESTED_SIZE);
}
break;
case MESSAGE_INTERESTED:
xbt_assert((remote_peer != NULL), "A non-in-our-list peer has sent us a message. WTH ?");
// Update the interested state of the peer.
remote_peer->interested = 1;
update_active_peers_set(peer, remote_peer);
break;
case MESSAGE_NOTINTERESTED:
xbt_assert((remote_peer != NULL), "A non-in-our-list peer has sent us a message. WTH ?");
remote_peer->interested = 0;
update_active_peers_set(peer, remote_peer);
break;
case MESSAGE_UNCHOKE:
xbt_assert((remote_peer != NULL), "A non-in-our-list peer has sent us a message. WTH ?");
xbt_assert(remote_peer->choked_download);
remote_peer->choked_download = 0;
// Send requests to the peer, since it has unchoked us
if (remote_peer->am_interested)
request_new_piece_to_peer(peer, remote_peer);
break;
case MESSAGE_CHOKE:
xbt_assert((remote_peer != NULL), "A non-in-our-list peer has sent us a message. WTH ?");
xbt_assert(!remote_peer->choked_download);
remote_peer->choked_download = 1;
if (remote_peer->current_piece != -1)
remove_current_piece(peer, remote_peer, remote_peer->current_piece);
break;
case MESSAGE_HAVE:
XBT_DEBUG("\t for piece %d", message->piece);
xbt_assert((message->piece >= 0 && (unsigned)message->piece < FILE_PIECES), "Wrong HAVE message received");
remote_peer->bitfield = remote_peer->bitfield | (1U << message->piece);
peer->pieces_count[message->piece]++;
// If the piece is in our pieces, we tell the peer that we are interested.
if ((remote_peer->am_interested == 0) && peer_has_not_piece(peer, message->piece)) {
remote_peer->am_interested = 1;
send_message(peer, message->return_mailbox, MESSAGE_INTERESTED, MESSAGE_INTERESTED_SIZE);
if (remote_peer->choked_download == 0)
request_new_piece_to_peer(peer, remote_peer);
}
break;
case MESSAGE_REQUEST:
xbt_assert(remote_peer->interested);
xbt_assert((message->piece >= 0 && (unsigned)message->piece < FILE_PIECES), "Wrong request received");
if (remote_peer->choked_upload == 0) {
XBT_DEBUG("\t for piece %d (%d,%d)", message->piece, message->block_index,
message->block_index + message->block_length);
if (!peer_has_not_piece(peer, message->piece)) {
send_piece(peer, message->return_mailbox, message->piece, message->block_index, message->block_length);
}
} else {
XBT_DEBUG("\t for piece %d but he is choked.", message->peer_id);
}
break;
case MESSAGE_PIECE:
XBT_DEBUG(" \t for piece %d (%d,%d)", message->piece, message->block_index,
message->block_index + message->block_length);
xbt_assert(!remote_peer->choked_download);
xbt_assert(remote_peer->choked_download != 1, "Can't received a piece if I'm choked !");
xbt_assert((message->piece >= 0 && (unsigned)message->piece < FILE_PIECES), "Wrong piece received");
// TODO: Execute a computation.
if (peer_has_not_piece(peer, message->piece)) {
update_bitfield_blocks(peer, message->piece, message->block_index, message->block_length);
if (piece_complete(peer, message->piece)) {
// Removing the piece from our piece list
remove_current_piece(peer, remote_peer, message->piece);
// Setting the fact that we have the piece
peer->bitfield = peer->bitfield | (1U << message->piece);
char* status = xbt_malloc0(FILE_PIECES + 1);
get_status(status, peer->bitfield);
XBT_DEBUG("My status is now %s", status);
xbt_free(status);
// Sending the information to all the peers we are connected to
send_have_to_all_peers(peer, message->piece);
// sending UNINTERESTED to peers that do not have what we want.
update_interested_after_receive(peer);
} else { // piece not completed
send_request_to_peer(peer, remote_peer, message->piece); // ask for the next block
}
} else {
XBT_DEBUG("However, we already have it");
request_new_piece_to_peer(peer, remote_peer);
}
break;
case MESSAGE_CANCEL:
break;
default:
THROW_IMPOSSIBLE;
}
// Update the peer speed.
if (remote_peer) {
connection_add_speed_value(remote_peer, 1.0 / (simgrid_get_clock() - peer->begin_receive_time));
}
peer->begin_receive_time = simgrid_get_clock();
}
/** Selects the appropriate piece to download and requests it to the remote_peer */
void request_new_piece_to_peer(peer_t peer, connection_t remote_peer)
{
int piece = select_piece_to_download(peer, remote_peer);
if (piece != -1) {
peer->current_pieces |= (1U << (unsigned int)piece);
send_request_to_peer(peer, remote_peer, piece);
}
}
/** remove current_piece from the list of currently downloaded pieces. */
void remove_current_piece(peer_t peer, connection_t remote_peer, unsigned int current_piece)
{
peer->current_pieces &= ~(1U << current_piece);
remote_peer->current_piece = -1;
}
/** @brief Return the piece to be downloaded
* There are two cases (as described in "Bittorrent Architecture Protocol", Ryan Toole :
* If a piece is partially downloaded, this piece will be selected prioritarily
* If the peer has strictly less than 4 pieces, he chooses a piece at random.
* If the peer has more than pieces, he downloads the pieces that are the less replicated (rarest policy).
* If all pieces have been downloaded or requested, we select a random requested piece (endgame mode).
* @param peer: local peer
* @param remote_peer: information about the connection
* @return the piece to download if possible. -1 otherwise
*/
int select_piece_to_download(const_peer_t peer, const_connection_t remote_peer)
{
int piece = partially_downloaded_piece(peer, remote_peer);
// strict priority policy
if (piece != -1)
return piece;
// end game mode
if (count_pieces(peer->current_pieces) >= (FILE_PIECES - count_pieces(peer->bitfield)) &&
(is_interested(peer, remote_peer) != 0)) {
int nb_interesting_pieces = 0;
// compute the number of interesting pieces
for (unsigned int i = 0; i < FILE_PIECES; i++) {
if (peer_has_not_piece(peer, i) && connection_has_piece(remote_peer, i)) {
nb_interesting_pieces++;
}
}
xbt_assert(nb_interesting_pieces != 0);
// get a random interesting piece
int random_piece_index = rand() % nb_interesting_pieces;
int current_index = 0;
for (unsigned int i = 0; i < FILE_PIECES; i++) {
if (peer_has_not_piece(peer, i) && connection_has_piece(remote_peer, i)) {
if (random_piece_index == current_index) {
piece = i;
break;
}
current_index++;
}
}
xbt_assert(piece != -1);
return piece;
}
// Random first policy
if (count_pieces(peer->bitfield) < 4 && (is_interested_and_free(peer, remote_peer) != 0)) {
int nb_interesting_pieces = 0;
// compute the number of interesting pieces
for (unsigned int i = 0; i < FILE_PIECES; i++) {
if (peer_has_not_piece(peer, i) && connection_has_piece(remote_peer, i) &&
peer_is_not_downloading_piece(peer, i)) {
nb_interesting_pieces++;
}
}
xbt_assert(nb_interesting_pieces != 0);
// get a random interesting piece
int random_piece_index = rand() % nb_interesting_pieces;
int current_index = 0;
for (unsigned int i = 0; i < FILE_PIECES; i++) {
if (peer_has_not_piece(peer, i) && connection_has_piece(remote_peer, i) &&
peer_is_not_downloading_piece(peer, i)) {
if (random_piece_index == current_index) {
piece = i;
break;
}
current_index++;
}
}
xbt_assert(piece != -1);
return piece;
} else { // Rarest first policy
short min = SHRT_MAX;
int nb_min_pieces = 0;
int current_index = 0;
// compute the smallest number of copies of available pieces
for (unsigned int i = 0; i < FILE_PIECES; i++) {
if (peer->pieces_count[i] < min && peer_has_not_piece(peer, i) && connection_has_piece(remote_peer, i) &&
peer_is_not_downloading_piece(peer, i))
min = peer->pieces_count[i];
}
xbt_assert(min != SHRT_MAX || (is_interested_and_free(peer, remote_peer) == 0));
// compute the number of rarest pieces
for (unsigned int i = 0; i < FILE_PIECES; i++) {
if (peer->pieces_count[i] == min && peer_has_not_piece(peer, i) && connection_has_piece(remote_peer, i) &&
peer_is_not_downloading_piece(peer, i))
nb_min_pieces++;
}
xbt_assert(nb_min_pieces != 0 || (is_interested_and_free(peer, remote_peer) == 0));
// get a random rarest piece
int random_rarest_index = 0;
if (nb_min_pieces > 0) {
random_rarest_index = rand() % nb_min_pieces;
}
for (unsigned int i = 0; i < FILE_PIECES; i++) {
if (peer->pieces_count[i] == min && peer_has_not_piece(peer, i) && connection_has_piece(remote_peer, i) &&
peer_is_not_downloading_piece(peer, i)) {
if (random_rarest_index == current_index) {
piece = i;
break;
}
current_index++;
}
}
xbt_assert(piece != -1 || (is_interested_and_free(peer, remote_peer) == 0));
return piece;
}
}
/** Update the list of current choked and unchoked peers, using the choke algorithm */
void update_choked_peers(peer_t peer)
{
if (nb_interested_peers(peer) == 0)
return;
XBT_DEBUG("(%d) update_choked peers %u active peers", peer->id, xbt_dict_size(peer->active_peers));
// update the current round
peer->round = (peer->round + 1) % 3;
char* key;
char* choked_key = NULL;
connection_t chosen_peer = NULL;
connection_t choked_peer = NULL;
// remove a peer from the list
xbt_dict_cursor_t cursor = NULL;
xbt_dict_cursor_first(peer->active_peers, &cursor);
if (!xbt_dict_is_empty(peer->active_peers)) {
choked_key = xbt_dict_cursor_get_key(cursor);
choked_peer = xbt_dict_cursor_get_data(cursor);
}
xbt_dict_cursor_free(&cursor);
/**If we are currently seeding, we unchoke the peer which has been unchoked the last time.*/
if (has_finished(peer->bitfield)) {
connection_t connection;
double unchoke_time = simgrid_get_clock() + 1;
xbt_dict_foreach (peer->connected_peers, cursor, key, connection) {
if (connection->last_unchoke < unchoke_time && (connection->interested != 0) &&
(connection->choked_upload != 0)) {
unchoke_time = connection->last_unchoke;
chosen_peer = connection;
}
}
} else {
// Random optimistic unchoking
if (peer->round == 0) {
int j = 0;
do {
// We choose a random peer to unchoke.
int id_chosen = 0;
if (xbt_dict_length(peer->connected_peers) > 0) {
id_chosen = rand() % xbt_dict_length(peer->connected_peers);
}
int i = 0;
connection_t connection;
xbt_dict_foreach (peer->connected_peers, cursor, key, connection) {
if (i == id_chosen) {
chosen_peer = connection;
break;
}
i++;
}
xbt_dict_cursor_free(&cursor);
xbt_assert(chosen_peer != NULL, "A peer should have been selected at this point");
if ((chosen_peer->interested == 0) || (chosen_peer->choked_upload == 0))
chosen_peer = NULL;
else
XBT_DEBUG("Nothing to do, keep going");
j++;
} while (chosen_peer == NULL && j < MAXIMUM_PEERS);
} else {
// Use the "fastest download" policy.
connection_t connection;
double fastest_speed = 0.0;
xbt_dict_foreach (peer->connected_peers, cursor, key, connection) {
if (connection->peer_speed > fastest_speed && (connection->choked_upload != 0) &&
(connection->interested != 0)) {
chosen_peer = connection;
fastest_speed = connection->peer_speed;
}
}
}
}
if (chosen_peer != NULL)
XBT_DEBUG("(%d) update_choked peers unchoked (%d) ; int (%d) ; choked (%d) ", peer->id, chosen_peer->id,
chosen_peer->interested, chosen_peer->choked_upload);
if (choked_peer != chosen_peer) {
if (choked_peer != NULL) {
xbt_assert((!choked_peer->choked_upload), "Tries to choked a choked peer");
choked_peer->choked_upload = 1;
xbt_assert((*((int*)choked_key) == choked_peer->id));
update_active_peers_set(peer, choked_peer);
XBT_DEBUG("(%d) Sending a CHOKE to %d", peer->id, choked_peer->id);
send_message(peer, choked_peer->mailbox, MESSAGE_CHOKE, MESSAGE_CHOKE_SIZE);
}
if (chosen_peer != NULL) {
xbt_assert((chosen_peer->choked_upload), "Tries to unchoked an unchoked peer");
chosen_peer->choked_upload = 0;
xbt_dict_set_ext(peer->active_peers, (char*)&chosen_peer->id, sizeof(int), chosen_peer);
chosen_peer->last_unchoke = simgrid_get_clock();
XBT_DEBUG("(%d) Sending a UNCHOKE to %d", peer->id, chosen_peer->id);
update_active_peers_set(peer, chosen_peer);
send_message(peer, chosen_peer->mailbox, MESSAGE_UNCHOKE, MESSAGE_UNCHOKE_SIZE);
}
}
}
/** Update "interested" state of peers: send "not interested" to peers that don't have any more pieces we want. */
void update_interested_after_receive(const_peer_t peer)
{
char* key;
xbt_dict_cursor_t cursor;
connection_t connection;
xbt_dict_foreach (peer->connected_peers, cursor, key, connection) {
if (connection->am_interested != 0) {
int interested = 0;
// Check if the peer still has a piece we want.
for (unsigned int i = 0; i < FILE_PIECES; i++) {
if (peer_has_not_piece(peer, i) && connection_has_piece(connection, i)) {
interested = 1;
break;
}
}
if (!interested) { // no more piece to download from connection
connection->am_interested = 0;
send_message(peer, connection->mailbox, MESSAGE_NOTINTERESTED, MESSAGE_NOTINTERESTED_SIZE);
}
}
}
}
void update_bitfield_blocks(peer_t peer, int index, int block_index, int block_length)
{
xbt_assert((index >= 0 && (unsigned)index <= FILE_PIECES), "Wrong piece.");
xbt_assert((block_index >= 0 && (unsigned)block_index <= PIECES_BLOCKS), "Wrong block : %d.", block_index);
for (int i = block_index; i < (block_index + block_length); i++) {
peer->bitfield_blocks |= (1ULL << (unsigned int)(index * PIECES_BLOCKS + i));
}
}
/** Returns if a peer has completed the download of a piece */
int piece_complete(const_peer_t peer, int index)
{
for (unsigned int i = 0; i < PIECES_BLOCKS; i++) {
if (!(peer->bitfield_blocks & 1ULL << (index * PIECES_BLOCKS + i))) {
return 0;
}
}
return 1;
}
/** Returns the first block that a peer doesn't have in a piece. If the peer has all blocks of the piece, returns -1. */
int get_first_missing_block_from(const_peer_t peer, int piece)
{
for (unsigned int i = 0; i < PIECES_BLOCKS; i++) {
if (!(peer->bitfield_blocks & 1ULL << (piece * PIECES_BLOCKS + i))) {
return i;
}
}
return -1;
}
/** Returns a piece that is partially downloaded and stored by the remote peer if any -1 otherwise. */
int partially_downloaded_piece(const_peer_t peer, const_connection_t remote_peer)
{
for (unsigned int i = 0; i < FILE_PIECES; i++) {
if (peer_has_not_piece(peer, i) && connection_has_piece(remote_peer, i) && peer_is_not_downloading_piece(peer, i) &&
get_first_missing_block_from(peer, i) > 0)
return i;
}
return -1;
}
int peer_has_not_piece(const_peer_t peer, unsigned int piece)
{
return !(peer->bitfield & 1U << piece);
}
/** Check that a piece is not currently being download by the peer. */
int peer_is_not_downloading_piece(const_peer_t peer, unsigned int piece)
{
return !(peer->current_pieces & 1U << piece);
}
/***************** Connection internal functions ***********************/
connection_t connection_new(int id)
{
connection_t connection = xbt_new(s_connection_t, 1);
char mailbox_name[MAILBOX_SIZE];
snprintf(mailbox_name, MAILBOX_SIZE - 1, "%d", id);
connection->id = id;
connection->mailbox = sg_mailbox_by_name(mailbox_name);
connection->bitfield = 0;
connection->peer_speed = 0;
connection->last_unchoke = 0;
connection->current_piece = -1;
connection->am_interested = 0;
connection->interested = 0;
connection->choked_upload = 1;
connection->choked_download = 1;
return connection;
}
void connection_add_speed_value(connection_t connection, double speed)
{
connection->peer_speed = connection->peer_speed * 0.6 + speed * 0.4;
}
int connection_has_piece(const_connection_t connection, unsigned int piece)
{
return (connection->bitfield & 1U << piece);
}
/***************** Messages creation functions ***********************/
/** @brief Build a new empty message */
message_t message_new(e_message_type type, int peer_id, sg_mailbox_t return_mailbox)
{
message_t message = xbt_new(s_message_t, 1);
message->peer_id = peer_id;
message->return_mailbox = return_mailbox;
message->type = type;
return message;
}
/** Builds a message containing an index. */
message_t message_index_new(e_message_type type, int peer_id, sg_mailbox_t return_mailbox, int index)
{
message_t message = message_new(type, peer_id, return_mailbox);
message->piece = index;
return message;
}
message_t message_other_new(e_message_type type, int peer_id, sg_mailbox_t return_mailbox, unsigned int bitfield)
{
message_t message = message_new(type, peer_id, return_mailbox);
message->bitfield = bitfield;
return message;
}
message_t message_request_new(int peer_id, sg_mailbox_t return_mailbox, int piece, int block_index, int block_length)
{
message_t message = message_index_new(MESSAGE_REQUEST, peer_id, return_mailbox, piece);
message->block_index = block_index;
message->block_length = block_length;
return message;
}
message_t message_piece_new(int peer_id, sg_mailbox_t return_mailbox, int piece, int block_index, int block_length)
{
message_t message = message_index_new(MESSAGE_PIECE, peer_id, return_mailbox, piece);
message->block_index = block_index;
message->block_length = block_length;
return message;
}
View examples/c/app-bittorrent/tracker.c
/* Copyright (c) 2012-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "tracker.h"
#include "simgrid/actor.h"
#include "simgrid/comm.h"
#include "simgrid/engine.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(bittorrent_tracker, "Messages specific for the tracker");
void tracker_answer_free(void* data)
{
tracker_answer_t a = (tracker_answer_t)data;
xbt_dynar_free(&a->peers);
xbt_free(a);
}
static int is_in_list(const_xbt_dynar_t peers, int id)
{
return xbt_dynar_member(peers, &id);
}
void tracker_run(int argc, char* argv[])
{
// Checking arguments
xbt_assert(argc == 2, "Wrong number of arguments for the tracker.");
// Retrieving end time
double deadline = xbt_str_parse_double(argv[1], "Invalid deadline");
xbt_assert(deadline > 0, "Wrong deadline supplied");
// Building peers array
xbt_dynar_t peers_list = xbt_dynar_new(sizeof(int), NULL);
sg_mailbox_t mailbox = sg_mailbox_by_name(TRACKER_MAILBOX);
XBT_INFO("Tracker launched.");
sg_comm_t comm_received = NULL;
void* received = NULL;
while (simgrid_get_clock() < deadline) {
if (comm_received == NULL)
comm_received = sg_mailbox_get_async(mailbox, &received);
if (sg_comm_test(comm_received)) {
// Retrieve the data sent by the peer.
xbt_assert(received != NULL);
tracker_query_t tq = (tracker_query_t)received;
// Add the peer to our peer list.
if (!is_in_list(peers_list, tq->peer_id))
xbt_dynar_push_as(peers_list, int, tq->peer_id);
// Sending peers to the requesting peer
tracker_answer_t ta = tracker_answer_new(TRACKER_QUERY_INTERVAL);
int next_peer;
int peers_length = (int)xbt_dynar_length(peers_list);
for (int i = 0; i < MAXIMUM_PEERS && i < peers_length; i++) {
do {
next_peer = xbt_dynar_get_as(peers_list, rand() % peers_length, int);
} while (is_in_list(ta->peers, next_peer));
xbt_dynar_push_as(ta->peers, int, next_peer);
}
// sending the task back to the peer.
sg_comm_t answer = sg_mailbox_put_init(tq->return_mailbox, ta, TRACKER_COMM_SIZE);
sg_comm_detach(answer, tracker_answer_free);
xbt_free(tq);
comm_received = NULL;
received = NULL;
} else {
sg_actor_sleep_for(1);
}
}
// Free the remaining communication if any
if (comm_received)
sg_comm_unref(comm_received);
// Free the peers list
xbt_dynar_free(&peers_list);
XBT_INFO("Tracker is leaving");
}
tracker_query_t tracker_query_new(int peer_id, sg_mailbox_t return_mailbox)
{
tracker_query_t tq = xbt_new(s_tracker_query_t, 1);
tq->peer_id = peer_id;
tq->return_mailbox = return_mailbox;
return tq;
}
tracker_answer_t tracker_answer_new(int interval)
{
tracker_answer_t ta = xbt_new(s_tracker_answer_t, 1);
ta->interval = interval;
ta->peers = xbt_dynar_new(sizeof(int), NULL);
return ta;
}
Chained Send
Data broadcast over a ring of processes.
View examples/cpp/app-chainsend/s4u-app-chainsend.cpp
Download s4u-app-chainsend.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <vector>
constexpr unsigned PIECE_SIZE = 65536;
constexpr unsigned MESSAGE_BUILD_CHAIN_SIZE = 40;
constexpr unsigned MESSAGE_SEND_DATA_HEADER_SIZE = 1;
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_chainsend, "Messages specific for chainsend");
namespace sg4 = simgrid::s4u;
class ChainMessage {
public:
sg4::Mailbox* prev_ = nullptr;
sg4::Mailbox* next_ = nullptr;
unsigned int num_pieces = 0;
explicit ChainMessage(sg4::Mailbox* prev, sg4::Mailbox* next, const unsigned int num_pieces)
: prev_(prev), next_(next), num_pieces(num_pieces)
{
}
};
class FilePiece {
public:
FilePiece() = default;
};
class Peer {
public:
sg4::Mailbox* prev = nullptr;
sg4::Mailbox* next = nullptr;
sg4::Mailbox* me = nullptr;
sg4::ActivitySet pending_recvs;
sg4::ActivitySet pending_sends;
unsigned long long received_bytes = 0;
unsigned int received_pieces = 0;
unsigned int total_pieces = 0;
Peer() { me = sg4::Mailbox::by_name(sg4::Host::current()->get_cname()); }
void joinChain()
{
auto msg = me->get_unique<ChainMessage>();
prev = msg->prev_;
next = msg->next_;
total_pieces = msg->num_pieces;
XBT_DEBUG("Peer %s got a 'BUILD_CHAIN' message (prev: %s / next: %s)", me->get_cname(),
prev ? prev->get_cname() : nullptr, next ? next->get_cname() : nullptr);
}
void forwardFile()
{
FilePiece* received;
bool done = false;
while (not done) {
sg4::CommPtr comm = me->get_async<FilePiece>(&received);
pending_recvs.push(comm);
auto completed_one = pending_recvs.wait_any();
if (completed_one != nullptr) {
comm = boost::dynamic_pointer_cast<sg4::Comm>(completed_one);
XBT_DEBUG("Peer %s got a 'SEND_DATA' message", me->get_cname());
if (next != nullptr) {
XBT_DEBUG("Sending (asynchronously) from %s to %s", me->get_cname(), next->get_cname());
sg4::CommPtr send = next->put_async(received, MESSAGE_SEND_DATA_HEADER_SIZE + PIECE_SIZE);
pending_sends.push(send);
} else
delete received;
received_pieces++;
received_bytes += PIECE_SIZE;
XBT_DEBUG("%u pieces received, %llu bytes received", received_pieces, received_bytes);
if (received_pieces >= total_pieces) {
done = true;
}
}
}
}
};
class Broadcaster {
public:
sg4::Mailbox* first = nullptr;
std::vector<sg4::Mailbox*> mailboxes;
unsigned int piece_count;
void buildChain()
{
/* Build the chain if there's at least one peer */
if (not mailboxes.empty())
first = mailboxes.front();
for (unsigned i = 0; i < mailboxes.size(); i++) {
sg4::Mailbox* prev = i > 0 ? mailboxes[i - 1] : nullptr;
sg4::Mailbox* next = i < mailboxes.size() - 1 ? mailboxes[i + 1] : nullptr;
XBT_DEBUG("Building chain--broadcaster:\"%s\" dest:\"%s\" prev:\"%s\" next:\"%s\"",
sg4::Host::current()->get_cname(), mailboxes[i]->get_cname(), prev ? prev->get_cname() : nullptr,
next ? next->get_cname() : nullptr);
/* Send message to current peer */
mailboxes[i]->put(new ChainMessage(prev, next, piece_count), MESSAGE_BUILD_CHAIN_SIZE);
}
}
void sendFile()
{
sg4::ActivitySet pending_sends;
for (unsigned int current_piece = 0; current_piece < piece_count; current_piece++) {
XBT_DEBUG("Sending (send) piece %u from %s into mailbox %s", current_piece, sg4::Host::current()->get_cname(),
first->get_cname());
sg4::CommPtr comm = first->put_async(new FilePiece(), MESSAGE_SEND_DATA_HEADER_SIZE + PIECE_SIZE);
pending_sends.push(comm);
}
pending_sends.wait_all();
}
Broadcaster(int hostcount, unsigned int piece_count) : piece_count(piece_count)
{
for (int i = 1; i <= hostcount; i++) {
std::string name = "node-" + std::to_string(i) + ".simgrid.org";
XBT_DEBUG("%s", name.c_str());
mailboxes.push_back(sg4::Mailbox::by_name(name));
}
}
};
static void peer()
{
XBT_DEBUG("peer");
Peer p;
double start_time = sg4::Engine::get_clock();
p.joinChain();
p.forwardFile();
p.pending_sends.wait_all();
double end_time = sg4::Engine::get_clock();
XBT_INFO("### %f %llu bytes (Avg %f MB/s); copy finished (simulated).", end_time - start_time, p.received_bytes,
p.received_bytes / 1024.0 / 1024.0 / (end_time - start_time));
}
static void broadcaster(int hostcount, unsigned int piece_count)
{
XBT_DEBUG("broadcaster");
Broadcaster bc(hostcount, piece_count);
bc.buildChain();
bc.sendFile();
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
sg4::Actor::create("broadcaster", e.host_by_name("node-0.simgrid.org"), broadcaster, 8, 256);
sg4::Actor::create("peer", e.host_by_name("node-1.simgrid.org"), peer);
sg4::Actor::create("peer", e.host_by_name("node-2.simgrid.org"), peer);
sg4::Actor::create("peer", e.host_by_name("node-3.simgrid.org"), peer);
sg4::Actor::create("peer", e.host_by_name("node-4.simgrid.org"), peer);
sg4::Actor::create("peer", e.host_by_name("node-5.simgrid.org"), peer);
sg4::Actor::create("peer", e.host_by_name("node-6.simgrid.org"), peer);
sg4::Actor::create("peer", e.host_by_name("node-7.simgrid.org"), peer);
sg4::Actor::create("peer", e.host_by_name("node-8.simgrid.org"), peer);
e.run();
XBT_INFO("Total simulation time: %e", sg4::Engine::get_clock());
return 0;
}
View examples/c/app-chainsend/chainsend.c
/* Copyright (c) 2007-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "chainsend.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(chainsend, "Messages specific for chainsend");
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
simgrid_load_platform(argv[1]);
/* Application deployment */
simgrid_register_function("broadcaster", broadcaster);
simgrid_register_function("peer", peer);
simgrid_load_deployment(argv[2]);
simgrid_run();
XBT_INFO("Total simulation time: %e", simgrid_get_clock());
return 0;
}
View examples/c/app-chainsend/broadcaster.c
/* Copyright (c) 2012-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "chainsend.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(broadcaster, "Messages specific for the broadcaster");
static chain_message_t chain_message_new(sg_mailbox_t prev, sg_mailbox_t next, const unsigned int num_pieces)
{
chain_message_t msg = xbt_malloc(sizeof(s_chain_message_t));
msg->prev_ = prev;
msg->next_ = next;
msg->num_pieces = num_pieces;
return msg;
}
static void broadcaster_build_chain(broadcaster_t bc)
{
/* Build the chain if there's at least one peer */
if (bc->host_count > 0)
bc->first = bc->mailboxes[0];
for (unsigned i = 0; i < bc->host_count; i++) {
sg_mailbox_t prev = i > 0 ? bc->mailboxes[i - 1] : NULL;
sg_mailbox_t next = i < bc->host_count - 1 ? bc->mailboxes[i + 1] : NULL;
XBT_DEBUG("Building chain--broadcaster:\"%s\" dest:\"%s\" prev:\"%s\" next:\"%s\"", sg_host_self_get_name(),
sg_mailbox_get_name(bc->mailboxes[i]), prev ? sg_mailbox_get_name(prev) : NULL,
next ? sg_mailbox_get_name(next) : NULL);
/* Send message to current peer */
sg_mailbox_put(bc->mailboxes[i], chain_message_new(prev, next, bc->piece_count), MESSAGE_BUILD_CHAIN_SIZE);
}
}
static void broadcaster_send_file(const_broadcaster_t bc)
{
for (unsigned int current_piece = 0; current_piece < bc->piece_count; current_piece++) {
XBT_DEBUG("Sending (send) piece %u from %s into mailbox %s", current_piece, sg_host_self_get_name(),
sg_mailbox_get_name(bc->first));
char* file_piece = bprintf("piece-%u", current_piece);
sg_comm_t comm = sg_mailbox_put_async(bc->first, file_piece, MESSAGE_SEND_DATA_HEADER_SIZE + PIECE_SIZE);
sg_activity_set_push(bc->pending_sends, (sg_activity_t)comm);
}
sg_activity_set_wait_all(bc->pending_sends);
}
static broadcaster_t broadcaster_init(sg_mailbox_t* mailboxes, unsigned int host_count, unsigned int piece_count)
{
broadcaster_t bc = xbt_malloc(sizeof(s_broadcaster_t));
bc->first = NULL;
bc->host_count = host_count;
bc->piece_count = piece_count;
bc->mailboxes = mailboxes;
bc->pending_sends = sg_activity_set_init();
broadcaster_build_chain(bc);
return bc;
}
static void broadcaster_destroy(broadcaster_t bc)
{
sg_activity_set_delete(bc->pending_sends);
xbt_free(bc->mailboxes);
xbt_free(bc);
}
/** Emitter function */
void broadcaster(int argc, char* argv[])
{
XBT_DEBUG("broadcaster");
xbt_assert(argc > 2);
unsigned int host_count = (unsigned int)xbt_str_parse_int(argv[1], "Invalid number of peers");
sg_mailbox_t* mailboxes = xbt_malloc(sizeof(sg_mailbox_t) * host_count);
for (unsigned int i = 1; i <= host_count; i++) {
char* name = bprintf("node-%u.simgrid.org", i);
XBT_DEBUG("%s", name);
mailboxes[i - 1] = sg_mailbox_by_name(name);
free(name);
}
unsigned int piece_count = (unsigned int)xbt_str_parse_int(argv[2], "Invalid number of pieces");
broadcaster_t bc = broadcaster_init(mailboxes, host_count, piece_count);
broadcaster_send_file(bc);
broadcaster_destroy(bc);
}
View examples/c/app-chainsend/peer.c
/* Copyright (c) 2012-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "chainsend.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(chainsend_peer, "Messages specific for the peer");
static void peer_join_chain(peer_t p)
{
chain_message_t msg = (chain_message_t)sg_mailbox_get(p->me);
p->prev = msg->prev_;
p->next = msg->next_;
p->total_pieces = msg->num_pieces;
XBT_DEBUG("Peer %s got a 'BUILD_CHAIN' message (prev: %s / next: %s)", sg_mailbox_get_name(p->me),
p->prev ? sg_mailbox_get_name(p->prev) : NULL, p->next ? sg_mailbox_get_name(p->next) : NULL);
xbt_free(msg);
}
static void peer_forward_file(peer_t p)
{
void* received;
int done = 0;
while (!done) {
sg_activity_set_push(p->pending_recvs, (sg_activity_t)sg_mailbox_get_async(p->me, &received));
sg_activity_t acti = sg_activity_set_wait_any(p->pending_recvs);
if (acti != NULL) {
sg_comm_unref((sg_comm_t)acti);
XBT_DEBUG("Peer %s got a 'SEND_DATA' message", sg_mailbox_get_name(p->me));
if (p->next != NULL) {
XBT_DEBUG("Sending %s (asynchronously) from %s to %s", (char*)received, sg_mailbox_get_name(p->me),
sg_mailbox_get_name(p->next));
sg_comm_t send = sg_mailbox_put_async(p->next, received, MESSAGE_SEND_DATA_HEADER_SIZE + PIECE_SIZE);
sg_activity_set_push(p->pending_sends, (sg_activity_t)send);
} else
free(received);
p->received_pieces++;
p->received_bytes += PIECE_SIZE;
XBT_DEBUG("%u pieces received, %llu bytes received", p->received_pieces, p->received_bytes);
if (p->received_pieces >= p->total_pieces) {
done = 1;
}
}
}
sg_activity_set_wait_all(p->pending_sends);
}
static peer_t peer_init(int argc, char* argv[])
{
peer_t p = xbt_malloc(sizeof(s_peer_t));
p->prev = NULL;
p->next = NULL;
p->received_pieces = 0;
p->received_bytes = 0;
p->pending_recvs = sg_activity_set_init();
p->pending_sends = sg_activity_set_init();
p->me = sg_mailbox_by_name(sg_host_self_get_name());
return p;
}
static void peer_delete(peer_t p)
{
sg_activity_set_delete(p->pending_recvs);
sg_activity_set_delete(p->pending_sends);
xbt_free(p);
}
void peer(int argc, char* argv[])
{
XBT_DEBUG("peer");
peer_t p = peer_init(argc, argv);
double start_time = simgrid_get_clock();
peer_join_chain(p);
peer_forward_file(p);
double end_time = simgrid_get_clock();
XBT_INFO("### %f %llu bytes (Avg %f MB/s); copy finished (simulated).", end_time - start_time, p->received_bytes,
p->received_bytes / 1024.0 / 1024.0 / (end_time - start_time));
peer_delete(p);
}
Distributed Hash Tables (DHT)
Chord Protocol
One of the most famous DHT protocol.
View examples/cpp/dht-chord/s4u-dht-chord.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "s4u-dht-chord.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_chord, "Messages specific for this s4u example");
int main(int argc, char* argv[])
{
simgrid::s4u::Engine e(&argc, argv);
xbt_assert(argc > 2,
"Usage: %s [-nb_bits=n] [-timeout=t] platform_file deployment_file\n"
"\tExample: %s ../platforms/cluster_backbone.xml ./s4u-dht-chord_d.xml\n",
argv[0], argv[0]);
std::string platform_file(argv[argc - 2]);
std::string deployment_file(argv[argc - 1]);
int nb_bits = 24;
int timeout = 50;
for (const auto& option : std::vector<std::string>(argv + 1, argv + argc - 2)) {
if (option.rfind("-nb_bits=", 0) == 0) {
nb_bits = std::stoi(option.substr(option.find('=') + 1));
XBT_DEBUG("Set nb_bits to %d", nb_bits);
} else if (option.rfind("-timeout=", 0) == 0) {
timeout = std::stoi(option.substr(option.find('=') + 1));
XBT_DEBUG("Set timeout to %d", timeout);
} else {
xbt_die("Invalid chord option '%s'", option.c_str());
}
}
int nb_keys = 1U << nb_bits;
XBT_DEBUG("Sets nb_keys to %d", nb_keys);
e.load_platform(platform_file);
/* Global initialization of the Chord simulation. */
Node::set_parameters(nb_bits, nb_keys, timeout);
e.register_actor<Node>("node");
e.load_deployment(deployment_file);
e.run();
XBT_INFO("Simulated time: %g", simgrid::s4u::Engine::get_clock());
return 0;
}
View examples/cpp/dht-chord/s4u-dht-chord-node.cpp
Download s4u-dht-chord-node.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "s4u-dht-chord.hpp"
XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(s4u_chord);
namespace sg4 = simgrid::s4u;
void ChordMessage::destroy(void* message)
{
delete static_cast<ChordMessage*>(message);
}
/* Returns whether an id belongs to the interval [start, end].
*
* The parameters are normalized to make sure they are between 0 and nb_keys_ - 1).
* 1 belongs to [62, 3]
* 1 does not belong to [3, 62]
* 63 belongs to [62, 3]
* 63 does not belong to [3, 62]
* 24 belongs to [21, 29]
* 24 does not belong to [29, 21]
*
* @param id id to check
* @param start lower bound
* @param end upper bound
* @return true if id in in [start, end]
*/
bool Node::is_in_interval(int id, int start, int end)
{
int i = id % nb_keys_;
int s = start % nb_keys_;
int e = end % nb_keys_;
// make sure end >= start and id >= start
if (e < s) {
e += nb_keys_;
}
if (i < s) {
i += nb_keys_;
}
return i <= e;
}
void Node::set_parameters(int nb_bits, int nb_keys, int timeout)
{
nb_bits_ = nb_bits;
nb_keys_ = nb_keys;
timeout_ = timeout;
}
/* Initializes the current node as the first one of the system */
Node::Node(std::vector<std::string> args)
{
xbt_assert(args.size() == 3 || args.size() == 5, "Wrong number of arguments for this node");
// initialize my node
id_ = std::stoi(args[1]);
XBT_DEBUG("Initialize node with id: %d", id_);
random_.set_seed(id_);
mailbox_ = sg4::Mailbox::by_name(std::to_string(id_));
next_finger_to_fix_ = 0;
fingers_.resize(nb_bits_, id_);
if (args.size() == 3) { // first ring
deadline_ = std::stod(args[2]);
start_time_ = sg4::Engine::get_clock();
XBT_DEBUG("Create a new Chord ring...");
} else {
known_id_ = std::stoi(args[2]);
start_time_ = std::stod(args[3]);
deadline_ = std::stod(args[4]);
XBT_DEBUG("Hey! Let's join the system in %f seconds (shall leave at time %f)", start_time_,
start_time_ + deadline_);
}
}
/* Makes the current node join the ring, knowing the id of a node already in the ring
*
* @param known_id id of a node already in the ring
* @return true if the join operation succeeded
* */
void Node::join(int known_id)
{
XBT_INFO("Joining the ring with id %d, knowing node %d", id_, known_id);
setPredecessor(-1); // no predecessor (yet)
int successor_id = remoteFindSuccessor(known_id, id_);
if (successor_id == -1) {
XBT_INFO("Cannot join the ring.");
} else {
setFinger(0, successor_id);
printFingerTable();
joined_ = true;
}
}
/* Makes the current node quit the system */
void Node::leave()
{
XBT_INFO("Well Guys! I Think it's time for me to leave ;)");
notifyAndQuit();
joined_ = false;
}
/* Notifies the successor and the predecessor of the current node before leaving */
void Node::notifyAndQuit()
{
// send the PREDECESSOR_LEAVING to our successor
auto* pred_msg = new ChordMessage(MessageType::PREDECESSOR_LEAVING);
pred_msg->request_id = pred_id_;
pred_msg->answer_to = mailbox_;
XBT_DEBUG("Sending a 'PREDECESSOR_LEAVING' to my successor %d", fingers_[0]);
try {
sg4::Mailbox::by_name(std::to_string(fingers_[0]))->put(pred_msg, 10, timeout_);
} catch (const simgrid::TimeoutException&) {
XBT_DEBUG("Timeout expired when sending a 'PREDECESSOR_LEAVING' to my successor %d", fingers_[0]);
delete pred_msg;
}
if (pred_id_ != -1 && pred_id_ != id_) {
// send the SUCCESSOR_LEAVING to our predecessor (only if I have one that is not me)
auto* succ_msg = new ChordMessage(MessageType::SUCCESSOR_LEAVING);
succ_msg->request_id = fingers_[0];
succ_msg->answer_to = mailbox_;
XBT_DEBUG("Sending a 'SUCCESSOR_LEAVING' to my predecessor %d", pred_id_);
try {
sg4::Mailbox::by_name(std::to_string(pred_id_))->put(succ_msg, 10, timeout_);
} catch (const simgrid::TimeoutException&) {
XBT_DEBUG("Timeout expired when sending a 'SUCCESSOR_LEAVING' to my predecessor %d", pred_id_);
delete succ_msg;
}
}
}
/* Performs a find successor request to a random id */
void Node::randomLookup()
{
int res = id_;
int random_index = random_.uniform_int(0, nb_bits_ - 1);
int random_id = fingers_[random_index];
XBT_DEBUG("Making a lookup request for id %d", random_id);
if (random_id != id_)
res = findSuccessor(random_id);
XBT_DEBUG("The successor of node %d is %d", random_id, res);
}
/* Sets a finger of the current node.
*
* @param node the current node
* @param finger_index index of the finger to set (0 to nb_bits_ - 1)
* @param id the id to set for this finger
*/
void Node::setFinger(int finger_index, int id)
{
if (id != fingers_[finger_index]) {
fingers_[finger_index] = id;
XBT_VERB("My new finger #%d is %d", finger_index, id);
}
}
/* Sets the predecessor of the current node.
* @param id the id to predecessor, or -1 to unset the predecessor
*/
void Node::setPredecessor(int predecessor_id)
{
if (predecessor_id != pred_id_) {
pred_id_ = predecessor_id;
XBT_VERB("My new predecessor is %d", predecessor_id);
}
}
/** refreshes the finger table of the current node (called periodically) */
void Node::fixFingers()
{
XBT_DEBUG("Fixing fingers");
int id = findSuccessor(id_ + (1U << next_finger_to_fix_));
if (id != -1) {
if (id != fingers_[next_finger_to_fix_]) {
setFinger(next_finger_to_fix_, id);
printFingerTable();
}
next_finger_to_fix_ = (next_finger_to_fix_ + 1) % nb_bits_;
}
}
/** Displays the finger table of a node. */
void Node::printFingerTable()
{
if (XBT_LOG_ISENABLED(s4u_chord, xbt_log_priority_verbose)) {
XBT_VERB("My finger table:");
XBT_VERB("Start | Succ");
for (int i = 0; i < nb_bits_; i++) {
XBT_VERB(" %3u | %3d", (id_ + (1U << i)) % nb_keys_, fingers_[i]);
}
XBT_VERB("Predecessor: %d", pred_id_);
}
}
/* checks whether the predecessor has failed (called periodically) */
void Node::checkPredecessor()
{
XBT_DEBUG("Checking whether my predecessor is alive");
if (pred_id_ == -1)
return;
sg4::Mailbox* mailbox = sg4::Mailbox::by_name(std::to_string(pred_id_));
sg4::Mailbox* return_mailbox = sg4::Mailbox::by_name(std::to_string(id_) + "_is_alive");
auto* message = new ChordMessage(MessageType::PREDECESSOR_ALIVE);
message->request_id = pred_id_;
message->answer_to = return_mailbox;
XBT_DEBUG("Sending a 'Predecessor Alive' request to my predecessor %d", pred_id_);
try {
mailbox->put(message, 10, timeout_);
} catch (const simgrid::TimeoutException&) {
XBT_DEBUG("Failed to send the 'Predecessor Alive' request to %d", pred_id_);
delete message;
return;
}
// receive the answer
XBT_DEBUG("Sent 'Predecessor Alive' request to %d, waiting for the answer on my mailbox '%s'", pred_id_,
message->answer_to->get_cname());
ChordMessage* answer = nullptr;
sg4::CommPtr comm = return_mailbox->get_async<ChordMessage>(&answer);
try {
comm->wait_for_or_cancel(timeout_);
XBT_DEBUG("Received the answer to my 'Predecessor Alive': my predecessor %d is alive", pred_id_);
delete answer;
} catch (const simgrid::TimeoutException&) {
XBT_DEBUG("Failed to receive the answer to my 'Predecessor Alive' request");
pred_id_ = -1;
}
}
/* Asks its predecessor to a remote node
*
* @param ask_to the node to ask to
* @return the id of its predecessor node, or -1 if the request failed (or if the node does not know its predecessor)
*/
int Node::remoteGetPredecessor(int ask_to)
{
int predecessor_id = -1;
sg4::Mailbox* mailbox = sg4::Mailbox::by_name(std::to_string(ask_to));
sg4::Mailbox* return_mailbox = sg4::Mailbox::by_name(std::to_string(id_) + "_pred");
auto* message = new ChordMessage(MessageType::GET_PREDECESSOR);
message->request_id = id_;
message->answer_to = return_mailbox;
// send a "Get Predecessor" request to ask_to_id
XBT_DEBUG("Sending a 'Get Predecessor' request to %d", ask_to);
try {
mailbox->put(message, 10, timeout_);
} catch (const simgrid::TimeoutException&) {
XBT_DEBUG("Failed to send the 'Get Predecessor' request to %d", ask_to);
delete message;
return predecessor_id;
}
// receive the answer
XBT_DEBUG("Sent 'Get Predecessor' request to %d, waiting for the answer on my mailbox '%s'", ask_to,
message->answer_to->get_cname());
ChordMessage* answer = nullptr;
sg4::CommPtr comm = return_mailbox->get_async<ChordMessage>(&answer);
try {
comm->wait_for_or_cancel(timeout_);
XBT_DEBUG("Received the answer to my 'Get Predecessor' request: the predecessor of node %d is %d", ask_to,
answer->answer_id);
predecessor_id = answer->answer_id;
delete answer;
} catch (const simgrid::TimeoutException&) {
XBT_DEBUG("Failed to receive the answer to my 'Get Predecessor' request");
delete answer;
}
return predecessor_id;
}
/* Returns the closest preceding finger of an id with respect to the finger table of the current node.
*
* @param id the id to find
* @return the closest preceding finger of that id
*/
int Node::closestPrecedingFinger(int id)
{
for (int i = nb_bits_ - 1; i >= 0; i--) {
if (is_in_interval(fingers_[i], id_ + 1, id - 1)) {
return fingers_[i];
}
}
return id_;
}
/* Makes the current node find the successor node of an id.
*
* @param id the id to find
* @return the id of the successor node, or -1 if the request failed
*/
int Node::findSuccessor(int id)
{
// is my successor the successor?
if (is_in_interval(id, id_ + 1, fingers_[0])) {
return fingers_[0];
}
// otherwise, ask the closest preceding finger in my table
return remoteFindSuccessor(closestPrecedingFinger(id), id);
}
int Node::remoteFindSuccessor(int ask_to, int id)
{
int successor = -1;
sg4::Mailbox* mailbox = sg4::Mailbox::by_name(std::to_string(ask_to));
sg4::Mailbox* return_mailbox = sg4::Mailbox::by_name(std::to_string(id_) + "_succ");
auto* message = new ChordMessage(MessageType::FIND_SUCCESSOR);
message->request_id = id_;
message->answer_to = return_mailbox;
// send a "Find Successor" request to ask_to_id
XBT_DEBUG("Sending a 'Find Successor' request to %d for id %d", ask_to, id);
try {
mailbox->put(message, 10, timeout_);
} catch (const simgrid::TimeoutException&) {
XBT_DEBUG("Failed to send the 'Find Successor' request to %d for id %d", ask_to, id_);
delete message;
return successor;
}
// receive the answer
XBT_DEBUG("Sent a 'Find Successor' request to %d for key %d, waiting for the answer", ask_to, id);
ChordMessage* answer = nullptr;
sg4::CommPtr comm = return_mailbox->get_async<ChordMessage>(&answer);
try {
comm->wait_for_or_cancel(timeout_);
XBT_DEBUG("Received the answer to my 'Find Successor' request for id %d: the successor of key %d is %d",
answer->request_id, id_, answer->answer_id);
successor = answer->answer_id;
delete answer;
} catch (const simgrid::TimeoutException&) {
XBT_DEBUG("Failed to receive the answer to my 'Find Successor' request");
delete answer;
}
return successor;
}
/* Notifies the current node that its predecessor may have changed. */
void Node::notify(int predecessor_candidate_id)
{
if (pred_id_ == -1 || is_in_interval(predecessor_candidate_id, pred_id_ + 1, id_ - 1)) {
setPredecessor(predecessor_candidate_id);
printFingerTable();
} else {
XBT_DEBUG("I don't have to change my predecessor to %d", predecessor_candidate_id);
}
}
/* Notifies a remote node that its predecessor may have changed. */
void Node::remoteNotify(int notify_id, int predecessor_candidate_id) const
{
auto* message = new ChordMessage(MessageType::NOTIFY);
message->request_id = predecessor_candidate_id;
message->answer_to = nullptr;
// send a "Notify" request to notify_id
XBT_DEBUG("Sending a 'Notify' request to %d", notify_id);
sg4::Mailbox* mailbox = sg4::Mailbox::by_name(std::to_string(notify_id));
mailbox->put_init(message, 10)->detach(ChordMessage::destroy);
}
/* This function is called periodically. It checks the immediate successor of the current node. */
void Node::stabilize()
{
XBT_DEBUG("Stabilizing node");
// get the predecessor of my immediate successor
int candidate_id = pred_id_;
int successor_id = fingers_[0];
if (successor_id != id_)
candidate_id = remoteGetPredecessor(successor_id);
// this node is a candidate to become my new successor
if (candidate_id != -1 && is_in_interval(candidate_id, id_ + 1, successor_id - 1)) {
setFinger(0, candidate_id);
}
if (successor_id != id_) {
remoteNotify(successor_id, id_);
}
}
/* This function is called when a node receives a message.
*
* @param message the message to handle (don't touch it afterward: it will be destroyed, reused or forwarded)
*/
void Node::handleMessage(ChordMessage* message)
{
switch (message->type) {
case MessageType::FIND_SUCCESSOR:
XBT_DEBUG("Received a 'Find Successor' request from %s for id %d", message->issuer_host_name.c_str(),
message->request_id);
// is my successor the successor?
if (is_in_interval(message->request_id, id_ + 1, fingers_[0])) {
message->type = MessageType::FIND_SUCCESSOR_ANSWER;
message->answer_id = fingers_[0];
XBT_DEBUG("Sending back a 'Find Successor Answer' to %s (mailbox %s): the successor of %d is %d",
message->issuer_host_name.c_str(), message->answer_to->get_cname(), message->request_id,
message->answer_id);
message->answer_to->put_init(message, 10)->detach(ChordMessage::destroy);
} else {
// otherwise, forward the request to the closest preceding finger in my table
int closest = closestPrecedingFinger(message->request_id);
XBT_DEBUG("Forwarding the 'Find Successor' request for id %d to my closest preceding finger %d",
message->request_id, closest);
sg4::Mailbox* mailbox = sg4::Mailbox::by_name(std::to_string(closest));
mailbox->put_init(message, 10)->detach(ChordMessage::destroy);
}
break;
case MessageType::GET_PREDECESSOR:
XBT_DEBUG("Receiving a 'Get Predecessor' request from %s", message->issuer_host_name.c_str());
message->type = MessageType::GET_PREDECESSOR_ANSWER;
message->answer_id = pred_id_;
XBT_DEBUG("Sending back a 'Get Predecessor Answer' to %s via mailbox '%s': my predecessor is %d",
message->issuer_host_name.c_str(), message->answer_to->get_cname(), message->answer_id);
message->answer_to->put_init(message, 10)->detach(ChordMessage::destroy);
break;
case MessageType::NOTIFY:
// someone is telling me that he may be my new predecessor
XBT_DEBUG("Receiving a 'Notify' request from %s", message->issuer_host_name.c_str());
notify(message->request_id);
delete message;
break;
case MessageType::PREDECESSOR_LEAVING:
// my predecessor is about to quit
XBT_DEBUG("Receiving a 'Predecessor Leaving' message from %s", message->issuer_host_name.c_str());
// modify my predecessor
setPredecessor(message->request_id);
delete message;
/*TODO :
>> notify my new predecessor
>> send a notify_predecessors !!
*/
break;
case MessageType::SUCCESSOR_LEAVING:
// my successor is about to quit
XBT_DEBUG("Receiving a 'Successor Leaving' message from %s", message->issuer_host_name.c_str());
// modify my successor FIXME : this should be implicit ?
setFinger(0, message->request_id);
delete message;
/* TODO
>> notify my new successor
>> update my table & predecessors table */
break;
case MessageType::PREDECESSOR_ALIVE:
XBT_DEBUG("Receiving a 'Predecessor Alive' request from %s", message->issuer_host_name.c_str());
message->type = MessageType::PREDECESSOR_ALIVE_ANSWER;
XBT_DEBUG("Sending back a 'Predecessor Alive Answer' to %s (mailbox %s)", message->issuer_host_name.c_str(),
message->answer_to->get_cname());
message->answer_to->put_init(message, 10)->detach(ChordMessage::destroy);
break;
default:
XBT_DEBUG("Ignoring unexpected message: %d from %s", static_cast<int>(message->type),
message->issuer_host_name.c_str());
delete message;
}
}
void Node::operator()()
{
sg4::this_actor::sleep_for(start_time_);
if (known_id_ == -1) {
setPredecessor(-1); // -1 means that I have no predecessor
printFingerTable();
joined_ = true;
} else {
join(known_id_);
}
if (not joined_)
return;
ChordMessage* message = nullptr;
double now = sg4::Engine::get_clock();
double next_stabilize_date = start_time_ + PERIODIC_STABILIZE_DELAY;
double next_fix_fingers_date = start_time_ + PERIODIC_FIX_FINGERS_DELAY;
double next_check_predecessor_date = start_time_ + PERIODIC_CHECK_PREDECESSOR_DELAY;
double next_lookup_date = start_time_ + PERIODIC_LOOKUP_DELAY;
sg4::CommPtr comm_receive = nullptr;
while (now < std::min(start_time_ + deadline_, MAX_SIMULATION_TIME)) {
if (comm_receive == nullptr)
comm_receive = mailbox_->get_async<ChordMessage>(&message);
bool comm_completed = true;
try {
if (not comm_receive->test())
comm_completed = false;
} catch (const simgrid::TimeoutException&) {
XBT_DEBUG("Caught a timeout, go ahead.");
}
if (comm_completed) {
if (message != nullptr) {
handleMessage(message);
message = nullptr;
}
comm_receive = nullptr;
} else {
// no task was received: make some periodic calls
if (now >= next_stabilize_date) {
stabilize();
next_stabilize_date = sg4::Engine::get_clock() + PERIODIC_STABILIZE_DELAY;
} else if (now >= next_fix_fingers_date) {
fixFingers();
next_fix_fingers_date = sg4::Engine::get_clock() + PERIODIC_FIX_FINGERS_DELAY;
} else if (now >= next_check_predecessor_date) {
checkPredecessor();
next_check_predecessor_date = sg4::Engine::get_clock() + PERIODIC_CHECK_PREDECESSOR_DELAY;
} else if (now >= next_lookup_date) {
randomLookup();
next_lookup_date = sg4::Engine::get_clock() + PERIODIC_LOOKUP_DELAY;
} else {
// nothing to do: sleep for a while
sg4::this_actor::sleep_for(SLEEP_DELAY);
}
}
now = sg4::Engine::get_clock();
}
if (comm_receive != nullptr) {
try {
if (comm_receive->test())
delete message;
else
comm_receive->cancel();
} catch (const simgrid::TimeoutException&) {
XBT_DEBUG("Caught a timeout for last message, nevermind.");
}
}
// leave the ring
leave();
}
Kademlia
Another well-known DHT protocol.
View examples/cpp/dht-kademlia/s4u-dht-kademlia.cpp
/* Copyright (c) 2012-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "s4u-dht-kademlia.hpp"
#include "message.hpp"
#include "node.hpp"
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(kademlia, "Messages specific for this example");
namespace sg4 = simgrid::s4u;
/** @brief Node function
* @param my node ID
* @param the ID of the person I know in the system (or not)
* @param Time before I leave the system because I'm bored
*/
static void node(std::vector<std::string> args)
{
bool join_success = true;
double deadline;
xbt_assert(args.size() == 3 || args.size() == 4, "Wrong number of arguments");
/* Node initialization */
auto node_id = static_cast<unsigned int>(std::stoul(args[1], nullptr, 0));
kademlia::Node node(node_id);
if (args.size() == 4) {
XBT_INFO("Hi, I'm going to join the network with id %u", node.getId());
auto known_id = static_cast<unsigned int>(std::stoul(args[2], nullptr, 0));
join_success = node.join(known_id);
deadline = std::stod(args[3]);
} else {
deadline = std::stod(args[2]);
XBT_INFO("Hi, I'm going to create the network with id %u", node.getId());
node.routingTableUpdate(node.getId());
}
if (join_success) {
XBT_VERB("Ok, I'm joining the network with id %u", node.getId());
// We start the main loop
double next_lookup_time = sg4::Engine::get_clock() + RANDOM_LOOKUP_INTERVAL;
XBT_VERB("Main loop start");
sg4::Mailbox* mailbox = sg4::Mailbox::by_name(std::to_string(node.getId()));
while (sg4::Engine::get_clock() < deadline) {
if (kademlia::Message* msg = node.receive(mailbox)) {
// There has been a message, we need to handle it !
node.handleFindNode(msg);
delete msg;
} else {
/* We search for a pseudo random node */
if (sg4::Engine::get_clock() >= next_lookup_time) {
node.randomLookup();
next_lookup_time += RANDOM_LOOKUP_INTERVAL;
} else {
// Didn't get a message: sleep for a while...
sg4::this_actor::sleep_for(1);
}
}
}
} else {
XBT_INFO("I couldn't join the network :(");
}
XBT_DEBUG("I'm leaving the network");
node.displaySuccessRate();
}
/** @brief Main function */
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
/* Check the arguments */
xbt_assert(argc > 2,
"Usage: %s platform_file deployment_file\n\tExample: %s cluster_backbone.xml dht-kademlia_d.xml\n",
argv[0], argv[0]);
e.load_platform(argv[1]);
e.register_function("node", node);
e.load_deployment(argv[2]);
e.run();
XBT_INFO("Simulated time: %g", sg4::Engine::get_clock());
return 0;
}
View examples/cpp/dht-kademlia/routing_table.cpp
/* Copyright (c) 2012-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "routing_table.hpp"
#include "node.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(kademlia_routing_table, "Messages specific for this example");
namespace kademlia {
/** @brief Initialization of a node routing table. */
RoutingTable::RoutingTable(unsigned int node_id) : id_(node_id)
{
buckets_.reserve(IDENTIFIER_SIZE + 1);
for (unsigned int i = 0; i < IDENTIFIER_SIZE + 1; i++)
buckets_.emplace_back(i);
}
void RoutingTable::print() const
{
XBT_INFO("Routing table of %08x:", id_);
for (unsigned int i = 0; i <= IDENTIFIER_SIZE; i++) {
if (not buckets_[i].nodes_.empty()) {
XBT_INFO("Bucket number %u: ", i);
int j = 0;
for (auto value : buckets_[i].nodes_) {
XBT_INFO("Element %d: %08x", j, value);
j++;
}
}
}
}
/** @brief Finds the corresponding bucket in a routing table for a given identifier
* @param id the identifier
* @return the bucket in which the the identifier would be.
*/
Bucket* RoutingTable::findBucket(unsigned int id)
{
unsigned int xor_number = id_ ^ id;
unsigned int prefix = get_node_prefix(xor_number, IDENTIFIER_SIZE);
xbt_assert(prefix <= IDENTIFIER_SIZE, "Tried to return a bucket that doesn't exist.");
return &buckets_[prefix];
}
/** Returns if the routing table contains the id. */
bool RoutingTable::contains(unsigned int node_id)
{
const Bucket* bucket = findBucket(node_id);
return std::find(bucket->nodes_.begin(), bucket->nodes_.end(), node_id) != bucket->nodes_.end();
}
} // namespace kademlia
View examples/cpp/dht-kademlia/answer.cpp
/* Copyright (c) 2012-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "answer.hpp"
XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(kademlia_node);
namespace kademlia {
/** @brief Prints an Answer, for debugging purposes */
void Answer::print() const
{
XBT_INFO("Searching %08x, size %zu", destination_id_, nodes_.size());
unsigned int i = 0;
for (auto const& [contact, distance] : nodes_)
XBT_INFO("Node %08x: %08x is at distance %u", i++, contact, distance);
}
/** @brief Merge two answers together, only keeping the best nodes
* @param source the source of the nodes to add
*/
unsigned int Answer::merge(const Answer* source)
{
if (this == source)
return 0;
unsigned int nb_added = 0;
for (auto const& contact : source->nodes_) {
if (std::find(nodes_.begin(), nodes_.end(), contact) == nodes_.end()) {
nodes_.push_back(contact);
nb_added++;
}
}
trim();
return nb_added;
}
/** @brief Trims an Answer, in order for it to have a size of less or equal to "bucket_size" */
void Answer::trim()
{
// sort by distance
std::sort(nodes_.begin(), nodes_.end(),
[](const std::pair<unsigned int, unsigned int>& a, const std::pair<unsigned int, unsigned int>& b) {
return (a.second < b.second);
});
if (nodes_.size() > BUCKET_SIZE)
nodes_.resize(BUCKET_SIZE);
}
/** @brief Returns if the destination we are trying to find is found
* @return if the destination is found.
*/
bool Answer::destinationFound() const
{
return not nodes_.empty() && nodes_.begin()->second == 0;
}
/** @brief Adds the content of a bucket unsigned into an answer object.
* @param bucket the bucket we have to had unsigned into
*/
void Answer::addBucket(const Bucket* bucket)
{
xbt_assert((bucket != nullptr), "Provided a NULL bucket");
for (auto const& id : bucket->nodes_) {
unsigned int distance = id ^ destination_id_;
nodes_.emplace_back(id, distance);
}
}
} // namespace kademlia
View examples/cpp/dht-kademlia/node.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "node.hpp"
#include "routing_table.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(kademlia_node, "Messages specific for this example");
namespace sg4 = simgrid::s4u;
namespace kademlia {
static void destroy(void* message)
{
const auto* msg = static_cast<Message*>(message);
delete msg;
}
/**
* Try to asynchronously get a new message from given mailbox. Return null if none available.
*/
Message* Node::receive(sg4::Mailbox* mailbox)
{
if (receive_comm == nullptr)
receive_comm = mailbox->get_async<kademlia::Message>(&received_msg);
if (not receive_comm->test())
return nullptr;
receive_comm = nullptr;
return received_msg;
}
/**
* @brief Tries to join the network
* @param known_id id of the node I know in the network.
*/
bool Node::join(unsigned int known_id)
{
bool got_answer = false;
/* Add the guy we know to our routing table and ourselves. */
routingTableUpdate(id_);
routingTableUpdate(known_id);
/* First step: Send a "FIND_NODE" request to the node we know */
sendFindNode(known_id, id_);
sg4::Mailbox* mailbox = sg4::Mailbox::by_name(std::to_string(id_));
do {
if (Message* msg = receive(mailbox)) {
XBT_DEBUG("Received an answer from the node I know.");
got_answer = true;
// retrieve the node list and ping them.
if (const Answer* node_list = msg->answer_.get()) {
for (auto const& [contact, _] : node_list->getNodes())
routingTableUpdate(contact);
} else {
handleFindNode(msg);
}
delete msg;
} else
sg4::this_actor::sleep_for(1);
} while (not got_answer);
/* Second step: Send a FIND_NODE to a random node in buckets */
unsigned int bucket_id = table.findBucket(known_id)->getId();
xbt_assert(bucket_id <= IDENTIFIER_SIZE);
for (unsigned int i = 0; ((bucket_id > i) || (bucket_id + i) <= IDENTIFIER_SIZE) && i < JOIN_BUCKETS_QUERIES; i++) {
if (bucket_id > i) {
unsigned int id_in_bucket = get_id_in_prefix(id_, bucket_id - i);
findNode(id_in_bucket, false);
}
if (bucket_id + i <= IDENTIFIER_SIZE) {
unsigned int id_in_bucket = get_id_in_prefix(id_, bucket_id + i);
findNode(id_in_bucket, false);
}
}
return got_answer;
}
/** @brief Send a "FIND_NODE" to a node
* @param id node we are querying
* @param destination node we are trying to find.
*/
void Node::sendFindNode(unsigned int id, unsigned int destination) const
{
/* Gets the mailbox to send to */
sg4::Mailbox* mailbox = sg4::Mailbox::by_name(std::to_string(id));
/* Build the task */
auto* msg =
new Message(id_, destination, sg4::Mailbox::by_name(std::to_string(id_)), sg4::Host::current()->get_cname());
/* Send the task */
mailbox->put_init(msg, 1)->detach(kademlia::destroy);
XBT_VERB("Asking %u for its closest nodes", id);
}
/**
* Sends to the best "KADEMLIA_ALPHA" nodes in the "node_list" array a "FIND_NODE" request, to ask them for their best
* nodes
*/
unsigned int Node::sendFindNodeToBest(const Answer* node_list) const
{
unsigned int i = 0;
unsigned int j = 0;
unsigned int destination = node_list->getDestinationId();
for (auto const& [node_to_query, _] : node_list->getNodes()) {
/* We need to have at most "KADEMLIA_ALPHA" requests each time, according to the protocol */
/* Gets the node we want to send the query to */
if (node_to_query != id_) { /* No need to query ourselves */
sendFindNode(node_to_query, destination);
j++;
}
i++;
if (j == KADEMLIA_ALPHA)
break;
}
return i;
}
/** @brief Updates/Puts the node id unsigned into our routing table
* @param id The id of the node we need to add unsigned into our routing table
*/
void Node::routingTableUpdate(unsigned int id)
{
// retrieval of the bucket in which the should be
Bucket* bucket = table.findBucket(id);
// check if the id is already in the bucket.
auto id_pos = std::find(bucket->nodes_.begin(), bucket->nodes_.end(), id);
if (id_pos == bucket->nodes_.end()) {
/* We check if the bucket is full or not. If it is, we evict an old element */
if (bucket->nodes_.size() >= BUCKET_SIZE) {
bucket->nodes_.pop_back();
}
bucket->nodes_.push_front(id);
XBT_VERB("I'm adding to my routing table %08x", id);
} else {
// We push the element to the front
bucket->nodes_.erase(id_pos);
bucket->nodes_.push_front(id);
XBT_VERB("I'm updating %08x", id);
}
}
/** @brief Finds the closest nodes to the node given.
* @param node : our node
* @param destination_id : the id of the guy we are trying to find
*/
std::unique_ptr<Answer> Node::findClosest(unsigned int destination_id)
{
auto answer = std::make_unique<Answer>(destination_id);
/* We find the corresponding bucket for the id */
const Bucket* bucket = table.findBucket(destination_id);
int bucket_id = bucket->getId();
xbt_assert((bucket_id <= IDENTIFIER_SIZE), "Bucket found has a wrong identifier");
/* So, we copy the contents of the bucket unsigned into our answer */
answer->addBucket(bucket);
/* However, if we don't have enough elements in our bucket, we NEED to include at least "BUCKET_SIZE" elements
* (if, of course, we know at least "BUCKET_SIZE" elements. So we're going to look unsigned into the other buckets.
*/
for (int i = 1; answer->getSize() < BUCKET_SIZE && ((bucket_id - i > 0) || (bucket_id + i < IDENTIFIER_SIZE)); i++) {
/* We check the previous buckets */
if (bucket_id - i >= 0) {
const Bucket* bucket_p = &table.getBucketAt(bucket_id - i);
answer->addBucket(bucket_p);
}
/* We check the next buckets */
if (bucket_id + i <= IDENTIFIER_SIZE) {
const Bucket* bucket_n = &table.getBucketAt(bucket_id + i);
answer->addBucket(bucket_n);
}
}
/* We trim the array to have only BUCKET_SIZE or less elements */
answer->trim();
return answer;
}
/** @brief Send a request to find a node in the node routing table.
* @param id_to_find the id of the node we are trying to find
*/
bool Node::findNode(unsigned int id_to_find, bool count_in_stats)
{
unsigned int queries;
unsigned int answers;
bool destination_found = false;
unsigned int nodes_added = 0;
double global_timeout = sg4::Engine::get_clock() + FIND_NODE_GLOBAL_TIMEOUT;
unsigned int steps = 0;
/* First we build a list of who we already know */
std::unique_ptr<Answer> node_list = findClosest(id_to_find);
xbt_assert((node_list != nullptr), "node_list incorrect");
XBT_DEBUG("Doing a FIND_NODE on %08x", id_to_find);
/* Ask the nodes on our list if they have information about the node we are trying to find */
do {
answers = 0;
queries = sendFindNodeToBest(node_list.get());
nodes_added = 0;
double timeout = sg4::Engine::get_clock() + FIND_NODE_TIMEOUT;
steps++;
double time_beginreceive = sg4::Engine::get_clock();
sg4::Mailbox* mailbox = sg4::Mailbox::by_name(std::to_string(id_));
do {
if (Message* msg = receive(mailbox)) {
// Check if what we have received is what we are looking for.
if (msg->answer_ && msg->answer_->getDestinationId() == id_to_find) {
routingTableUpdate(msg->sender_id_);
// Handle the answer
for (auto const& [contact, _] : node_list->getNodes())
routingTableUpdate(contact);
answers++;
nodes_added = node_list->merge(msg->answer_.get());
XBT_DEBUG("Received an answer from %s (%s) with %zu nodes on it", msg->answer_to_->get_cname(),
msg->issuer_host_name_.c_str(), msg->answer_->getSize());
} else {
if (msg->answer_) {
routingTableUpdate(msg->sender_id_);
XBT_DEBUG("Received a wrong answer for a FIND_NODE");
} else {
handleFindNode(msg);
}
// Update the timeout if we didn't have our answer
timeout += sg4::Engine::get_clock() - time_beginreceive;
time_beginreceive = sg4::Engine::get_clock();
}
delete msg;
} else {
sg4::this_actor::sleep_for(1);
}
} while (sg4::Engine::get_clock() < timeout && answers < queries);
destination_found = node_list->destinationFound();
} while (not destination_found && (nodes_added > 0 || answers == 0) && sg4::Engine::get_clock() < global_timeout &&
steps < MAX_STEPS);
if (destination_found) {
if (count_in_stats)
find_node_success++;
if (queries > 4)
XBT_VERB("FIND_NODE on %08x success in %u steps", id_to_find, steps);
routingTableUpdate(id_to_find);
} else {
if (count_in_stats) {
find_node_failed++;
XBT_VERB("%08x not found in %u steps", id_to_find, steps);
}
}
return destination_found;
}
/** @brief Does a pseudo-random lookup for someone in the system
* @param node caller node data
*/
void Node::randomLookup()
{
unsigned int id_to_look = RANDOM_LOOKUP_NODE; // Totally random.
/* TODO: Use some pseudo-random generator. */
XBT_DEBUG("I'm doing a random lookup");
findNode(id_to_look, true);
}
/** @brief Handles the answer to an incoming "find_node" task */
void Node::handleFindNode(const Message* msg)
{
routingTableUpdate(msg->sender_id_);
XBT_VERB("Received a FIND_NODE from %s (%s), he's trying to find %08x", msg->answer_to_->get_cname(),
msg->issuer_host_name_.c_str(), msg->destination_id_);
// Building the answer to the request
auto* answer = new Message(id_, msg->destination_id_, findClosest(msg->destination_id_),
sg4::Mailbox::by_name(std::to_string(id_)), sg4::Host::current()->get_cname());
// Sending the answer
msg->answer_to_->put_init(answer, 1)->detach(kademlia::destroy);
}
void Node::displaySuccessRate() const
{
XBT_INFO("%u/%u FIND_NODE have succeeded", find_node_success, find_node_success + find_node_failed);
}
} // namespace kademlia
/**@brief Returns an identifier which is in a specific bucket of a routing table
* @param id id of the routing table owner
* @param prefix id of the bucket where we want that identifier to be
*/
unsigned int get_id_in_prefix(unsigned int id, unsigned int prefix)
{
if (prefix == 0) {
return 0;
} else {
return (1U << (prefix - 1)) ^ id;
}
}
/** @brief Returns the prefix of an identifier.
* The prefix is the id of the bucket in which the remote identifier xor our identifier should be stored.
* @param id : big unsigned int id to test
* @param nb_bits : key size
*/
unsigned int get_node_prefix(unsigned int id, unsigned int nb_bits)
{
unsigned int size = sizeof(unsigned int) * 8;
for (unsigned int j = 0; j < size; j++) {
if (((id >> (size - 1 - j)) & 0x1) != 0) {
return nb_bits - j;
}
}
return 0;
}
View examples/c/dht-kademlia/dht-kademlia.c
/* Copyright (c) 2012-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "message.h"
#include "node.h"
#include "simgrid/actor.h"
#include "simgrid/comm.h"
#include "simgrid/engine.h"
#include "simgrid/mailbox.h"
#include "xbt/asserts.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(dht_kademlia, "Messages specific for this example");
/** @brief Node function
* @param my node ID
* @param the ID of the person I know in the system (or not)
* @param Time before I leave the system because I'm bored
*/
static void node(int argc, char* argv[])
{
unsigned int join_success = 1;
double deadline;
xbt_assert(argc == 3 || argc == 4, "Wrong number of arguments");
/* Node initialization */
unsigned int id = (unsigned int)strtoul(argv[1], NULL, 0);
node_t node = node_init(id);
if (argc == 4) {
XBT_INFO("Hi, I'm going to join the network with id %s", sg_mailbox_get_name(node->mailbox));
unsigned int id_known = (unsigned int)strtoul(argv[2], NULL, 0);
join_success = join(node, id_known);
deadline = strtod(argv[3], NULL);
} else {
deadline = strtod(argv[2], NULL);
XBT_INFO("Hi, I'm going to create the network with id %s", sg_mailbox_get_name(node->mailbox));
routing_table_update(node, node->id);
}
if (join_success) {
XBT_VERB("Ok, I'm joining the network with id %s", sg_mailbox_get_name(node->mailbox));
// We start the main loop
double next_lookup_time = simgrid_get_clock() + RANDOM_LOOKUP_INTERVAL;
XBT_VERB("Main loop start");
sg_mailbox_t mailbox = get_node_mailbox(node->id);
while (simgrid_get_clock() < deadline) {
const kademlia_message_t msg = receive(node, mailbox);
if (msg) {
// There has been a transfer, we need to handle it !
handle_find_node(node, msg);
answer_free(msg->answer);
free(msg);
} else {
/* We search for a pseudo random node */
if (simgrid_get_clock() >= next_lookup_time) {
random_lookup(node);
next_lookup_time += RANDOM_LOOKUP_INTERVAL;
} else {
// Didn't get a task: sleep for a while...
sg_actor_sleep_for(1);
}
}
}
} else {
XBT_INFO("I couldn't join the network :(");
}
if (node->receive_comm)
sg_comm_unref(node->receive_comm);
XBT_DEBUG("I'm leaving the network");
XBT_INFO("%u/%u FIND_NODE have succeeded", node->find_node_success, node->find_node_success + node->find_node_failed);
node_free(node);
}
/** @brief Main function */
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
/* Check the arguments */
xbt_assert(argc > 2, "Usage: %s platform_file deployment_file\n\tExample: %s platform.xml deployment.xml\n", argv[0],
argv[0]);
simgrid_load_platform(argv[1]);
simgrid_register_function("node", node);
simgrid_load_deployment(argv[2]);
simgrid_run();
XBT_INFO("Simulated time: %g", simgrid_get_clock());
return 0;
}
View examples/c/dht-kademlia/routing_table.c
/* Copyright (c) 2012-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "routing_table.h"
#include "node.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(dht_kademlia_routing_table, "Messages specific for this example");
/** @brief Initialization of a node routing table. */
routing_table_t routing_table_init(unsigned int node_id)
{
routing_table_t table = xbt_new(s_routing_table_t, 1);
table->buckets = xbt_new(s_bucket_t, IDENTIFIER_SIZE + 1);
for (unsigned int i = 0; i < IDENTIFIER_SIZE + 1; i++) {
table->buckets[i].nodes = xbt_dynar_new(sizeof(unsigned int), NULL);
table->buckets[i].id = i;
}
table->id = node_id;
return table;
}
/** @brief Frees the routing table */
void routing_table_free(routing_table_t table)
{
// Free the buckets.
for (unsigned int i = 0; i <= IDENTIFIER_SIZE; i++) {
xbt_dynar_free(&table->buckets[i].nodes);
}
xbt_free(table->buckets);
xbt_free(table);
}
/**@brief prints the routing table, to debug stuff. */
void routing_table_print(const_routing_table_t table)
{
XBT_INFO("Routing table of %08x:", table->id);
for (unsigned int i = 0; i <= IDENTIFIER_SIZE; i++) {
if (!xbt_dynar_is_empty(table->buckets[i].nodes)) {
XBT_INFO("Bucket number %u: ", i);
unsigned int j;
unsigned int value;
xbt_dynar_foreach (table->buckets[i].nodes, j, value) {
XBT_INFO("Element %u: %08x", j, value);
}
}
}
}
/** @brief Finds an identifier in a bucket and returns its position or returns -1 if it doesn't exists
* @param bucket the bucket in which we try to find our identifier
* @param id the id
*/
unsigned int bucket_find_id(const_bucket_t bucket, unsigned int id)
{
unsigned int i;
unsigned int current_id;
xbt_dynar_foreach (bucket->nodes, i, current_id) {
if (id == current_id) {
return i;
}
}
return -1;
}
/** @brief Finds the corresponding bucket in a routing table for a given identifier
* @param table the routing table
* @param id the identifier
* @return the bucket in which the the identifier would be.
*/
bucket_t routing_table_find_bucket(const_routing_table_t table, unsigned int id)
{
unsigned int xor_number = table->id ^ id;
unsigned int prefix = get_node_prefix(xor_number, IDENTIFIER_SIZE);
xbt_assert(prefix <= IDENTIFIER_SIZE, "Tried to return a bucket that doesn't exist.");
bucket_t bucket = &table->buckets[prefix];
return bucket;
}
View examples/c/dht-kademlia/answer.c
/* Copyright (c) 2012-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "answer.h"
#include "node.h"
XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(dht_kademlia_node);
/** Initialize a node answer object. */
answer_t answer_init(unsigned int destination_id)
{
answer_t answer = xbt_new(s_answer_t, 1);
answer->nodes = xbt_dynar_new(sizeof(node_contact_t), NULL);
answer->size = 0;
answer->destination_id = destination_id;
return answer;
}
/** Destroys a node answer object. */
void answer_free(answer_t answer)
{
if (answer) {
for (unsigned int i = 0; i < answer->size; i++)
node_contact_free(*(void**)xbt_dynar_get_ptr(answer->nodes, i));
xbt_dynar_free(&answer->nodes);
}
xbt_free(answer);
}
/** @brief Prints an answer_t, for debugging purposes */
void answer_print(const_answer_t answer)
{
unsigned int cpt;
node_contact_t contact;
XBT_INFO("Searching %08x, size %u", answer->destination_id, answer->size);
xbt_dynar_foreach (answer->nodes, cpt, contact) {
XBT_INFO("Node %08x: %08x is at distance %u", cpt, contact->id, contact->distance);
}
}
/** @brief Merge two answer_t together, only keeping the best nodes
* @param destination the destination in which the nodes will be put
* @param source the source of the nodes to add
*/
unsigned int answer_merge(answer_t destination, const_answer_t source)
{
if (destination == source)
return 0;
node_contact_t contact;
node_contact_t contact_copy;
unsigned int cpt;
unsigned int nb_added = 0;
/* TODO: Check if same destination */
xbt_dynar_foreach (source->nodes, cpt, contact) {
if (answer_contains(destination, contact->id) == 0) {
contact_copy = node_contact_copy(contact);
xbt_dynar_push(destination->nodes, &contact_copy);
destination->size++;
nb_added++;
}
}
answer_sort(destination);
answer_trim(destination);
return nb_added;
}
/** Helper to sort answer_t objects */
static int _answer_sort_function(const void* e1, const void* e2)
{
const s_node_contact_t* c1 = *(const node_contact_t*)e1;
const s_node_contact_t* c2 = *(const node_contact_t*)e2;
if (c1->distance == c2->distance)
return 0;
else if (c1->distance < c2->distance)
return -1;
else
return 1;
}
/** @brief Sorts an answer_t, by node distance.
* @param answer the answer to sort
* @param destination_id the id of the guy we are trying to find
*/
void answer_sort(const_answer_t answer)
{
xbt_dynar_sort(answer->nodes, &_answer_sort_function);
}
/** @brief Trims an answer_t, in order for it to have a size of less or equal to "BUCKET_SIZE"
* @param answer the answer_t to trim
*/
void answer_trim(answer_t answer)
{
node_contact_t value;
while (answer->size > BUCKET_SIZE) {
xbt_dynar_pop(answer->nodes, &value);
answer->size--;
node_contact_free(value);
}
xbt_assert(xbt_dynar_length(answer->nodes) == answer->size, "Wrong size for the answer");
}
/** @brief Adds the content of a bucket unsigned into an answer object.
* @param bucket the bucket we have to had unsigned into
* @param answer the answer object we're going to put the data in
* @param destination_id the id of the guy we are trying to find.
*/
void answer_add_bucket(const_bucket_t bucket, answer_t answer)
{
xbt_assert((bucket != NULL), "Provided a NULL bucket");
xbt_assert((bucket->nodes != NULL), "Provided a bucket which nodes are NULL");
unsigned int cpt;
unsigned int id;
xbt_dynar_foreach (bucket->nodes, cpt, id) {
unsigned int distance = id ^ answer->destination_id;
node_contact_t contact = node_contact_new(id, distance);
xbt_dynar_push(answer->nodes, &contact);
answer->size++;
}
}
/** @brief Returns if the id supplied is in the answer.
* @param id : id we're looking for
*/
unsigned int answer_contains(const_answer_t answer, unsigned int id)
{
unsigned int i = 0;
node_contact_t contact;
xbt_dynar_foreach (answer->nodes, i, contact) {
if (id == contact->id) {
return 1;
}
}
return 0;
}
/** @brief Returns if the destination we are trying to find is found
* @param answer the answer
* @return if the destination is found.
*/
unsigned int answer_destination_found(const_answer_t answer)
{
if (xbt_dynar_is_empty(answer->nodes)) {
return 0;
}
const s_node_contact_t* contact_tail = xbt_dynar_get_as(answer->nodes, 0, node_contact_t);
return contact_tail->distance == 0;
}
View examples/c/dht-kademlia/message.c
/* Copyright (c) 2012-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "message.h"
#include "answer.h"
kademlia_message_t new_message(unsigned int sender_id, unsigned int destination_id, answer_t answer,
sg_mailbox_t mailbox, const char* hostname)
{
kademlia_message_t msg = xbt_new(s_kademlia_message_t, 1);
msg->sender_id = sender_id;
msg->destination_id = destination_id;
msg->answer = answer;
msg->answer_to = mailbox;
msg->issuer_host_name = hostname;
return msg;
}
void free_message(void* message)
{
const kademlia_message_t msg = (kademlia_message_t)message;
if (msg)
answer_free(msg->answer);
xbt_free(msg);
}
View examples/c/dht-kademlia/node.c
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "node.h"
#include "routing_table.h"
#include "simgrid/comm.h"
#include <stdio.h> /* snprintf */
XBT_LOG_NEW_DEFAULT_CATEGORY(dht_kademlia_node, "Messages specific for this example");
/** @brief Initialization of a node
* @param node_id the id of the node
* @return the node created
*/
node_t node_init(unsigned int node_id)
{
node_t node = xbt_new(s_node_t, 1);
node->id = node_id;
node->table = routing_table_init(node_id);
node->mailbox = get_node_mailbox(node_id);
node->find_node_failed = 0;
node->find_node_success = 0;
node->received_msg = NULL;
node->receive_comm = NULL;
return node;
}
/* @brief Node destructor */
void node_free(node_t node)
{
routing_table_free(node->table);
xbt_free(node);
}
/**
* Try to asynchronously get a new message from given mailbox. Return null if none available.
*/
kademlia_message_t receive(node_t node, sg_mailbox_t mailbox)
{
if (node->receive_comm == NULL)
node->receive_comm = sg_mailbox_get_async(mailbox, &node->received_msg);
if (!sg_comm_test(node->receive_comm))
return NULL;
node->receive_comm = NULL;
return node->received_msg;
}
/**
* @brief Tries to join the network
* @param node node data
* @param id_known id of the node I know in the network.
*/
unsigned int join(node_t node, unsigned int id_known)
{
unsigned int i;
unsigned int got_answer = 0;
sg_mailbox_t mailbox = get_node_mailbox(node->id);
/* Add the guy we know to our routing table and ourselves. */
routing_table_update(node, node->id);
routing_table_update(node, id_known);
/* First step: Send a "FIND_NODE" request to the node we know */
send_find_node(node, id_known, node->id);
do {
const kademlia_message_t msg = receive(node, mailbox);
if (msg) {
XBT_DEBUG("Received an answer from the node I know.");
got_answer = 1;
// retrieve the node list and ping them.
const s_answer_t* node_list = msg->answer;
if (node_list != NULL) {
node_contact_t contact;
xbt_dynar_foreach (node_list->nodes, i, contact)
routing_table_update(node, contact->id);
node->received_msg = NULL;
} else {
handle_find_node(node, msg);
}
free_message(msg);
} else {
sg_actor_sleep_for(1);
}
} while (got_answer == 0);
/* Second step: Send a FIND_NODE to a random node in buckets */
unsigned int bucket_id = routing_table_find_bucket(node->table, id_known)->id;
xbt_assert(bucket_id <= IDENTIFIER_SIZE);
for (i = 0; ((bucket_id > i) || (bucket_id + i) <= IDENTIFIER_SIZE) && i < JOIN_BUCKETS_QUERIES; i++) {
if (bucket_id > i) {
unsigned int id_in_bucket = get_id_in_prefix(node->id, bucket_id - i);
find_node(node, id_in_bucket, 0);
}
if (bucket_id + i <= IDENTIFIER_SIZE) {
unsigned int id_in_bucket = get_id_in_prefix(node->id, bucket_id + i);
find_node(node, id_in_bucket, 0);
}
}
return got_answer;
}
/** @brief Send a "FIND_NODE" to a node
* @param node sender node data
* @param id node we are querying
* @param destination node we are trying to find.
*/
void send_find_node(const_node_t node, unsigned int id, unsigned int destination)
{
/* Gets the mailbox to send to */
sg_mailbox_t mailbox = get_node_mailbox(id);
/* Build the message */
kademlia_message_t msg = new_message(node->id, destination, NULL, node->mailbox, sg_host_self_get_name());
sg_comm_t comm = sg_mailbox_put_init(mailbox, msg, COMM_SIZE);
sg_comm_detach(comm, free_message);
XBT_VERB("Asking %u for its closest nodes", id);
}
/**
* Sends to the best "KADEMLIA_ALPHA" nodes in the "node_list" array a "FIND_NODE" request, to ask them for their best
* nodes
*/
unsigned int send_find_node_to_best(const_node_t node, const_answer_t node_list)
{
unsigned int i = 0;
unsigned int j = 0;
unsigned int destination = node_list->destination_id;
while (j < KADEMLIA_ALPHA && i < node_list->size) {
/* We need to have at most "KADEMLIA_ALPHA" requests each time, according to the protocol */
/* Gets the node we want to send the query to */
const s_node_contact_t* node_to_query = xbt_dynar_get_as(node_list->nodes, i, node_contact_t);
if (node_to_query->id != node->id) { /* No need to query ourselves */
send_find_node(node, node_to_query->id, destination);
j++;
}
i++;
}
return i;
}
/** @brief Updates/Puts the node id unsigned into our routing table
* @param node Our node data
* @param id The id of the node we need to add unsigned into our routing table
*/
void routing_table_update(const_node_t node, unsigned int id)
{
const_routing_table_t table = node->table;
// retrieval of the bucket in which the should be
const_bucket_t bucket = routing_table_find_bucket(table, id);
// check if the id is already in the bucket.
unsigned int id_pos = bucket_find_id(bucket, id);
if (id_pos == (unsigned int)-1) {
/* We check if the bucket is full or not. If it is, we evict an old element */
if (xbt_dynar_length(bucket->nodes) >= BUCKET_SIZE)
xbt_dynar_pop(bucket->nodes, NULL);
xbt_dynar_unshift(bucket->nodes, &id);
XBT_VERB("I'm adding to my routing table %08x", id);
} else {
// We push to the front of the dynar the element.
unsigned int element = xbt_dynar_get_as(bucket->nodes, id_pos, unsigned int);
xbt_dynar_remove_at(bucket->nodes, id_pos, NULL);
xbt_dynar_unshift(bucket->nodes, &element);
XBT_VERB("I'm updating %08x", element);
}
}
/** @brief Finds the closest nodes to the node given.
* @param node : our node
* @param destination_id : the id of the guy we are trying to find
*/
answer_t find_closest(const_node_t node, unsigned int destination_id)
{
answer_t answer = answer_init(destination_id);
/* We find the corresponding bucket for the id */
const_bucket_t bucket = routing_table_find_bucket(node->table, destination_id);
int bucket_id = bucket->id;
xbt_assert((bucket_id <= IDENTIFIER_SIZE), "Bucket found has a wrong identifier");
/* So, we copy the contents of the bucket unsigned into our result dynar */
answer_add_bucket(bucket, answer);
/* However, if we don't have enough elements in our bucket, we NEED to include at least "BUCKET_SIZE" elements
* (if, of course, we know at least "BUCKET_SIZE" elements. So we're going to look unsigned into the other buckets.
*/
for (int i = 1; answer->size < BUCKET_SIZE && ((bucket_id - i > 0) || (bucket_id + i < IDENTIFIER_SIZE)); i++) {
/* We check the previous buckets */
if (bucket_id - i >= 0) {
const_bucket_t bucket_p = &node->table->buckets[bucket_id - i];
answer_add_bucket(bucket_p, answer);
}
/* We check the next buckets */
if (bucket_id + i <= IDENTIFIER_SIZE) {
const_bucket_t bucket_n = &node->table->buckets[bucket_id + i];
answer_add_bucket(bucket_n, answer);
}
}
/* We sort the array by XOR distance */
answer_sort(answer);
/* We trim the array to have only BUCKET_SIZE or less elements */
answer_trim(answer);
return answer;
}
unsigned int find_node(node_t node, unsigned int id_to_find, unsigned int count_in_stats)
{
unsigned int queries;
unsigned int answers;
unsigned int destination_found = 0;
unsigned int nodes_added = 0;
double global_timeout = simgrid_get_clock() + FIND_NODE_GLOBAL_TIMEOUT;
unsigned int steps = 0;
/* First we build a list of who we already know */
answer_t node_list = find_closest(node, id_to_find);
xbt_assert((node_list != NULL), "node_list incorrect");
XBT_DEBUG("Doing a FIND_NODE on %08x", id_to_find);
/* Ask the nodes on our list if they have information about the node we are trying to find */
sg_mailbox_t mailbox = get_node_mailbox(node->id);
do {
answers = 0;
queries = send_find_node_to_best(node, node_list);
nodes_added = 0;
double timeout = simgrid_get_clock() + FIND_NODE_TIMEOUT;
steps++;
double time_beginreceive = simgrid_get_clock();
do {
const kademlia_message_t msg = receive(node, mailbox);
if (msg) {
// Figure out if we received an answer or something else
// Check if what we have received is what we are looking for.
if (msg->answer != NULL && msg->answer->destination_id == id_to_find) {
// Handle the answer
routing_table_update(node, msg->sender_id);
node_contact_t contact;
unsigned int i;
xbt_dynar_foreach (node_list->nodes, i, contact)
routing_table_update(node, contact->id);
answers++;
nodes_added = answer_merge(node_list, msg->answer);
XBT_DEBUG("Received an answer from %s (%s) with %lu nodes on it", sg_mailbox_get_name(msg->answer_to),
msg->issuer_host_name, xbt_dynar_length(msg->answer->nodes));
} else {
if (msg->answer != NULL) {
routing_table_update(node, msg->sender_id);
XBT_DEBUG("Received a wrong answer for a FIND_NODE");
} else {
handle_find_node(node, msg);
}
// Update the timeout if we didn't have our answer
timeout += simgrid_get_clock() - time_beginreceive;
time_beginreceive = simgrid_get_clock();
}
free_message(msg);
} else {
sg_actor_sleep_for(1);
}
} while (simgrid_get_clock() < timeout && answers < queries);
destination_found = answer_destination_found(node_list);
} while (!destination_found && (nodes_added > 0 || answers == 0) && simgrid_get_clock() < global_timeout &&
steps < MAX_STEPS);
if (destination_found) {
if (count_in_stats)
node->find_node_success++;
if (queries > 4)
XBT_VERB("FIND_NODE on %08x success in %u steps", id_to_find, steps);
routing_table_update(node, id_to_find);
} else {
if (count_in_stats) {
node->find_node_failed++;
XBT_VERB("%08x not found in %u steps", id_to_find, steps);
}
}
answer_free(node_list);
return destination_found;
}
/** @brief Does a pseudo-random lookup for someone in the system
* @param node caller node data
*/
void random_lookup(node_t node)
{
unsigned int id_to_look = RANDOM_LOOKUP_NODE; // Totally random.
/* TODO: Use some pseudorandom generator. */
XBT_DEBUG("I'm doing a random lookup");
find_node(node, id_to_look, 1);
}
/** @brief Handles the answer to an incoming "find_node" message */
void handle_find_node(const_node_t node, const_kademlia_message_t msg)
{
routing_table_update(node, msg->sender_id);
XBT_VERB("Received a FIND_NODE from %s (%s), he's trying to find %08x", sg_mailbox_get_name(msg->answer_to),
msg->issuer_host_name, msg->destination_id);
// Building the msg to send
kademlia_message_t answer = new_message(node->id, msg->destination_id, find_closest(node, msg->destination_id),
node->mailbox, sg_host_self_get_name());
// Sending the msg
sg_comm_t comm = sg_mailbox_put_init(msg->answer_to, answer, COMM_SIZE);
sg_comm_detach(comm, &free_message);
}
/**@brief Returns an identifier which is in a specific bucket of a routing table
* @param id id of the routing table owner
* @param prefix id of the bucket where we want that identifier to be
*/
unsigned int get_id_in_prefix(unsigned int id, unsigned int prefix)
{
if (prefix == 0) {
return 0;
} else {
return (1U << (prefix - 1)) ^ id;
}
}
/** @brief Returns the prefix of an identifier.
* The prefix is the id of the bucket in which the remote identifier xor our identifier should be stored.
* @param id : big unsigned int id to test
* @param nb_bits : key size
*/
unsigned int get_node_prefix(unsigned int id, unsigned int nb_bits)
{
unsigned int size = sizeof(unsigned int) * 8;
for (unsigned int j = 0; j < size; j++) {
if (((id >> (size - 1 - j)) & 0x1) != 0) {
return nb_bits - j;
}
}
return 0;
}
/** @brief Gets the mailbox name of a host given its identifier */
sg_mailbox_t get_node_mailbox(unsigned int id)
{
char mailbox_name[MAILBOX_NAME_SIZE];
snprintf(mailbox_name, MAILBOX_NAME_SIZE - 1, "%u", id);
return sg_mailbox_by_name(mailbox_name);
}
/** Constructor, build a new contact information. */
node_contact_t node_contact_new(unsigned int id, unsigned int distance)
{
node_contact_t contact = xbt_new(s_node_contact_t, 1);
contact->id = id;
contact->distance = distance;
return contact;
}
/** Builds a contact information from a contact information */
node_contact_t node_contact_copy(const_node_contact_t node_contact)
{
node_contact_t contact = xbt_new(s_node_contact_t, 1);
contact->id = node_contact->id;
contact->distance = node_contact->distance;
return contact;
}
/** Destructor */
void node_contact_free(node_contact_t contact)
{
xbt_free(contact);
}
Pastry
Yet another well-known DHT protocol.
View examples/c/dht-pastry/dht-pastry.c
/* Copyright (c) 2013-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/comm.h"
#include "simgrid/engine.h"
#include "simgrid/mailbox.h"
#include "xbt/dynar.h"
#include "xbt/ex.h"
#include "xbt/log.h"
#include "xbt/str.h"
#include "xbt/sysdep.h"
#include <stdio.h>
XBT_LOG_NEW_DEFAULT_CATEGORY(pastry, "Messages specific for this example");
#define COMM_SIZE 10
#define COMP_SIZE 0
#define MAILBOX_NAME_SIZE 10
#define DOMAIN_SIZE 4
#define LEVELS_COUNT 8 // sizeof(int)*8/DOMAIN_SIZE
#define LEVEL_SIZE 16 // 2^DOMAIN_SIZE
#define NEIGHBORHOOD_SIZE 6
#define NAMESPACE_SIZE 6
#define MAILBOX_NAME_SIZE 10
static int nb_bits = 16;
static int timeout = 50;
static int max_simulation_time = 1000;
typedef struct s_node {
int id; // 128bits generated random(2^128 -1)
int known_id;
sg_mailbox_t mailbox;
int namespace_set[NAMESPACE_SIZE];
int neighborhood_set[NEIGHBORHOOD_SIZE];
int routing_table[LEVELS_COUNT][LEVEL_SIZE];
int ready;
sg_comm_t comm_receive; // current communication to receive
xbt_dynar_t pending_messages;
} s_node_t;
typedef s_node_t* node_t;
typedef const s_node_t* const_node_t;
typedef struct s_state {
int id;
int namespace_set[NAMESPACE_SIZE];
int neighborhood_set[NEIGHBORHOOD_SIZE];
int routing_table[LEVELS_COUNT][LEVEL_SIZE];
} s_state_t;
typedef s_state_t* state_t;
/** Types of tasks exchanged between nodes. */
typedef enum { JOIN, JOIN_REPLY, JOIN_LAST_REPLY, UPDATE } e_message_type_t;
typedef struct s_pastry_message {
e_message_type_t type; // type of task
int sender_id; // id parameter (used by some types of tasks)
// int request_finger; // finger parameter (used by some types of tasks)
int answer_id; // answer (used by some types of tasks)
sg_mailbox_t answer_to; // mailbox to send an answer to (if any)
int steps;
state_t state;
} s_pastry_message_t;
typedef s_pastry_message_t* pastry_message_t;
typedef const s_pastry_message_t* const_pastry_message_t;
/** Get the mailbox of a host given its pastry id. */
static sg_mailbox_t get_mailbox(int node_id)
{
char mailbox_name[MAILBOX_NAME_SIZE];
snprintf(mailbox_name, MAILBOX_NAME_SIZE - 1, "%d", node_id);
return sg_mailbox_by_name(mailbox_name);
}
/** Get the specific level of a node id */
unsigned int domain_mask = 0;
static int domain(unsigned int a, unsigned int level)
{
if (domain_mask == 0)
domain_mask = (1U << DOMAIN_SIZE) - 1;
unsigned int shift = (LEVELS_COUNT - level - 1) * DOMAIN_SIZE;
return (a >> shift) & domain_mask;
}
/* Get the shared domains between the two givens ids */
static int shl(int a, int b)
{
int l = 0;
while (l < LEVELS_COUNT && domain(a, l) == domain(b, l))
l++;
return l;
}
/* Frees the memory used by a task and destroy it */
static void message_free(pastry_message_t message)
{
if (message != NULL) {
xbt_free(message->state);
xbt_free(message);
}
}
/* Get the closest id to the dest in the node namespace_set */
static int closest_in_namespace_set(const_node_t node, int dest)
{
int res = -1;
if ((node->namespace_set[NAMESPACE_SIZE - 1] <= dest) && (dest <= node->namespace_set[0])) {
int best_dist = abs(node->id - dest);
res = node->id;
for (int i = 0; i < NAMESPACE_SIZE; i++) {
if (node->namespace_set[i] != -1) {
int dist = abs(node->namespace_set[i] - dest);
if (dist < best_dist) {
best_dist = dist;
res = node->namespace_set[i];
}
}
}
}
return res;
}
/* Find the next node to forward a message to */
static int routing_next(const_node_t node, int dest)
{
int closest = closest_in_namespace_set(node, dest);
if (closest != -1)
return closest;
int l = shl(node->id, dest);
int res = node->routing_table[l][domain(dest, l)];
if (res != -1)
return res;
// rare case
int dist = abs(node->id - dest);
for (int i = l; i < LEVELS_COUNT; i++) {
for (int j = 0; j < LEVEL_SIZE; j++) {
res = node->routing_table[i][j];
if (res != -1 && abs(res - dest) < dist)
return res;
}
}
for (int i = 0; i < NEIGHBORHOOD_SIZE; i++) {
res = node->neighborhood_set[i];
if (res != -1 && shl(res, dest) >= l && abs(res - dest) < dist)
return res;
}
for (int i = 0; i < NAMESPACE_SIZE; i++) {
res = node->namespace_set[i];
if (res != -1 && shl(res, dest) >= l && abs(res - dest) < dist)
return res;
}
return node->id;
}
/* Get the corresponding state of a node */
static state_t node_get_state(const_node_t node)
{
state_t state = xbt_new0(s_state_t, 1);
state->id = node->id;
for (int i = 0; i < NEIGHBORHOOD_SIZE; i++)
state->neighborhood_set[i] = node->neighborhood_set[i];
for (int i = 0; i < LEVELS_COUNT; i++)
for (int j = 0; j < LEVEL_SIZE; j++)
state->routing_table[i][j] = node->routing_table[i][j];
for (int i = 0; i < NAMESPACE_SIZE; i++)
state->namespace_set[i] = node->namespace_set[i];
return state;
}
static void print_node_id(const_node_t node)
{
XBT_INFO(" Id: %i '%08x'", node->id, (unsigned)node->id);
}
/* Print the node namespace set */
static void print_node_namespace_set(const_node_t node)
{
XBT_INFO(" Namespace:");
for (int i = 0; i < NAMESPACE_SIZE; i++)
XBT_INFO(" %08x", (unsigned)node->namespace_set[i]);
}
/** Handle a given task */
static void handle_message(node_t node, pastry_message_t message)
{
XBT_DEBUG("Handling task %p", message);
int i;
int j;
int min;
int max;
int next;
sg_mailbox_t mailbox;
sg_comm_t comm = NULL;
sg_error_t err = SG_OK;
pastry_message_t request;
e_message_type_t type = message->type;
// If the node is not ready keep the task for later
if (node->ready != 0 && !(type == JOIN_LAST_REPLY || type == JOIN_REPLY)) {
XBT_DEBUG("Task pending %u", type);
xbt_dynar_push(node->pending_messages, &message);
return;
}
switch (type) {
/* Try to join the ring */
case JOIN:
next = routing_next(node, message->answer_id);
XBT_DEBUG("Join request from %08x forwarding to %08x", (unsigned)message->answer_id, (unsigned)next);
type = JOIN_LAST_REPLY;
request = xbt_new0(s_pastry_message_t, 1);
request->answer_id = message->sender_id;
request->steps = message->steps + 1;
// if next different from current node forward the join
if (next != node->id) {
mailbox = get_mailbox(next);
message->sender_id = node->id;
message->steps++;
comm = sg_mailbox_put_async(mailbox, message, COMM_SIZE);
err = sg_comm_wait_for(comm, timeout);
if (err == SG_ERROR_TIMEOUT) {
XBT_DEBUG("Timeout expired when forwarding join to next %d", next);
xbt_free(request);
break;
}
type = JOIN_REPLY;
}
// send back the current node state to the joining node
request->type = type;
request->sender_id = node->id;
request->answer_to = get_mailbox(node->id);
request->state = node_get_state(node);
comm = sg_mailbox_put_async(message->answer_to, request, COMM_SIZE);
err = sg_comm_wait_for(comm, timeout);
if (err == SG_ERROR_TIMEOUT) {
XBT_DEBUG("Timeout expired when sending back the current node state to the joining node to %d", node->id);
message_free(request);
}
break;
/* Join reply from all the node touched by the join */
case JOIN_LAST_REPLY:
// if last node touched reply, copy its namespace set
// TODO: it works only if the two nodes are side to side (is it really the case ?)
j = (message->sender_id < node->id) ? -1 : 0;
for (i = 0; i < NAMESPACE_SIZE / 2; i++) {
node->namespace_set[i] = message->state->namespace_set[i - j];
node->namespace_set[NAMESPACE_SIZE - 1 - i] = message->state->namespace_set[NAMESPACE_SIZE - 1 - i - j - 1];
}
node->namespace_set[NAMESPACE_SIZE / 2 + j] = message->sender_id;
node->ready += message->steps + 1;
/* fallthrough */
case JOIN_REPLY:
XBT_DEBUG("Joining Reply");
// if first node touched reply, copy its neighborhood set
if (message->sender_id == node->known_id) {
node->neighborhood_set[0] = message->sender_id;
for (i = 1; i < NEIGHBORHOOD_SIZE; i++)
node->neighborhood_set[i] = message->state->neighborhood_set[i - 1];
}
// copy the corresponding routing table levels
min = (node->id == message->answer_id) ? 0 : shl(node->id, message->answer_id);
max = shl(node->id, message->sender_id) + 1;
for (i = min; i < max; i++) {
int d = domain(node->id, i);
for (j = 0; j < LEVEL_SIZE; j++)
if (d != j)
node->routing_table[i][j] = message->state->routing_table[i][j];
}
node->ready--;
// if the node is ready, do all the pending tasks and send update to known nodes
if (node->ready == 0) {
XBT_DEBUG("Node %i is ready!!!", node->id);
while (!xbt_dynar_is_empty(node->pending_messages)) {
pastry_message_t m;
xbt_dynar_shift(node->pending_messages, &m);
handle_message(node, m);
}
for (i = 0; i < NAMESPACE_SIZE; i++) {
j = node->namespace_set[i];
if (j != -1) {
XBT_DEBUG("Send update to %i", j);
mailbox = get_mailbox(j);
request = xbt_new0(s_pastry_message_t, 1);
request->answer_id = node->id;
request->steps = 0;
request->type = UPDATE;
request->sender_id = node->id;
request->answer_to = get_mailbox(node->id);
request->state = node_get_state(node);
comm = sg_mailbox_put_async(mailbox, request, COMM_SIZE);
err = sg_comm_wait_for(comm, timeout);
if (err == SG_ERROR_TIMEOUT) {
XBT_DEBUG("Timeout expired when sending update to %d", j);
message_free(request);
break;
}
}
}
}
break;
/* Received an update of state */
case UPDATE:
XBT_DEBUG("Task update %i !!!", node->id);
/* Update namespace ses */
XBT_INFO("Task update from %i !!!", message->sender_id);
XBT_INFO("Node:");
print_node_id(node);
print_node_namespace_set(node);
int curr_namespace_set[NAMESPACE_SIZE];
int task_namespace_set[NAMESPACE_SIZE + 1];
// Copy the current namespace and the task state namespace with state->id in the middle
i = 0;
for (; i < NAMESPACE_SIZE / 2; i++) {
curr_namespace_set[i] = node->namespace_set[i];
task_namespace_set[i] = message->state->namespace_set[i];
}
task_namespace_set[i] = message->state->id;
for (; i < NAMESPACE_SIZE; i++) {
curr_namespace_set[i] = node->namespace_set[i];
task_namespace_set[i + 1] = message->state->namespace_set[i];
}
// get the index of values before and after node->id in task_namespace
min = -1;
max = -1;
for (i = 0; i <= NAMESPACE_SIZE; i++) {
j = task_namespace_set[i];
if (j != -1 && j < node->id)
min = i;
if (j != -1 && max == -1 && j > node->id)
max = i;
}
// add lower elements
j = NAMESPACE_SIZE / 2 - 1;
for (i = NAMESPACE_SIZE / 2 - 1; i >= 0; i--) {
if (min < 0 || curr_namespace_set[j] > task_namespace_set[min]) {
node->namespace_set[i] = curr_namespace_set[j];
j--;
} else if (curr_namespace_set[j] == task_namespace_set[min]) {
node->namespace_set[i] = curr_namespace_set[j];
j--;
min--;
} else {
node->namespace_set[i] = task_namespace_set[min];
min--;
}
}
// add greater elements
j = NAMESPACE_SIZE / 2;
for (i = NAMESPACE_SIZE / 2; i < NAMESPACE_SIZE; i++) {
if (min < 0 || max >= NAMESPACE_SIZE) {
node->namespace_set[i] = curr_namespace_set[j];
j++;
} else if (max >= 0) {
if (curr_namespace_set[j] == -1 || curr_namespace_set[j] > task_namespace_set[max]) {
node->namespace_set[i] = task_namespace_set[max];
max++;
} else if (curr_namespace_set[j] == task_namespace_set[max]) {
node->namespace_set[i] = curr_namespace_set[j];
j++;
max++;
} else {
node->namespace_set[i] = curr_namespace_set[j];
j++;
}
}
}
/* Update routing table */
for (i = shl(node->id, message->state->id); i < LEVELS_COUNT; i++) {
for (j = 0; j < LEVEL_SIZE; j++) {
// FIXME: this is a no-op!
if (node->routing_table[i][j] == -1 && message->state->routing_table[i][j] == -1)
node->routing_table[i][j] = message->state->routing_table[i][j];
}
}
break;
default:
THROW_IMPOSSIBLE;
}
message_free(message);
}
/* Join the ring */
static int join(const_node_t node)
{
pastry_message_t request = xbt_new0(s_pastry_message_t, 1);
request->type = JOIN;
request->sender_id = node->id;
request->answer_id = node->id;
request->steps = 0;
request->answer_to = get_mailbox(node->id);
sg_mailbox_t mailbox = get_mailbox(node->known_id);
XBT_DEBUG("Trying to join Pastry ring... (with node %s)", sg_mailbox_get_name(mailbox));
sg_comm_t comm = sg_mailbox_put_async(mailbox, request, COMM_SIZE);
sg_error_t err = sg_comm_wait_for(comm, timeout);
if (err == SG_ERROR_TIMEOUT) {
XBT_DEBUG("Timeout expired when joining ring with node %d", node->known_id);
message_free(request);
return 0;
}
return 1;
}
/**
* @brief Node Function
* Arguments:
* - my id
* - the id of a guy I know in the system (except for the first node)
* - the time to sleep before I join (except for the first node)
* - the deadline time
*/
static void node(int argc, char* argv[])
{
double init_time = simgrid_get_clock();
void* received = NULL;
int join_success = 0;
double deadline;
xbt_assert(argc == 3 || argc == 5, "Wrong number of arguments for this node");
s_node_t node = {0};
node.id = (int)xbt_str_parse_int(argv[1], "Invalid ID");
node.known_id = -1;
node.ready = -1;
node.pending_messages = xbt_dynar_new(sizeof(pastry_message_t), NULL);
node.mailbox = get_mailbox(node.id);
XBT_DEBUG("New node with id %s (%08x)", sg_mailbox_get_name(node.mailbox), (unsigned)node.id);
for (int i = 0; i < LEVELS_COUNT; i++) {
int d = domain(node.id, i);
for (int j = 0; j < LEVEL_SIZE; j++)
node.routing_table[i][j] = (d == j) ? node.id : -1;
}
for (int i = 0; i < NEIGHBORHOOD_SIZE; i++)
node.neighborhood_set[i] = -1;
for (int i = 0; i < NAMESPACE_SIZE; i++)
node.namespace_set[i] = -1;
if (argc == 3) { // first ring
XBT_DEBUG("Hey! Let's create the system.");
deadline = xbt_str_parse_double(argv[2], "Invalid deadline");
node.ready = 0;
XBT_DEBUG("Create a new Pastry ring...");
join_success = 1;
} else {
node.known_id = (int)xbt_str_parse_int(argv[2], "Invalid known ID");
double sleep_time = xbt_str_parse_double(argv[3], "Invalid sleep time");
deadline = xbt_str_parse_double(argv[4], "Invalid deadline");
// sleep before starting
XBT_DEBUG("Let's sleep during %f", sleep_time);
sg_actor_sleep_for(sleep_time);
XBT_DEBUG("Hey! Let's join the system.");
join_success = join(&node);
}
if (join_success) {
XBT_DEBUG("Waiting ….");
while (simgrid_get_clock() < init_time + deadline && simgrid_get_clock() < max_simulation_time) {
if (node.comm_receive == NULL) {
received = NULL;
node.comm_receive = sg_mailbox_get_async(node.mailbox, &received);
}
if (!sg_comm_test(node.comm_receive)) {
sg_actor_sleep_for(5);
} else {
// the task was successfully received
handle_message(&node, received);
node.comm_receive = NULL;
}
}
// Cleanup the receiving communication.
if (node.comm_receive != NULL)
sg_comm_unref(node.comm_receive);
}
xbt_dynar_free(&node.pending_messages);
}
/** @brief Main function. */
int main(int argc, char* argv[])
{
simgrid_init(&argc, argv);
xbt_assert(argc > 2,
"Usage: %s [-nb_bits=n] [-timeout=t] platform_file deployment_file\n"
"\tExample: %s ../platform.xml pastry10.xml\n",
argv[0], argv[0]);
char** options = &argv[1];
while (!strncmp(options[0], "-", 1)) {
size_t length = strlen("-nb_bits=");
if (!strncmp(options[0], "-nb_bits=", length) && strlen(options[0]) > length) {
nb_bits = (int)xbt_str_parse_int(options[0] + length, "Invalid nb_bits parameter");
XBT_DEBUG("Set nb_bits to %d", nb_bits);
} else {
length = strlen("-timeout=");
xbt_assert(strncmp(options[0], "-timeout=", length) == 0 && strlen(options[0]) > length,
"Invalid pastry option '%s'", options[0]);
timeout = (int)xbt_str_parse_int(options[0] + length, "Invalid timeout parameter");
XBT_DEBUG("Set timeout to %d", timeout);
}
options++;
}
simgrid_load_platform(options[0]);
simgrid_register_function("node", node);
simgrid_load_deployment(options[1]);
simgrid_run();
XBT_INFO("Simulated time: %g", simgrid_get_clock());
return 0;
}
Simulating Clouds
Cloud basics
This example starts some computations both on PMs and VMs and migrates some VMs around.
View examples/cpp/cloud-simple/s4u-cloud-simple.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include "simgrid/plugins/live_migration.h"
#include "simgrid/s4u/VirtualMachine.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void computation_fun()
{
double clock_sta = sg4::Engine::get_clock();
sg4::this_actor::execute(1000000);
double clock_end = sg4::Engine::get_clock();
XBT_INFO("%s:%s executed %g", sg4::this_actor::get_host()->get_cname(), sg4::this_actor::get_cname(),
clock_end - clock_sta);
}
static void launch_computation_worker(s4u_Host* host)
{
sg4::Actor::create("compute", host, computation_fun);
}
struct s_payload {
s4u_Host* tx_host;
const char* tx_actor_name;
double clock_sta;
};
static void communication_tx_fun(std::vector<std::string> args)
{
sg4::Mailbox* mbox = sg4::Mailbox::by_name(args.at(0));
auto* payload = new s_payload;
payload->tx_actor_name = sg4::Actor::self()->get_cname();
payload->tx_host = sg4::this_actor::get_host();
payload->clock_sta = sg4::Engine::get_clock();
mbox->put(payload, 1000000);
}
static void communication_rx_fun(std::vector<std::string> args)
{
const char* actor_name = sg4::Actor::self()->get_cname();
const char* host_name = sg4::this_actor::get_host()->get_cname();
sg4::Mailbox* mbox = sg4::Mailbox::by_name(args.at(0));
auto payload = mbox->get_unique<struct s_payload>();
double clock_end = sg4::Engine::get_clock();
XBT_INFO("%s:%s to %s:%s => %g sec", payload->tx_host->get_cname(), payload->tx_actor_name, host_name, actor_name,
clock_end - payload->clock_sta);
}
static void launch_communication_worker(s4u_Host* tx_host, s4u_Host* rx_host)
{
std::string mbox_name = "MBOX:" + tx_host->get_name() + "-" + rx_host->get_name();
std::vector<std::string> args;
args.push_back(mbox_name);
sg4::Actor::create("comm_tx", tx_host, communication_tx_fun, args);
sg4::Actor::create("comm_rx", rx_host, communication_rx_fun, args);
}
static void master_main()
{
s4u_Host* pm0 = sg4::Host::by_name("Fafard");
s4u_Host* pm1 = sg4::Host::by_name("Tremblay");
s4u_Host* pm2 = sg4::Host::by_name("Bourassa");
XBT_INFO("## Test 1 (started): check computation on normal PMs");
XBT_INFO("### Put an activity on a PM");
launch_computation_worker(pm0);
sg4::this_actor::sleep_for(2);
XBT_INFO("### Put two activities on a PM");
launch_computation_worker(pm0);
launch_computation_worker(pm0);
sg4::this_actor::sleep_for(2);
XBT_INFO("### Put an activity on each PM");
launch_computation_worker(pm0);
launch_computation_worker(pm1);
sg4::this_actor::sleep_for(2);
XBT_INFO("## Test 1 (ended)");
XBT_INFO(
"## Test 2 (started): check impact of running an activity inside a VM (there is no degradation for the moment)");
XBT_INFO("### Put a VM on a PM, and put an activity to the VM");
auto* vm0 = pm0->create_vm("VM0", 1);
vm0->start();
launch_computation_worker(vm0);
sg4::this_actor::sleep_for(2);
vm0->destroy();
XBT_INFO("## Test 2 (ended)");
XBT_INFO("## Test 3 (started): check impact of running an activity collocated with a VM (there is no VM noise for "
"the moment)");
XBT_INFO("### Put a VM on a PM, and put an activity to the PM");
vm0 = pm0->create_vm("VM0", 1);
vm0->start();
launch_computation_worker(pm0);
sg4::this_actor::sleep_for(2);
vm0->destroy();
XBT_INFO("## Test 3 (ended)");
XBT_INFO(
"## Test 4 (started): compare the cost of running two activities inside two different VMs collocated or not (for"
" the moment, there is no degradation for the VMs. Hence, the time should be equals to the time of test 1");
XBT_INFO("### Put two VMs on a PM, and put an activity to each VM");
vm0 = pm0->create_vm("VM0", 1);
vm0->start();
auto* vm1 = pm0->create_vm("VM1", 1);
launch_computation_worker(vm0);
launch_computation_worker(vm1);
sg4::this_actor::sleep_for(2);
vm0->destroy();
vm1->destroy();
XBT_INFO("### Put a VM on each PM, and put an activity to each VM");
vm0 = pm0->create_vm("VM0", 1);
vm1 = pm1->create_vm("VM1", 1);
vm0->start();
vm1->start();
launch_computation_worker(vm0);
launch_computation_worker(vm1);
sg4::this_actor::sleep_for(2);
vm0->destroy();
vm1->destroy();
XBT_INFO("## Test 4 (ended)");
XBT_INFO("## Test 5 (started): Analyse network impact");
XBT_INFO("### Make a connection between PM0 and PM1");
launch_communication_worker(pm0, pm1);
sg4::this_actor::sleep_for(5);
XBT_INFO("### Make two connection between PM0 and PM1");
launch_communication_worker(pm0, pm1);
launch_communication_worker(pm0, pm1);
sg4::this_actor::sleep_for(5);
XBT_INFO("### Make a connection between PM0 and VM0@PM0");
vm0 = pm0->create_vm("VM0", 1);
vm0->start();
launch_communication_worker(pm0, vm0);
sg4::this_actor::sleep_for(5);
vm0->destroy();
XBT_INFO("### Make a connection between PM0 and VM0@PM1");
vm0 = pm1->create_vm("VM0", 1);
launch_communication_worker(pm0, vm0);
sg4::this_actor::sleep_for(5);
vm0->destroy();
XBT_INFO("### Make two connections between PM0 and VM0@PM1");
vm0 = pm1->create_vm("VM0", 1);
vm0->start();
launch_communication_worker(pm0, vm0);
launch_communication_worker(pm0, vm0);
sg4::this_actor::sleep_for(5);
vm0->destroy();
XBT_INFO("### Make a connection between PM0 and VM0@PM1, and also make a connection between PM0 and PM1");
vm0 = pm1->create_vm("VM0", 1);
vm0->start();
launch_communication_worker(pm0, vm0);
launch_communication_worker(pm0, pm1);
sg4::this_actor::sleep_for(5);
vm0->destroy();
XBT_INFO("### Make a connection between VM0@PM0 and PM1@PM1, and also make a connection between VM0@PM0 and VM1@PM1");
vm0 = pm0->create_vm("VM0", 1);
vm1 = pm1->create_vm("VM1", 1);
vm0->start();
vm1->start();
launch_communication_worker(vm0, vm1);
launch_communication_worker(vm0, vm1);
sg4::this_actor::sleep_for(5);
vm0->destroy();
vm1->destroy();
XBT_INFO("## Test 5 (ended)");
XBT_INFO("## Test 6 (started): Check migration impact (not yet implemented neither on the CPU resource nor on the"
" network one");
XBT_INFO("### Relocate VM0 between PM0 and PM1");
vm0 = pm0->create_vm("VM0", 1);
vm0->set_ramsize(1L * 1024 * 1024 * 1024); // 1GiB
vm0->start();
launch_communication_worker(vm0, pm2);
sg4::this_actor::sleep_for(0.01);
sg_vm_migrate(vm0, pm1);
sg4::this_actor::sleep_for(0.01);
sg_vm_migrate(vm0, pm0);
sg4::this_actor::sleep_for(5);
vm0->destroy();
XBT_INFO("## Test 6 (ended)");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
sg_vm_live_migration_plugin_init();
e.load_platform(argv[1]); /* - Load the platform description */
sg4::Actor::create("master_", e.host_by_name("Fafard"), master_main);
e.run();
XBT_INFO("Simulation time %g", sg4::Engine::get_clock());
return 0;
}
View examples/c/cloud-simple/cloud-simple.c
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "simgrid/mailbox.h"
#include "simgrid/plugins/live_migration.h"
#include "simgrid/vm.h"
#include "xbt/log.h"
#include "xbt/str.h"
#include "xbt/sysdep.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(cloud_simple, "Messages specific for this example");
static void computation_fun(int argc, char* argv[])
{
const char* pr_name = sg_actor_get_name(sg_actor_self());
const char* host_name = sg_host_get_name(sg_host_self());
double clock_sta = simgrid_get_clock();
sg_actor_execute(1000000);
double clock_end = simgrid_get_clock();
XBT_INFO("%s:%s task executed %g", host_name, pr_name, clock_end - clock_sta);
}
static void launch_computation_worker(sg_host_t host)
{
sg_actor_create("compute", host, &computation_fun, 0, NULL);
}
struct task_priv {
sg_host_t tx_host;
sg_actor_t tx_proc;
double clock_sta;
};
static void communication_tx_fun(int argc, char* argv[])
{
xbt_assert(argc == 2);
sg_mailbox_t mbox = sg_mailbox_by_name(argv[1]);
struct task_priv* priv = xbt_new(struct task_priv, 1);
priv->tx_proc = sg_actor_self();
priv->tx_host = sg_host_self();
priv->clock_sta = simgrid_get_clock();
sg_mailbox_put(mbox, priv, 1000000);
}
static void communication_rx_fun(int argc, char* argv[])
{
const char* pr_name = sg_actor_get_name(sg_actor_self());
const char* host_name = sg_host_get_name(sg_host_self());
xbt_assert(argc == 2);
sg_mailbox_t mbox = sg_mailbox_by_name(argv[1]);
struct task_priv* priv = (struct task_priv*)sg_mailbox_get(mbox);
double clock_end = simgrid_get_clock();
XBT_INFO("%s:%s to %s:%s => %g sec", sg_host_get_name(priv->tx_host), sg_actor_get_name(priv->tx_proc), host_name,
pr_name, clock_end - priv->clock_sta);
xbt_free(priv);
}
static void launch_communication_worker(sg_host_t tx_host, sg_host_t rx_host)
{
char* mbox = bprintf("MBOX:%s-%s", sg_host_get_name(tx_host), sg_host_get_name(rx_host));
const char* tx_argv[] = {"comm_tx", mbox, NULL};
sg_actor_create_("comm_tx", tx_host, &communication_tx_fun, 2, tx_argv);
const char* rx_argv[] = {"comm_rx", mbox, NULL};
sg_actor_create_("comm_rx", rx_host, &communication_rx_fun, 2, rx_argv);
xbt_free(mbox);
}
static void master_main(int argc, char* argv[])
{
sg_host_t pm0 = sg_host_by_name("Fafard");
sg_host_t pm1 = sg_host_by_name("Tremblay");
sg_host_t pm2 = sg_host_by_name("Bourassa");
XBT_INFO("## Test 1 (started): check computation on normal PMs");
XBT_INFO("### Put a task on a PM");
launch_computation_worker(pm0);
sg_actor_sleep_for(2);
XBT_INFO("### Put two tasks on a PM");
launch_computation_worker(pm0);
launch_computation_worker(pm0);
sg_actor_sleep_for(2);
XBT_INFO("### Put a task on each PM");
launch_computation_worker(pm0);
launch_computation_worker(pm1);
sg_actor_sleep_for(2);
XBT_INFO("## Test 1 (ended)");
XBT_INFO("## Test 2 (started): check impact of running a task inside a VM (there is no degradation for the moment)");
XBT_INFO("### Put a VM on a PM, and put a task to the VM");
sg_vm_t vm0 = sg_vm_create_core(pm0, "VM0");
sg_vm_start(vm0);
launch_computation_worker((sg_host_t)vm0);
sg_actor_sleep_for(2);
sg_vm_destroy(vm0);
XBT_INFO("## Test 2 (ended)");
XBT_INFO(
"## Test 3 (started): check impact of running a task collocated with a VM (there is no VM noise for the moment)");
XBT_INFO("### Put a VM on a PM, and put a task to the PM");
vm0 = sg_vm_create_core(pm0, "VM0");
sg_vm_start(vm0);
launch_computation_worker(pm0);
sg_actor_sleep_for(2);
sg_vm_destroy(vm0);
XBT_INFO("## Test 3 (ended)");
XBT_INFO("## Test 4 (started): compare the cost of running two tasks inside two different VMs collocated or not (for"
" the moment, there is no degradation for the VMs. Hence, the time should be equals to the time of test 1");
XBT_INFO("### Put two VMs on a PM, and put a task to each VM");
vm0 = sg_vm_create_core(pm0, "VM0");
sg_vm_t vm1 = sg_vm_create_core(pm0, "VM1");
sg_vm_start(vm0);
sg_vm_start(vm1);
launch_computation_worker((sg_host_t)vm0);
launch_computation_worker((sg_host_t)vm1);
sg_actor_sleep_for(2);
sg_vm_destroy(vm0);
sg_vm_destroy(vm1);
XBT_INFO("### Put a VM on each PM, and put a task to each VM");
vm0 = sg_vm_create_core(pm0, "VM0");
vm1 = sg_vm_create_core(pm1, "VM1");
sg_vm_start(vm0);
sg_vm_start(vm1);
launch_computation_worker((sg_host_t)vm0);
launch_computation_worker((sg_host_t)vm1);
sg_actor_sleep_for(2);
sg_vm_destroy(vm0);
sg_vm_destroy(vm1);
XBT_INFO("## Test 4 (ended)");
XBT_INFO("## Test 5 (started): Analyse network impact");
XBT_INFO("### Make a connection between PM0 and PM1");
launch_communication_worker(pm0, pm1);
sg_actor_sleep_for(5);
XBT_INFO("### Make two connection between PM0 and PM1");
launch_communication_worker(pm0, pm1);
launch_communication_worker(pm0, pm1);
sg_actor_sleep_for(5);
XBT_INFO("### Make a connection between PM0 and VM0@PM0");
vm0 = sg_vm_create_core(pm0, "VM0");
sg_vm_start(vm0);
launch_communication_worker(pm0, (sg_host_t)vm0);
sg_actor_sleep_for(5);
sg_vm_destroy(vm0);
XBT_INFO("### Make a connection between PM0 and VM0@PM1");
vm0 = sg_vm_create_core(pm1, "VM0");
sg_vm_start(vm0);
launch_communication_worker(pm0, (sg_host_t)vm0);
sg_actor_sleep_for(5);
sg_vm_destroy(vm0);
XBT_INFO("### Make two connections between PM0 and VM0@PM1");
vm0 = sg_vm_create_core(pm1, "VM0");
sg_vm_start(vm0);
launch_communication_worker(pm0, (sg_host_t)vm0);
launch_communication_worker(pm0, (sg_host_t)vm0);
sg_actor_sleep_for(5);
sg_vm_destroy(vm0);
XBT_INFO("### Make a connection between PM0 and VM0@PM1, and also make a connection between PM0 and PM1");
vm0 = sg_vm_create_core(pm1, "VM0");
sg_vm_start(vm0);
launch_communication_worker(pm0, (sg_host_t)vm0);
launch_communication_worker(pm0, pm1);
sg_actor_sleep_for(5);
sg_vm_destroy(vm0);
XBT_INFO("### Make a connection between VM0@PM0 and PM1@PM1, and also make a connection between VM0@PM0 and VM1@PM1");
vm0 = sg_vm_create_core(pm0, "VM0");
vm1 = sg_vm_create_core(pm1, "VM1");
sg_vm_start(vm0);
sg_vm_start(vm1);
launch_communication_worker((sg_host_t)vm0, (sg_host_t)vm1);
launch_communication_worker((sg_host_t)vm0, (sg_host_t)vm1);
sg_actor_sleep_for(5);
sg_vm_destroy(vm0);
sg_vm_destroy(vm1);
XBT_INFO("## Test 5 (ended)");
XBT_INFO("## Test 6 (started): Check migration impact (not yet implemented neither on the CPU resource nor on the"
" network one");
XBT_INFO("### Relocate VM0 between PM0 and PM1");
vm0 = sg_vm_create_core(pm0, "VM0");
sg_vm_set_ramsize(vm0, 1L * 1024 * 1024 * 1024); // 1GiB
sg_vm_start(vm0);
launch_communication_worker((sg_host_t)vm0, pm2);
sg_actor_sleep_for(0.01);
sg_vm_migrate(vm0, pm1);
sg_actor_sleep_for(0.01);
sg_vm_migrate(vm0, pm0);
sg_actor_sleep_for(5);
sg_vm_destroy(vm0);
XBT_INFO("## Test 6 (ended)");
}
int main(int argc, char* argv[])
{
/* Get the arguments */
simgrid_init(&argc, argv);
sg_vm_live_migration_plugin_init();
/* load the platform file */
simgrid_load_platform(argv[1]);
sg_actor_create("master_", sg_host_by_name("Fafard"), &master_main, 0, NULL);
simgrid_run();
XBT_INFO("Simulation time %g", simgrid_get_clock());
return 0;
}
Migrating VMs
This example shows how to migrate VMs between PMs.
View examples/cpp/cloud-migration/s4u-cloud-migration.cpp
Download s4u-cloud-migration.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include "simgrid/plugins/live_migration.h"
#include "simgrid/s4u/VirtualMachine.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_cloud_migration, "Messages specific for this example");
namespace sg4 = simgrid::s4u;
static void vm_migrate(sg4::VirtualMachine* vm, sg4::Host* dst_pm)
{
const sg4::Host* src_pm = vm->get_pm();
double mig_sta = sg4::Engine::get_clock();
sg_vm_migrate(vm, dst_pm);
double mig_end = sg4::Engine::get_clock();
XBT_INFO("%s migrated: %s->%s in %g s", vm->get_cname(), src_pm->get_cname(), dst_pm->get_cname(), mig_end - mig_sta);
}
static void vm_migrate_async(sg4::VirtualMachine* vm, sg4::Host* dst_pm)
{
sg4::Actor::create("mig_wrk", sg4::Host::current(), vm_migrate, vm, dst_pm);
}
static void master_main()
{
sg4::Host* pm0 = sg4::Host::by_name("Fafard");
sg4::Host* pm1 = sg4::Host::by_name("Tremblay");
sg4::Host* pm2 = sg4::Host::by_name("Bourassa");
auto* vm0 = pm0->create_vm("VM0", 1);
vm0->set_ramsize(1e9); // 1Gbytes
vm0->start();
XBT_INFO("Test: Migrate a VM with %zu Mbytes RAM", vm0->get_ramsize() / 1000 / 1000);
vm_migrate(vm0, pm1);
vm0->destroy();
vm0 = pm0->create_vm("VM0", 1);
vm0->set_ramsize(1e8); // 100Mbytes
vm0->start();
XBT_INFO("Test: Migrate a VM with %zu Mbytes RAM", vm0->get_ramsize() / 1000 / 1000);
vm_migrate(vm0, pm1);
vm0->destroy();
vm0 = pm0->create_vm("VM0", 1);
auto* vm1 = pm0->create_vm("VM1", 1);
vm0->set_ramsize(1e9); // 1Gbytes
vm1->set_ramsize(1e9); // 1Gbytes
vm0->start();
vm1->start();
XBT_INFO("Test: Migrate two VMs at once from PM0 to PM1");
vm_migrate_async(vm0, pm1);
vm_migrate_async(vm1, pm1);
sg4::this_actor::sleep_for(10000);
vm0->destroy();
vm1->destroy();
vm0 = pm0->create_vm("VM0", 1);
vm1 = pm0->create_vm("VM1", 1);
vm0->set_ramsize(1e9); // 1Gbytes
vm1->set_ramsize(1e9); // 1Gbytes
vm0->start();
vm1->start();
XBT_INFO("Test: Migrate two VMs at once to different PMs");
vm_migrate_async(vm0, pm1);
vm_migrate_async(vm1, pm2);
sg4::this_actor::sleep_for(10000);
vm0->destroy();
vm1->destroy();
}
int main(int argc, char* argv[])
{
/* Get the arguments */
sg4::Engine e(&argc, argv);
sg_vm_live_migration_plugin_init();
/* load the platform file */
e.load_platform(argv[1]);
sg4::Actor::create("master_", sg4::Host::by_name("Fafard"), master_main);
e.run();
XBT_INFO("Bye (simulation time %g)", sg4::Engine::get_clock());
return 0;
}
View examples/c/cloud-migration/cloud-migration.c
/* Copyright (c) 2007-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "simgrid/mailbox.h"
#include "simgrid/plugins/live_migration.h"
#include "simgrid/vm.h"
#include "xbt/log.h"
#include "xbt/str.h"
#include "xbt/sysdep.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(cloud_migration, "Messages specific for this example");
static void vm_migrate(sg_vm_t vm, sg_host_t dst_pm)
{
const_sg_host_t src_pm = sg_vm_get_pm(vm);
double mig_sta = simgrid_get_clock();
sg_vm_migrate(vm, dst_pm);
double mig_end = simgrid_get_clock();
XBT_INFO("%s migrated: %s->%s in %g s", sg_vm_get_name(vm), sg_host_get_name(src_pm), sg_host_get_name(dst_pm),
mig_end - mig_sta);
}
static void migration_worker_main(int argc, char* argv[])
{
xbt_assert(argc == 3);
const char* vm_name = argv[1];
const char* dst_pm_name = argv[2];
sg_host_t src_pm = sg_host_self();
sg_vm_t vm = sg_vm_by_name(src_pm, vm_name);
sg_host_t dst_pm = sg_host_by_name(dst_pm_name);
vm_migrate(vm, dst_pm);
}
static void vm_migrate_async(const_sg_vm_t vm, const_sg_host_t dst_pm)
{
const char* vm_name = sg_vm_get_name(vm);
const char* dst_pm_name = sg_host_get_name(dst_pm);
const char* argv[] = {"mig_work", vm_name, dst_pm_name, NULL};
sg_actor_create_("mig_wrk", sg_host_self(), &migration_worker_main, 3, argv);
}
static void master_main(int argc, char* argv[])
{
sg_host_t pm0 = sg_host_by_name("Fafard");
sg_host_t pm1 = sg_host_by_name("Tremblay");
const_sg_host_t pm2 = sg_host_by_name("Bourassa");
sg_vm_t vm0 = sg_vm_create_core(pm0, "VM0");
sg_vm_set_ramsize(vm0, 1e9); // 1Gbytes
sg_vm_start(vm0);
XBT_INFO("Test: Migrate a VM with %zu Mbytes RAM", sg_vm_get_ramsize(vm0) / 1000 / 1000);
vm_migrate(vm0, pm1);
sg_vm_destroy(vm0);
vm0 = sg_vm_create_core(pm0, "VM0");
sg_vm_set_ramsize(vm0, 1e8); // 100Mbytes
sg_vm_start(vm0);
XBT_INFO("Test: Migrate a VM with %zu Mbytes RAM", sg_vm_get_ramsize(vm0) / 1000 / 1000);
vm_migrate(vm0, pm1);
sg_vm_destroy(vm0);
vm0 = sg_vm_create_core(pm0, "VM0");
sg_vm_t vm1 = sg_vm_create_core(pm0, "VM1");
sg_vm_set_ramsize(vm0, 1e9); // 1Gbytes
sg_vm_set_ramsize(vm1, 1e9); // 1Gbytes
sg_vm_start(vm0);
sg_vm_start(vm1);
XBT_INFO("Test: Migrate two VMs at once from PM0 to PM1");
vm_migrate_async(vm0, pm1);
vm_migrate_async(vm1, pm1);
sg_actor_sleep_for(10000);
sg_vm_destroy(vm0);
sg_vm_destroy(vm1);
vm0 = sg_vm_create_core(pm0, "VM0");
vm1 = sg_vm_create_core(pm0, "VM1");
sg_vm_set_ramsize(vm0, 1e9); // 1Gbytes
sg_vm_set_ramsize(vm1, 1e9); // 1Gbytes
sg_vm_start(vm0);
sg_vm_start(vm1);
XBT_INFO("Test: Migrate two VMs at once to different PMs");
vm_migrate_async(vm0, pm1);
vm_migrate_async(vm1, pm2);
sg_actor_sleep_for(10000);
sg_vm_destroy(vm0);
sg_vm_destroy(vm1);
}
int main(int argc, char* argv[])
{
/* Get the arguments */
simgrid_init(&argc, argv);
sg_vm_live_migration_plugin_init();
/* load the platform file */
simgrid_load_platform(argv[1]);
sg_actor_create("master_", sg_host_by_name("Fafard"), &master_main, 0, NULL);
simgrid_run();
XBT_INFO("Bye (simulation time %g)", simgrid_get_clock());
return 0;
}
Model-Related Examples
ns-3 as a model
This simple ping-pong example demonstrates how to use the bindings to the Network Simulator. The most interesting is probably not the C++ files since they are unchanged from the other simulations, but the associated files, such as the platform file to see how to declare a platform to be used with the ns-3 bindings of SimGrid and the tesh file to see how to start a simulation in these settings.
View examples/cpp/network-ns3/s4u-network-ns3.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <string>
#include <unordered_map>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
struct MasterWorkerNames {
std::string master;
std::string worker;
};
using MasterWorkerNamesMap = std::unordered_map<int, MasterWorkerNames>;
struct Payload {
double msg_size;
double start_time;
};
static void master(MasterWorkerNamesMap& names, const std::vector<std::string>& args)
{
xbt_assert(args.size() == 4, "Strange number of arguments expected 3 got %zu", args.size() - 1);
XBT_DEBUG("Master started");
/* data size */
double msg_size = std::stod(args[1]);
int id = std::stoi(args[3]); // unique id to control statistics
/* master and worker names */
names.try_emplace(id, MasterWorkerNames{sg4::Host::current()->get_name(), args[2]});
sg4::Mailbox* mbox = sg4::Mailbox::by_name(args[3]);
auto* payload = new Payload{msg_size, sg4::Engine::get_clock()};
mbox->put(payload, static_cast<uint64_t>(msg_size));
XBT_DEBUG("Finished");
}
static void worker(const MasterWorkerNamesMap& names, const std::vector<std::string>& args)
{
xbt_assert(args.size() == 2, "Strange number of arguments expected 1 got %zu", args.size() - 1);
int id = std::stoi(args[1]);
sg4::Mailbox* mbox = sg4::Mailbox::by_name(args[1]);
XBT_DEBUG("Worker started");
auto payload = mbox->get_unique<Payload>();
double elapsed_time = sg4::Engine::get_clock() - payload->start_time;
XBT_INFO("FLOW[%d] : Receive %.0f bytes from %s to %s", id, payload->msg_size, names.at(id).master.c_str(),
names.at(id).worker.c_str());
XBT_DEBUG("FLOW[%d] : transferred in %f seconds", id, elapsed_time);
XBT_DEBUG("Finished");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 2,
"Usage: %s platform_file deployment_file\n"
"\tExample: %s platform.xml deployment.xml\n",
argv[0], argv[0]);
e.load_platform(argv[1]);
MasterWorkerNamesMap master_worker_names;
e.register_function("master", [&master_worker_names](auto args) { master(master_worker_names, args); });
e.register_function("worker", [&master_worker_names](auto args) { worker(master_worker_names, args); });
e.load_deployment(argv[2]);
e.run();
return 0;
}
Platform files:
View examples/platforms/small_platform_one_link_routes.xml
Download small_platform_one_link_routes.xml
<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
<!-- All routes of this platform are of length 1, so you can use it with the ns-3 model.
https://simgrid.org/doc/latest/ns3.html -->
<platform version="4.1">
<zone id="AS0" routing="Full">
<host id="Tremblay" speed="98.095Mf"/>
<host id="Jupiter" speed="76.296Mf"/>
<host id="Fafard" speed="76.296Mf"/>
<host id="Ginette" speed="48.492Mf"/>
<host id="Bourassa" speed="48.492Mf"/>
<host id="Lovelace" speed="30.343Mf"/>
<link id="0" bandwidth="41.279125MBps" latency="59.904us"/>
<link id="1" bandwidth="34.285625MBps" latency="514.433us"/>
<link id="2" bandwidth="118.6825MBps" latency="136.931us"/>
<link id="3" bandwidth="34.285625MBps" latency="514.433us"/>
<link id="4" bandwidth="10.099625MBps" latency="479.78us"/>
<link id="5" bandwidth="27.94625MBps" latency="278.066us"/>
<link id="6" bandwidth="41.279125MBps" latency="59.904us"/>
<link id="7" bandwidth="11.618875MBps" latency="189.98us"/>
<link id="8" bandwidth="8.158MBps" latency="270.544us"/>
<link id="9" bandwidth="7.20975MBps" latency="1.461517ms"/>
<link id="10" bandwidth="4.67975MBps" latency="848.712us"/>
<link id="11" bandwidth="252.75kBps" latency="5.70455ms"/>
<link id="12" bandwidth="1.792625MBps" latency="7.877863ms"/>
<link id="13" bandwidth="1.792625MBps" latency="7.877863ms"/>
<link id="14" bandwidth="1.792625MBps" latency="7.877863ms"/>
<route src="Tremblay" dst="Jupiter">
<link_ctn id="0"/>
</route>
<route src="Tremblay" dst="Fafard">
<link_ctn id="1"/>
</route>
<route src="Tremblay" dst="Ginette">
<link_ctn id="2"/>
</route>
<route src="Tremblay" dst="Bourassa">
<link_ctn id="3"/>
</route>
<route src="Tremblay" dst="Lovelace">
<link_ctn id="4"/>
</route>
<route src="Jupiter" dst="Fafard">
<link_ctn id="5"/>
</route>
<route src="Jupiter" dst="Ginette">
<link_ctn id="6"/>
</route>
<route src="Jupiter" dst="Bourassa">
<link_ctn id="7"/>
</route>
<route src="Jupiter" dst="Lovelace">
<link_ctn id="8"/>
</route>
<route src="Fafard" dst="Ginette">
<link_ctn id="9"/>
</route>
<route src="Fafard" dst="Bourassa">
<link_ctn id="10"/>
</route>
<route src="Fafard" dst="Lovelace">
<link_ctn id="11"/>
</route>
<route src="Ginette" dst="Bourassa">
<link_ctn id="12"/>
</route>
<route src="Ginette" dst="Lovelace">
<link_ctn id="13"/>
</route>
<route src="Bourassa" dst="Lovelace">
<link_ctn id="14"/>
</route>
</zone>
</platform>
WiFi links
This demonstrates how to declare a wifi zone in your platform and how to use it in your simulation. For that, you should have a link whose sharing policy is set to WIFI. Such links can have more than one bandwidth value (separated by commas), corresponding to the several SNR level of your wifi link.
In this case, SimGrid automatically switches to validated performance models of wifi networks, where the time is shared between users instead of the bandwidth for wired links (the corresponding publication is currently being written).
If your wifi link provides more than one SNR level, you can switch
the level of a given host using
simgrid::s4u::Link::set_host_wifi_rate()
. By default,
the first level is used.
View examples/cpp/network-wifi/s4u-network-wifi.cpp
/* Copyright (c) 2017-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
/* This example demonstrates how to use wifi links in SimGrid. Most of the interesting things happen in the
* corresponding XML file: examples/platforms/wifi.xml
*/
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_network_wifi, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void sender(sg4::Mailbox* mailbox, int data_size)
{
XBT_INFO("Send a message to the other station.");
static std::string message = "message";
mailbox->put(&message, data_size);
XBT_INFO("Done.");
}
static void receiver(sg4::Mailbox* mailbox)
{
XBT_INFO("Wait for a message.");
mailbox->get<std::string>();
XBT_INFO("Done.");
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform_file\n\tExample: %s platform.xml deployment.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
/* Exchange a message between the 2 stations */
auto* mailbox = sg4::Mailbox::by_name("mailbox");
auto* station1 = e.host_by_name("Station 1");
auto* station2 = e.host_by_name("Station 2");
sg4::Actor::create("sender", station1, sender, mailbox, 1e7);
sg4::Actor::create("receiver", station2, receiver, mailbox);
/* Declare that the stations are not at the same distance from their AP */
const auto* ap = e.link_by_name("AP1");
ap->set_host_wifi_rate(station1, 1); // The host "Station 1" uses the second level of bandwidths on that AP
ap->set_host_wifi_rate(station2, 0); // This is perfectly useless as level 0 is used by default
e.run();
return 0;
}
Platform files:
View examples/platforms/wifi.xml
<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
<platform version="4.1">
<zone id="world" routing="Full">
<zone id="WIFI zone" routing="Wifi">
<prop id="access_point" value="WIFI router" />
<!-- Declare the stations of this wifi zone -->
<host id="Station 1" speed="100.0Mf,50.0Mf,20.0Mf" />
<host id="Station 2" speed="100.0Mf,50.0Mf,20.0Mf" />
<!-- Declare the wifi media (after hosts because our parser is sometimes annoying) -->
<link id="AP1" sharing_policy="WIFI" bandwidth="54Mbps,36Mbps,24Mbps" latency="0ms" />
<router id="WIFI router"/>
</zone>
<!-- NODE1 AS -->
<zone id="Wired zone" routing="Full">
<host id="node1" speed="100.0Mf,50.0Mf,20.0Mf" />
</zone>
<!-- AS Routing -->
<link id="Collector" sharing_policy="SHARED" bandwidth="100Mbps" latency="0ms" />
<zoneRoute src="WIFI zone" dst="Wired zone" gw_src="WIFI router" gw_dst="node1">
<link_ctn id="Collector" />
</zoneRoute>
</zone>
</platform>
You can also use the ns-3 models on your wifi networks as follows:
View examples/cpp/network-ns3-wifi/s4u-network-ns3-wifi.cpp
Download s4u-network-ns3-wifi.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/s4u.hpp"
#include <iostream>
#include <iomanip>
XBT_LOG_NEW_DEFAULT_CATEGORY(ns3_wifi_example, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
class Message
{
public:
std::string sender;
int size;
Message(const std::string& sender_, int size_) : sender(sender_), size(size_) {}
};
static void sender(const std::string& mailbox, int msg_size, unsigned sleep_time)
{
sg4::this_actor::sleep_for(sleep_time);
auto* mbox = sg4::Mailbox::by_name(mailbox);
auto* msg = new Message(sg4::this_actor::get_host()->get_name(), msg_size);
mbox->put(msg, msg_size);
}
static void receiver(const std::string& mailbox)
{
auto* mbox = sg4::Mailbox::by_name(mailbox);
auto msg = mbox->get_unique<Message>();
XBT_INFO("[%s] %s received %d bytes from %s", mailbox.c_str(), sg4::this_actor::get_host()->get_name().c_str(),
msg->size, msg->sender.c_str());
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
e.load_platform(argv[1]);
int msg_size = 1e5;
/* Communication between STA in the same wifi zone */
sg4::Actor::create("sender", e.host_by_name("STA0-0"), sender, "1", msg_size, 10);
sg4::Actor::create("receiver", e.host_by_name("STA0-1"), receiver, "1");
sg4::Actor::create("sender", e.host_by_name("STA0-1"), sender, "2", msg_size, 20);
sg4::Actor::create("receiver", e.host_by_name("STA0-0"), receiver, "2");
sg4::Actor::create("sender", e.host_by_name("STA1-1"), sender, "3", msg_size, 30);
sg4::Actor::create("receiver", e.host_by_name("STA1-2"), receiver, "3");
sg4::Actor::create("sender", e.host_by_name("STA1-2"), sender, "4", msg_size, 40);
sg4::Actor::create("receiver", e.host_by_name("STA1-1"), receiver, "4");
/* Communication between STA of different wifi zones */
sg4::Actor::create("sender", e.host_by_name("STA0-0"), sender, "5", msg_size, 50);
sg4::Actor::create("receiver", e.host_by_name("STA1-0"), receiver, "5");
sg4::Actor::create("sender", e.host_by_name("STA1-0"), sender, "6", msg_size, 60);
sg4::Actor::create("receiver", e.host_by_name("STA0-0"), receiver, "6");
sg4::Actor::create("sender", e.host_by_name("STA0-1"), sender, "7", msg_size, 70);
sg4::Actor::create("receiver", e.host_by_name("STA1-2"), receiver, "7");
sg4::Actor::create("sender", e.host_by_name("STA1-2"), sender, "8", msg_size, 80);
sg4::Actor::create("receiver", e.host_by_name("STA0-1"), receiver, "8");
e.run();
return 0;
}
Platform files:
View examples/platforms/wifi_ns3.xml
<?xml version='1.0'?><!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
<platform version="4.1">
<config>
<prop id = "network/model" value = "ns-3" />
</config>
<zone id="world" routing="Floyd">
<zone id="SSID_1" routing="WIFI">
<prop id="access_point" value="alice"/>
<prop id="mcs" value="2"/> <!-- Optionnal: default = 3 -->
<prop id="nss" value="1"/> <!-- Optionnal: default = 1 -->
<host id="alice" speed="1Gf"/>
<host id="STA0-0" speed="1Gf">
<prop id="wifi_distance" value="37"/> <!-- Optionnal: default = 10 -->
</host>
<host id="STA0-1" speed="1Gf"/>
</zone>
<zone id="SSID_2" routing="WIFI">
<prop id="access_point" value="bob"/>
<router id="bob"/>
<host id="STA1-0" speed="1Gf"/>
<host id="STA1-1" speed="1Gf"/>
<host id="STA1-2" speed="1Gf"/>
</zone>
<link id="wireline" bandwidth="100Mbps" latency="2ms" sharing_policy="SHARED"/>
<zoneRoute src="SSID_1" dst="SSID_2" gw_src="alice" gw_dst="bob">
<link_ctn id="wireline"/>
</zoneRoute>
</zone>
</platform>
Plugin Examples
It is possible to extend SimGrid without modifying its internals by attaching code to the existing signals and by adding extra data to the simulation objects through extensions. How to do that is not exactly documented yet, and you should look for examples in the src/plugins directory.
This section documents how the existing plugins can be used. Remember that you are very welcome to modify the plugins to fit your needs. It should be much easier than modifying the SimGrid kernel.
Monitoring the host load
View examples/cpp/plugin-host-load/s4u-plugin-host-load.cpp
Download s4u-plugin-host-load.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/plugins/load.h"
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void execute_load_test()
{
s4u_Host* host = sg4::Host::by_name("MyHost1");
XBT_INFO("Initial peak speed: %.0E flop/s; number of flops computed so far: %.0E (should be 0) and current average "
"load: %.5f (should be 0)",
host->get_speed(), sg_host_get_computed_flops(host), sg_host_get_avg_load(host));
double start = sg4::Engine::get_clock();
XBT_INFO("Sleep for 10 seconds");
sg4::this_actor::sleep_for(10);
double speed = host->get_speed();
XBT_INFO("Done sleeping %.2fs; peak speed: %.0E flop/s; number of flops computed so far: %.0E (nothing should have "
"changed)",
sg4::Engine::get_clock() - start, host->get_speed(), sg_host_get_computed_flops(host));
// Run an activity
start = sg4::Engine::get_clock();
XBT_INFO("Run an activity of %.0E flops at current speed of %.0E flop/s", 200E6, host->get_speed());
sg4::this_actor::execute(200E6);
XBT_INFO(
"Done working on my activity; this took %.2fs; current peak speed: %.0E flop/s (when I started the computation, "
"the speed was set to %.0E flop/s); number of flops computed so "
"far: %.2E, average load as reported by the HostLoad plugin: %.5f (should be %.5f)",
sg4::Engine::get_clock() - start, host->get_speed(), speed, sg_host_get_computed_flops(host),
sg_host_get_avg_load(host),
200E6 / (10.5 * speed * host->get_core_count() +
(sg4::Engine::get_clock() - start - 0.5) * host->get_speed() * host->get_core_count()));
// ========= Change power peak =========
int pstate = 1;
host->set_pstate(pstate);
XBT_INFO(
"========= Requesting pstate %d (speed should be of %.0E flop/s and is of %.0E flop/s, average load is %.5f)",
pstate, host->get_pstate_speed(pstate), host->get_speed(), sg_host_get_avg_load(host));
// Run a second activity
start = sg4::Engine::get_clock();
XBT_INFO("Run an activity of %.0E flops", 100E6);
sg4::this_actor::execute(100E6);
XBT_INFO("Done working on my activity; this took %.2fs; current peak speed: %.0E flop/s; number of flops computed so "
"far: %.2E",
sg4::Engine::get_clock() - start, host->get_speed(), sg_host_get_computed_flops(host));
start = sg4::Engine::get_clock();
XBT_INFO("========= Requesting a reset of the computation and load counters");
sg_host_load_reset(host);
XBT_INFO("After reset: %.0E flops computed; load is %.5f", sg_host_get_computed_flops(host),
sg_host_get_avg_load(host));
XBT_INFO("Sleep for 4 seconds");
sg4::this_actor::sleep_for(4);
XBT_INFO("Done sleeping %.2f s; peak speed: %.0E flop/s; number of flops computed so far: %.0E",
sg4::Engine::get_clock() - start, host->get_speed(), sg_host_get_computed_flops(host));
// =========== Turn the other host off ==========
s4u_Host* host2 = sg4::Host::by_name("MyHost2");
XBT_INFO("Turning MyHost2 off, and sleeping another 10 seconds. MyHost2 computed %.0f flops so far and has an "
"average load of %.5f.",
sg_host_get_computed_flops(host2), sg_host_get_avg_load(host2));
host2->turn_off();
start = sg4::Engine::get_clock();
sg4::this_actor::sleep_for(10);
XBT_INFO("Done sleeping %.2f s; peak speed: %.0E flop/s; number of flops computed so far: %.0E",
sg4::Engine::get_clock() - start, host->get_speed(), sg_host_get_computed_flops(host));
}
static void change_speed()
{
s4u_Host* host = sg4::Host::by_name("MyHost1");
sg4::this_actor::sleep_for(10.5);
XBT_INFO("I slept until now, but now I'll change the speed of this host "
"while the other actor is still computing! This should slow the computation down.");
host->set_pstate(2);
}
int main(int argc, char* argv[])
{
sg_host_load_plugin_init();
sg4::Engine e(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s ../platforms/energy_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
sg4::Actor::create("load_test", e.host_by_name("MyHost1"), execute_load_test);
sg4::Actor::create("change_speed", e.host_by_name("MyHost1"), change_speed);
e.run();
XBT_INFO("Total simulation time: %.2f", sg4::Engine::get_clock());
return 0;
}
View examples/c/plugin-host-load/plugin-host-load.c
/* Copyright (c) 2007-2024. The SimGrid Team.
* All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/actor.h"
#include "simgrid/engine.h"
#include "simgrid/host.h"
#include "simgrid/plugins/load.h"
#include "xbt/asserts.h"
#include "xbt/log.h"
XBT_LOG_NEW_DEFAULT_CATEGORY(hostload_test, "Messages specific for this example");
static void execute_load_test(int argc, char* argv[])
{
sg_host_t host = sg_host_by_name("MyHost1");
XBT_INFO("Initial peak speed: %.0E flop/s; number of flops computed so far: %.0E (should be 0) and current average "
"load: %.5f (should be 0)",
sg_host_get_speed(host), sg_host_get_computed_flops(host), sg_host_get_avg_load(host));
double start = simgrid_get_clock();
XBT_INFO("Sleep for 10 seconds");
sg_actor_sleep_for(10);
double speed = sg_host_get_speed(host);
XBT_INFO("Done sleeping %.2fs; peak speed: %.0E flop/s; number of flops computed so far: %.0E (nothing should have "
"changed)",
simgrid_get_clock() - start, sg_host_get_speed(host), sg_host_get_computed_flops(host));
// Run a task
start = simgrid_get_clock();
XBT_INFO("Run a task of %.0E flops at current speed of %.0E flop/s", 200e6, sg_host_get_speed(host));
sg_actor_execute(200e6);
XBT_INFO("Done working on my task; this took %.2fs; current peak speed: %.0E flop/s (when I started the computation, "
"the speed was set to %.0E flop/s); number of flops computed so "
"far: %.2E, average load as reported by the HostLoad plugin: %.5f (should be %.5f)",
simgrid_get_clock() - start, sg_host_get_speed(host), speed, sg_host_get_computed_flops(host),
sg_host_get_avg_load(host),
200E6 / (10.5 * speed * sg_host_core_count(host) +
(simgrid_get_clock() - start - 0.5) * sg_host_get_speed(host) * sg_host_core_count(host)));
// ========= Change power peak =========
int pstate = 1;
sg_host_set_pstate(host, pstate);
XBT_INFO(
"========= Requesting pstate %d (speed should be of %.0E flop/s and is of %.0E flop/s, average load is %.5f)",
pstate, sg_host_get_pstate_speed(host, pstate), sg_host_get_speed(host), sg_host_get_avg_load(host));
// Run a second task
start = simgrid_get_clock();
XBT_INFO("Run a task of %.0E flops", 100e6);
sg_actor_execute(100e6);
XBT_INFO("Done working on my task; this took %.2fs; current peak speed: %.0E flop/s; number of flops computed so "
"far: %.2E",
simgrid_get_clock() - start, sg_host_get_speed(host), sg_host_get_computed_flops(host));
start = simgrid_get_clock();
XBT_INFO("========= Requesting a reset of the computation and load counters");
sg_host_load_reset(host);
XBT_INFO("After reset: %.0E flops computed; load is %.5f", sg_host_get_computed_flops(host),
sg_host_get_avg_load(host));
XBT_INFO("Sleep for 4 seconds");
sg_actor_sleep_for(4);
XBT_INFO("Done sleeping %.2f s; peak speed: %.0E flop/s; number of flops computed so far: %.0E",
simgrid_get_clock() - start, sg_host_get_speed(host), sg_host_get_computed_flops(host));
// =========== Turn the other host off ==========
XBT_INFO("Turning MyHost2 off, and sleeping another 10 seconds. MyHost2 computed %.0f flops so far and has an "
"average load of %.5f.",
sg_host_get_computed_flops(sg_host_by_name("MyHost2")), sg_host_get_avg_load(sg_host_by_name("MyHost2")));
sg_host_turn_off(sg_host_by_name("MyHost2"));
start = simgrid_get_clock();
sg_actor_sleep_for(10);
XBT_INFO("Done sleeping %.2f s; peak speed: %.0E flop/s; number of flops computed so far: %.0E",
simgrid_get_clock() - start, sg_host_get_speed(host), sg_host_get_computed_flops(host));
}
static void change_speed(int argc, char* argv[])
{
sg_host_t host = sg_host_by_name("MyHost1");
sg_actor_sleep_for(10.5);
XBT_INFO("I slept until now, but now I'll change the speed of this host "
"while the other actor is still computing! This should slow the computation down.");
sg_host_set_pstate(host, 2);
}
int main(int argc, char* argv[])
{
sg_host_load_plugin_init();
simgrid_init(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s platform.xml\n", argv[0], argv[0]);
simgrid_load_platform(argv[1]);
sg_actor_create("load_test", sg_host_by_name("MyHost1"), &execute_load_test, 0, NULL);
sg_actor_create("change_speed", sg_host_by_name("MyHost1"), &change_speed, 0, NULL);
simgrid_run();
XBT_INFO("Total simulation time: %.2f", simgrid_get_clock());
return 0;
}
Monitoring the link load
View examples/cpp/plugin-link-load/s4u-plugin-link-load.cpp
Download s4u-plugin-link-load.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/plugins/load.h"
#include "simgrid/s4u.hpp"
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
static void sender(const std::string& mailbox, uint64_t msg_size)
{
auto* mbox = sg4::Mailbox::by_name(mailbox);
static int payload = 42;
mbox->put(&payload, msg_size);
}
static void receiver(const std::string& mailbox)
{
auto* mbox = sg4::Mailbox::by_name(mailbox);
mbox->get<int>();
}
static void run_transfer(sg4::Host* src_host, sg4::Host* dst_host, const std::string& mailbox, unsigned long msg_size)
{
XBT_INFO("Launching the transfer of %lu bytes", msg_size);
sg4::Actor::create("sender", src_host, sender, mailbox, msg_size);
sg4::Actor::create("receiver", dst_host, receiver, mailbox);
}
static void execute_load_test()
{
auto* host0 = sg4::Host::by_name("node-0.simgrid.org");
auto* host1 = sg4::Host::by_name("node-1.simgrid.org");
sg4::this_actor::sleep_for(1);
run_transfer(host0, host1, "1", 1000 * 1000 * 1000);
sg4::this_actor::sleep_for(10);
run_transfer(host0, host1, "2", 1000 * 1000 * 1000);
sg4::this_actor::sleep_for(3);
run_transfer(host0, host1, "3", 1000 * 1000 * 1000);
}
static void show_link_load(const std::string& link_name, const sg4::Link* link)
{
XBT_INFO("%s link load (cum, avg, min, max): (%g, %g, %g, %g)", link_name.c_str(), sg_link_get_cum_load(link),
sg_link_get_avg_load(link), sg_link_get_min_instantaneous_load(link),
sg_link_get_max_instantaneous_load(link));
}
static void monitor()
{
const auto* link_backbone = sg4::Link::by_name("cluster0_backbone");
const auto* link_host0 = sg4::Link::by_name("cluster0_link_0_UP");
const auto* link_host1 = sg4::Link::by_name("cluster0_link_1_DOWN");
XBT_INFO("Tracking desired links");
sg_link_load_track(link_backbone);
sg_link_load_track(link_host0);
sg_link_load_track(link_host1);
show_link_load("Backbone", link_backbone);
while (sg4::Engine::get_clock() < 5) {
sg4::this_actor::sleep_for(1);
show_link_load("Backbone", link_backbone);
}
XBT_INFO("Untracking the backbone link");
sg_link_load_untrack(link_backbone);
show_link_load("Host0_UP", link_host0);
show_link_load("Host1_UP", link_host1);
XBT_INFO("Now resetting and probing host links each second.");
while (sg4::Engine::get_clock() < 29) {
sg_link_load_reset(link_host0);
sg_link_load_reset(link_host1);
sg4::this_actor::sleep_for(1);
show_link_load("Host0_UP", link_host0);
show_link_load("Host1_UP", link_host1);
}
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
sg_link_load_plugin_init();
xbt_assert(argc == 2, "Usage: %s platform_file\n\tExample: %s ../platforms/energy_platform.xml\n", argv[0], argv[0]);
e.load_platform(argv[1]);
sg4::Actor::create("load_test", e.host_by_name("node-42.simgrid.org"), execute_load_test);
sg4::Actor::create("monitor", e.host_by_name("node-51.simgrid.org"), monitor);
e.run();
XBT_INFO("Total simulation time: %.2f", sg4::Engine::get_clock());
return 0;
}
Model-Checking examples
The model-checker can be used to exhaustively search for issues in the tested application. It must be activated at compile-time, but this mode is rather experimental in SimGrid (as of v3.25). We are working on it :)
Failing assert
In this example, two actors send some data to a central server, which asserts that the messages are always received in the same order. This is wrong, and the model-checker correctly finds a counter-example to that assertion.
View examples/cpp/mc-failing-assert/s4u-mc-failing-assert.cpp
Download s4u-mc-failing-assert.cpp
/* Copyright (c) 2010-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/******************** Non-deterministic message ordering *********************/
/* Server assumes a fixed order in the reception of messages from its clients */
/* which is incorrect because the message ordering is non-deterministic */
/******************************************************************************/
#include <simgrid/modelchecker.h>
#include <simgrid/s4u.hpp>
XBT_LOG_NEW_DEFAULT_CATEGORY(mc_assert_example, "Logging channel used in this example");
namespace sg4 = simgrid::s4u;
static int server(int worker_amount)
{
int value_got = -1;
sg4::Mailbox* mb = sg4::Mailbox::by_name("server");
for (int count = 0; count < worker_amount; count++) {
auto msg = mb->get_unique<int>();
value_got = *msg;
}
/*
* We assert here that the last message we got (which overwrite any previously received message) is the one from the
* last worker This will obviously fail when the messages are received out of order.
*/
MC_assert(value_got == 2);
XBT_INFO("OK");
return 0;
}
static int client(int rank)
{
/* I just send my rank onto the mailbox. It must be passed as a stable memory block (thus the new) so that that
* memory survives even after the end of the client */
sg4::Mailbox* mailbox = sg4::Mailbox::by_name("server");
mailbox->put(new int(rank), 1 /* communication cost is not really relevant in MC mode */);
XBT_INFO("Sent!");
return 0;
}
int main(int argc, char* argv[])
{
sg4::Engine e(&argc, argv);
xbt_assert(argc > 1, "Usage: %s platform_file\n", argv[0]);
e.load_platform(argv[1]);
auto hosts = e.get_all_hosts();
xbt_assert(hosts.size() >= 3, "This example requires at least 3 hosts");
sg4::Actor::create("server", hosts[0], &server, 2);
sg4::Actor::create("client1", hosts[1], &client, 1);
sg4::Actor::create("client2", hosts[2], &client, 2);
e.run();
return 0;
}
Advanced examples
Changing maestro’s thread
Usually, SimGrid’s maestro executes in the main thread of your application, meaning that the main thread is in charge of
initializing the simulation and then scheduling the activities. If you really need it, it is possible to move away maestro in
another system thread, for example because another library absolutely wants to run as the system main thread. The following
example shows how to do that, using sg_actor_attach()
at the begining and sg_actor_detach()
on
termination.
View examples/cpp/maestro-set/s4u-maestro-set.cpp
/* Copyright (c) 2007-2024. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/** Switch the system thread hosting our maestro.
*
* That's a very advanced example in which we move the maestro context to another system thread.
* Not many users need it (maybe only one, actually), but this example is also a regression test.
*
* This example is in C++ because we use C++11 threads to ensure that the feature is working as
* expected. You can still use that feature from a C code.
*/
#include "simgrid/Exception.hpp"
#include "simgrid/actor.h"
#include "simgrid/s4u.hpp"
#include <string>
#include <thread>
XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
namespace sg4 = simgrid::s4u;
const std::thread::id root_id = std::this_thread::get_id();
static void ensure_root_tid()
{
std::thread::id this_id = std::this_thread::get_id();
xbt_assert(root_id == this_id, "I was supposed to be the main thread");
XBT_INFO("I am the main thread, as expected");
}
static void ensure_other_tid()
{
std::thread::id this_id = std::this_thread::get_id();
xbt_assert(this_id != root_id, "I was NOT supposed to be the main thread");
XBT_INFO("I am not the main thread, as expected");
}
static void sender()
{
ensure_root_tid();
auto* payload = new std::string("some message");
sg4::Mailbox::by_name("some mailbox")->put(payload, 10e8);
}
static void receiver()
{
ensure_other_tid();
sg4::Mailbox::by_name("some mailbox")->get_unique<std::string>();
XBT_INFO("Task received");
}
static void maestro(void* /* data */)
{
ensure_other_tid();
sg4::Actor::create("receiver", sg4::Host::by_name("Jupiter"), receiver);
sg4::Engine::get_instance()->run();
}
/** Main function */
int main(int argc, char* argv[])
{
/* Specify which code should be executed by maestro on another thread, once this current thread is affected to an
* actor by the subsequent sg_actor_attach(). This must be done before the creation of the engine. */
simgrid_set_maestro(maestro, nullptr);
sg4::Engine e(&argc, argv);
xbt_assert(argc == 2, "Usage: %s platform_file\n"
"example: %s ../platforms/small_platform.xml\n",
argv[0], argv[0]);
e.load_platform(argv[1]);
/* Become one of the simulated actors (must be done after the platform creation, or the host won't exist). */
sg_actor_attach("sender", nullptr, e.host_by_name("Tremblay"), nullptr);
ensure_root_tid(); // Only useful in this test: we ensure that SimGrid is not broken and that this code is executed in
// the correct system thread
// Execute the sender code. The root thread was actually turned into a regular actor
sender();
sg_actor_detach(); // The root thread becomes maestro again (as proved by the output)
XBT_INFO("Detached");
ensure_root_tid();
return 0;
}