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:

View examples/cpp/actor-create/s4u-actor-create.cpp

Download s4u-actor-create.cpp

/* Copyright (c) 2006-2023. 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;
}

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:

View examples/cpp/actor-exiting/s4u-actor-exiting.cpp

Download s4u-actor-exiting.cpp

/* Copyright (c) 2017-2023. 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;
}

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

Download s4u-actor-kill.cpp

/* Copyright (c) 2017-2023. 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;
}

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-2023. 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;
}

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

Download s4u-actor-daemon.cpp

/* Copyright (c) 2017-2023. 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;
}

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-2023. 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;
}

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-2023. 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;
}

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-2023. 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;
}

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

Download s4u-actor-join.cpp

/* Copyright (c) 2017-2023. 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;
}

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

Download s4u-actor-yield.cpp

/* Copyright (c) 2017-2023. 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;
}

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

Download s4u-replay-comm.cpp

/* Copyright (c) 2009-2023. 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

Download s4u-replay-io.cpp

/* Copyright (c) 2017-2023. 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-2023. 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;
}

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

Download s4u-comm-wait.cpp

/* Copyright (c) 2010-2023. 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;
}

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-2023. 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;
}

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

Download s4u-comm-ready.cpp

/* Copyright (c) 2010-2023. 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;
}

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

Download s4u-comm-suspend.cpp

/* Copyright (c) 2010-2023. 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;
}

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

Download s4u-comm-failure.cpp

/* Copyright (c) 2021-2023. 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");
  auto* link2 = zone->create_link("linkto2", "1bps")->seal();
  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;
}

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-2023. 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;
}

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

Download s4u-exec-basic.cpp

/* Copyright (c) 2010-2023. 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;
}

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

Download s4u-exec-async.cpp

/* Copyright (c) 2007-2023. 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;
}

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

Download s4u-exec-remote.cpp

/* Copyright (c) 2007-2023. 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;
}

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

Download s4u-exec-ptask.cpp

/* Copyright (c) 2017-2023. 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;
}

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-2023. 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;
}

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

Download s4u-exec-failure.cpp

/* Copyright (c) 2021-2023. 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

Download s4u-exec-dvfs.cpp

/* Copyright (c) 2007-2023. 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;
}

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

Download s4u-io-disk-raw.cpp

/* Copyright (c) 2017-2023. 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;
}

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

Download s4u-io-async.cpp

/* Copyright (c) 2007-2023. 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-2023. 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;
    }
    
  • 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-2023. 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;
    }