The S4U Interface



The S4U interface (SimGrid for you) mixes the full power of SimGrid with the full power of C++. This is the preferred interface to describe abstract algorithms in the domains of Cloud, P2P, HPC, IoT, and similar settings.

Since v3.20 (June 2018), S4U is the way to go for long-term projects. It is feature complete, but may still evolve slightly in future releases. It can already be used to do everything that can be done in SimGrid, but you may have to adapt your code in future releases. When this happens, compiling your code will produce deprecation warnings for 4 releases (one year) before the removal of the old symbols. If you want an API that will never ever evolve in the future, you should use the deprecated MSG API instead.

Main Concepts

A typical SimGrid simulation is composed of several Actors, that execute user-provided functions. The actors have to explicitly use the S4U interface to express their computation, communication, disk usage, and other Activities, so that they get reflected within the simulator. These activities take place on resources such as Hosts, Links, and Disks. SimGrid predicts the time taken by each activity and orchestrates the actors accordingly, waiting for the completion of these activities.

When communicating, data is not directly sent to other actors but posted onto a Mailbox that serves as a rendezvous point between communicating actors. This means that you don’t need to know who you are talking to, you just put your communication Put request in a mailbox, and it will be matched with a complementary Get request. Alternatively, actors can interact through classical synchronization mechanisms such as Barrier, Semaphore, Mutex, and ConditionVariable.

Each actor is located on a simulated Host. Each host is located in a NetZone, that knows the networking path between one resource to another. Each NetZone is included in another one, forming a tree of NetZones which root zone contains the whole platform. The actors can also be located on a VirtualMachines that may restrict the activities it contains to a limited amount of cores. Virtual machines can also be migrated between hosts.

The simgrid::s4u::this_actor namespace provides many helper functions to simplify the code of actors.

Activities

Activities represent the actions that consume a resource, such as a Comm that consumes the transmitting power of Link resources, or an Exec that consumes the computing power of Host resources. See also the full API below.

Asynchronous Activities

Every activity can be either blocking or asynchronous. For example, s4u::Mailbox::put() and s4u::Mailbox::get() create blocking communications: the actor is blocked until the completion of that communication. Asynchronous communications do not block the actor during their execution but progress on their own.

Once your asynchronous activity is started, you can test for its completion using s4u::Activity::test(). This function returns true if the activity is completed already. You can also use s4u::Activity::wait() to block until the completion of the activity. To wait for at most a given amount of time, use s4u::Activity::wait_for(). Finally, to wait at most until a specified time limit, use s4u::Activity::wait_until().

Every kind of activity can be asynchronous:

The following example shows how to have several concurrent communications ongoing. First, you have to declare a vector in which we will store the ongoing communications. It is also useful to have a vector of mailboxes.

/* Vector in which we store all ongoing communications */
std::vector<sg4::CommPtr> pending_comms;

/* Make a vector of the mailboxes to use */
std::vector<sg4::Mailbox*> mboxes;
for (int i = 0; i < receivers_count; i++)
  mboxes.push_back(sg4::Mailbox::by_name(std::string("receiver-") + std::to_string(i)));

Then, you start all the communications that should occur concurrently with s4u::Mailbox::put_async(). Finally, the actor waits for the completion of all of them at once with s4u::Comm::wait_all().

for (int i = 0; i < receivers_count; i++) {
  XBT_INFO("Send 'finalize' to 'receiver-%d'", i);
  sg4::CommPtr comm = mboxes[i]->put_async(new std::string("finalize"), 0);
  pending_comms.push_back(comm);
}
XBT_INFO("Done dispatching all messages");

/* Now that all message exchanges were initiated, wait for their completion in one single call */
sg4::Comm::wait_all(pending_comms);

Activities Life cycle

Sometimes, you want to change the setting of an activity before it even starts.

Todo

write this section

Mailboxes

Please also refer to the API reference for s4u::Mailbox.

What are Mailboxes?

Mailboxes are rendezvous points for network communications, similar to URLs on which you could post and retrieve data. Actually, the mailboxes are not involved in the communication once it starts, but only to find the contact with which you want to communicate.

They are similar to many common things: The phone number, which allows the caller to find the receiver. The Twitter hashtag, which helps senders and receivers to find each other. In TCP, the pair {host name, host port} to which you can connect to find your peer. In HTTP, URLs through which the clients can connect to the servers. In ZeroMQ, the queues are used to match senders and receivers.

One big difference with most of these systems is that no actor is the exclusive owner of a mailbox, neither in sending nor in receiving. Many actors can send into and/or receive from the same mailbox. TCP socket ports for example are shared on the sender side but exclusive on the receiver side (only one process can receive from a given socket at a given point of time).

A big difference with TCP sockets or MPI communications is that communications do not start right away after a Mailbox::put(), but wait for the corresponding Mailbox::get(). You can change this by declaring a receiving actor.

A big difference with Twitter hashtags is that SimGrid does not offer easy support to broadcast a given message to many receivers. So that would be like a Twitter tag where each message is consumed by the first receiver.

A big difference with the ZeroMQ queues is that you cannot filter on the data you want to get from the mailbox. To model such settings in SimGrid, you’d have one mailbox per potential topic, and subscribe to each topic individually with a get_async() on each mailbox. Then, use Comm::wait_any() to get the first message on any of the mailboxes you are subscribed to.

The mailboxes are not located on the network, and you can access them without any latency. The network delays are only related to the location of the sender and receiver once the match between them is done on the mailbox. This is just like the phone number that you can use locally, and the geographical distance only comes into play once you start the communication by dialing this number.

How to use Mailboxes?

You can retrieve any existing mailbox from its name (which is a unique string, just like a Twitter tag). This results in a versatile tool that can be used to build many different situations.

To model classical socket communications, use “hostname:port” as mailbox names, and make sure that only one actor reads into a given mailbox. This does not make it easy to build a perfectly realistic model of the TCP sockets, but in most cases, this system is too cumbersome for your simulations anyway. You probably want something simpler, that turns out to be easy to build with the mailboxes.

Many SimGrid examples use a sort of yellow page system where the mailbox names are the name of the service (such as “worker”, “master”, or “reducer”). That way, you don’t have to know where your peer is located to contact it. You don’t even need its name. Its function is enough for that. This also gives you some sort of load balancing for free if more than one actor pulls from the mailbox: the first actor that can deal with the request will handle it.

How are put() and get() requests matched?

The matching algorithm simple: first come, first serve. When a new send arrives, it matches the oldest enqueued receive. If no receive is currently enqueued, then the incoming send is enqueued. As you can see, the mailbox cannot contain both send and receive requests: all enqueued requests must be of the same sort.

Declaring a Receiving Actor

The last twist is that by default in the simulator, the data starts to be exchanged only when both the sender and the receiver are announced (it waits until both put() and get() are posted). In TCP, since you establish connections beforehand, the data starts to flow as soon as the sender posts it, even if the receiver did not post its put() yet.

To model this in SimGrid, you can declare a specific receiver to a given mailbox (with the function set_receiver()). That way, any put() posted to that mailbox will start as soon as possible, and the data will already be there on the receiver host when the receiver actor posts its get()

Note that being permanent receivers of a mailbox prevents actors to be garbage-collected. If your simulation creates many short-lived actors that are marked as permanent receiver, you should call mailbox->set_receiver(nullptr) by the end of the actors so that their memory gets properly reclaimed. This call should be at the end of the actor’s function, not in an on_exit callback.

Communicating without Mailboxes

Sometimes you don’t want to simulate communications between actors as allowed by mailboxes, but you want to create a direct communication between two arbitrary hosts. This can arise when you write a high-level model of a centralized scheduler, or when you model direct communications such as one-sided communications in MPI or remote memory direct access in PGAS.

For that, Comm::sendto() simulates a direct communication between the two specified hosts. No mailbox is used, and there is no rendezvous between actors. You can freely mix such direct communications and rendezvous-based communications. Alternatively, Comm::sendto_init() and Comm::sendto_async() create asynchronous direct communications.

Memory Management

For sake of simplicity, we use RAII for many classes in S4U. This is an idiom where resources are automatically managed through the context. Provided that you never manipulate objects of type Foo directly but always FooPtr references (which are defined as boost::intrusive_ptr <Foo>), you will never have to explicitly release the resource that you use nor to free the memory of unused objects. Here is a little example:

void myFunc()
{
  simgrid::s4u::MutexPtr mutex = simgrid::s4u::Mutex::create(); // Too bad we cannot use `new`

  mutex->lock();   // use the mutex as a simple reference
  //  bla bla
  mutex->unlock();

} // The mutex gets automatically freed because the only existing reference gets out of scope

Note that Mailboxes, Hosts, and Links are not handled through smart pointers (yet?). This means that it is currently impossible to destroy a mailbox or a link. You can still destroy a host (but probably shouldn’t), using simgrid::s4u::Host::destroy().

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-2021. 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("../../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("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;
    }
    
  • React to the end of actors: 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-2021. 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.connect(
          [](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.connect(
          [](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;
    }
    
  • Kill 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-2021. 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;
    }
    
  • Controlling the actor life cycle from the XML: 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-2021. 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;
    }
    
  • Daemonize 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-2021. 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;
    }
    
  • Specify the stack size to use 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-2021. 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.

  • Suspend and Resume 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-2021. 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-2021. 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-2021. 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-2021. 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");
    /* Main function of the Yielder actor */
    class yielder {
      long number_of_yields;
    
    public:
      explicit yielder(std::vector<std::string> args) { number_of_yields = std::stol(args[1]); }
      void operator()() const
      {
        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);
    
      xbt_assert(argc > 2, "Usage: %s platform_file deployment_file\n"
                           "\tExample: %s platform.xml deployment.xml\n",
                 argv[0], argv[0]);
    
      e.load_platform(argv[1]);             /* Load the platform description */
      e.register_actor<yielder>("yielder"); /* Register the class representing the actors */
    
      e.load_deployment(argv[2]);
    
      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-2021. 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 msg example");
    
    #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  = simgrid::s4u::Engine::get_clock();
        ACT_DEBUG("Entering %s", NAME.c_str());
        simgrid::s4u::this_actor::execute(amount);
        log_action(action, simgrid::s4u::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              = simgrid::s4u::Engine::get_clock();
        simgrid::s4u::Mailbox* to = simgrid::s4u::Mailbox::by_name(simgrid::s4u::this_actor::get_name() + "_" + action[2]);
        ACT_DEBUG("Entering Send: %s (size: %" PRIu64 ") -- Actor %s on mailbox %s", NAME.c_str(), size,
                  simgrid::s4u::this_actor::get_cname(), to->get_cname());
        to->put(payload, size);
        log_action(action, simgrid::s4u::Engine::get_clock() - clock);
      }
    
      static void recv(simgrid::xbt::ReplayAction& action)
      {
        double clock = simgrid::s4u::Engine::get_clock();
        simgrid::s4u::Mailbox* from =
            simgrid::s4u::Mailbox::by_name(std::string(action[2]) + "_" + simgrid::s4u::this_actor::get_name());
    
        ACT_DEBUG("Receiving: %s -- Actor %s on mailbox %s", NAME.c_str(), simgrid::s4u::this_actor::get_cname(),
                  from->get_cname());
        from->get_unique<std::string>();
        log_action(action, simgrid::s4u::Engine::get_clock() - clock);
      }
    };
    
    int main(int argc, char* argv[])
    {
      simgrid::s4u::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", simgrid::s4u::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-2021. 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");
    
    #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, simgrid::s4u::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 simgrid::s4u::File* get_file_descriptor(const std::string& file_name)
      {
        std::string full_name = simgrid::s4u::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          = simgrid::s4u::Engine::get_clock();
        std::string full_name = simgrid::s4u::this_actor::get_name() + ":" + file_name;
    
        ACT_DEBUG("Entering Open: %s (filename: %s)", NAME.c_str(), file_name.c_str());
        opened_files.emplace(std::piecewise_construct, std::forward_as_tuple(full_name),
                             std::forward_as_tuple(file_name, nullptr));
    
        log_action(action, simgrid::s4u::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          = simgrid::s4u::Engine::get_clock();
    
        simgrid::s4u::File* file = get_file_descriptor(file_name);
    
        ACT_DEBUG("Entering Read: %s (size: %llu)", NAME.c_str(), size);
        file->read(size);
    
        log_action(action, simgrid::s4u::Engine::get_clock() - clock);
      }
    
      static void close(simgrid::xbt::ReplayAction& action)
      {
        std::string file_name = action[2];
        std::string full_name = simgrid::s4u::this_actor::get_name() + ":" + file_name;
        double clock          = simgrid::s4u::Engine::get_clock();
    
        ACT_DEBUG("Entering Close: %s (filename: %s)", NAME.c_str(), file_name.c_str());
        xbt_assert(opened_files.erase(full_name) == 1, "File not found in opened files: %s", full_name.c_str());
    
        log_action(action, simgrid::s4u::Engine::get_clock() - clock);
      }
    };
    
    std::unordered_map<std::string, simgrid::s4u::File> Replayer::opened_files;
    
    int main(int argc, char* argv[])
    {
      simgrid::s4u::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", simgrid::s4u::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-2021. 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-2021. 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 argc, char** argv)
    {
      xbt_assert(argc == 3, "Expecting 2 parameters from the XML deployment file but got %d", argc);
      long messages_count     = std::stol(argv[1]); /* - number of messages */
      long msg_size           = std::stol(argv[2]); /* - message size in bytes */
      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 = std::string("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, msg_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(int, char**)
    {
      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[])
    {
      xbt_assert(argc > 2, "Usage: %s platform_file deployment_file\n", argv[0]);
    
      sg4::Engine e(&argc, argv);
      e.register_function("sender", &sender);
      e.register_function("receiver", &receiver);
    
      e.load_platform(argv[1]);
      e.load_deployment(argv[2]);
      e.run();
    
      return 0;
    }
    
  • Waiting for communications with timeouts: 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-2021. 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 argc, char** argv)
    {
      xbt_assert(argc == 4, "Expecting 3 parameters from the XML deployment file but got %d", argc);
      long messages_count  = std::stol(argv[1]); /* - number of messages */
      long msg_size        = std::stol(argv[2]); /* - message size in bytes */
      long receivers_count = std::stol(argv[3]); /* - number of receivers */
    
      std::vector<sg4::CommPtr> pending_comms;
    
      /* Start dispatching all messages to receivers, in a round robin fashion */
      for (int i = 0; i < messages_count; i++) {
        std::string mboxName        = std::string("receiver-") + std::to_string(i % receivers_count);
        sg4::Mailbox* mbox          = sg4::Mailbox::by_name(mboxName);
        std::string msgName         = std::string("Message ") + std::to_string(i);
        auto* payload               = new std::string(msgName); // copy the data we send:
    
        // 'msgName' is not a stable storage location
        XBT_INFO("Send '%s' to '%s'", msgName.c_str(), mboxName.c_str());
        /* Create a communication representing the ongoing communication */
        sg4::CommPtr comm = mbox->put_async(payload, msg_size);
        /* Add this comm to the vector of all known comms */
        pending_comms.push_back(comm);
      }
    
      /* Start sending messages to let the workers know that they should stop */
      for (int i = 0; i < receivers_count; i++) {
        std::string mboxName        = std::string("receiver-") + std::to_string(i % receivers_count);
        sg4::Mailbox* mbox          = sg4::Mailbox::by_name(mboxName);
        auto* payload               = new std::string("finalize"); // Make a copy of the data we will send
    
        sg4::CommPtr comm = mbox->put_async(payload, 0);
        pending_comms.push_back(comm);
        XBT_INFO("Send 'finalize' to 'receiver-%ld'", i % receivers_count);
      }
      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!");
    }
    
    /* Receiver actor expects 1 argument: its ID */
    static void receiver(int argc, char** argv)
    {
      xbt_assert(argc == 2, "Expecting one parameter from the XML deployment file but got %d", argc);
      sg4::Mailbox* mbox = sg4::Mailbox::by_name(std::string("receiver-") + argv[1]);
    
      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[])
    {
      xbt_assert(argc > 2, "Usage: %s platform_file deployment_file\n", argv[0]);
    
      sg4::Engine e(&argc, argv);
      e.register_function("sender", &sender);
      e.register_function("receiver", &receiver);
    
      e.load_platform(argv[1]);
      e.load_deployment(argv[2]);
      e.run();
    
      return 0;
    }
    
  • Suspending communications: The suspend() and resume() functions allow to block the progression of a given communication for a while and then unblock it. is_suspended() can be used to retrieve whether the 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-2021. 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(int argc, char**)
    {
      xbt_assert(argc == 1, "Expecting no parameter from the XML deployment file but got %d", argc - 1);
    
      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(int, char**)
    {
      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[])
    {
      xbt_assert(argc > 2, "Usage: %s platform_file deployment_file\n", argv[0]);
    
      sg4::Engine e(&argc, argv);
      e.register_function("sender", &sender);
      e.register_function("receiver", &receiver);
    
      e.load_platform(argv[1]);
      e.load_deployment(argv[2]);
      e.run();
    
      return 0;
    }
    
  • Waiting for all communications in a set: The wait_all() function is useful when you want to block until all activities in a given set have been completed.

    See also simgrid::s4u::Comm::wait_all().

    View examples/cpp/comm-waitall/s4u-comm-waitall.cpp

    Download s4u-comm-waitall.cpp

    /* Copyright (c) 2010-2021. 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 block on the completion of a set of communications.
     *
     * 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 occur concurrently.
     *
     * The sender then blocks until all ongoing communication terminate, using simgrid::s4u::Comm::wait_all()
     *
     */
    
    #include "simgrid/s4u.hpp"
    #include <cstdlib>
    #include <iostream>
    #include <string>
    namespace sg4 = simgrid::s4u;
    
    XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_async_waitall, "Messages specific for this s4u example");
    
    class Sender {
      long messages_count;  /* - number of messages */
      long receivers_count; /* - number of receivers */
      long msg_size;        /* - message size in bytes */
    
    public:
      explicit Sender(std::vector<std::string> args)
      {
        xbt_assert(args.size() == 4, "Expecting 3 parameters from the XML deployment file but got %zu", args.size());
        messages_count  = std::stol(args[1]);
        msg_size        = std::stol(args[2]);
        receivers_count = std::stol(args[3]);
      }
      void operator()() const
      {
        // sphinx-doc: init-begin (this line helps the doc to build; ignore it)
        /* Vector in which we store all ongoing communications */
        std::vector<sg4::CommPtr> pending_comms;
    
        /* Make a vector of the mailboxes to use */
        std::vector<sg4::Mailbox*> mboxes;
        for (int i = 0; i < receivers_count; i++)
          mboxes.push_back(sg4::Mailbox::by_name(std::string("receiver-") + std::to_string(i)));
        // sphinx-doc: init-end
    
        /* Start dispatching all messages to receivers, in a round robin fashion */
        for (int i = 0; i < messages_count; i++) {
          std::string msg_content = std::string("Message ") + std::to_string(i);
          // Copy the data we send: the 'msg_content' variable is not a stable storage location.
          // It will be destroyed when this actor leaves the loop, ie before the receiver gets it
          auto* payload = new std::string(msg_content);
    
          XBT_INFO("Send '%s' to '%s'", msg_content.c_str(), mboxes[i % receivers_count]->get_cname());
    
          /* Create a communication representing the ongoing communication, and store it in pending_comms */
          sg4::CommPtr comm = mboxes[i % receivers_count]->put_async(payload, msg_size);
          pending_comms.push_back(comm);
        }
    
        /* Start sending messages to let the workers know that they should stop */ // sphinx-doc: put-begin
        for (int i = 0; i < receivers_count; i++) {
          XBT_INFO("Send 'finalize' to 'receiver-%d'", i);
          sg4::CommPtr comm = mboxes[i]->put_async(new std::string("finalize"), 0);
          pending_comms.push_back(comm);
        }
        XBT_INFO("Done dispatching all messages");
    
        /* Now that all message exchanges were initiated, wait for their completion in one single call */
        sg4::Comm::wait_all(pending_comms);
        // sphinx-doc: put-end
    
        XBT_INFO("Goodbye now!");
      }
    };
    
    /* Receiver actor expects 1 argument: its ID */
    class Receiver {
      sg4::Mailbox* mbox;
    
    public:
      explicit Receiver(std::vector<std::string> args)
      {
        xbt_assert(args.size() == 2, "Expecting one parameter from the XML deployment file but got %zu", args.size());
        std::string mboxName = std::string("receiver-") + args[1];
        mbox                 = sg4::Mailbox::by_name(mboxName);
      }
      void operator()()
      {
        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());
          cont = (*received != "finalize"); // If it's a finalize message, we're done
          // Receiving the message was all we were supposed to do
        }
      }
    };
    
    int main(int argc, char* argv[])
    {
      xbt_assert(argc > 2, "Usage: %s platform_file deployment_file\n", argv[0]);
    
      sg4::Engine e(&argc, argv);
      e.register_actor<Sender>("sender");
      e.register_actor<Receiver>("receiver");
    
      e.load_platform(argv[1]);
      e.load_deployment(argv[2]);
      e.run();
    
      return 0;
    }
    
  • Waiting for the first completed communication in a set: The wait_any() function is useful when you want to block until one activity of the set completes, no matter which terminates first.

    See also simgrid::s4u::Comm::wait_any().

    View examples/cpp/comm-waitany/s4u-comm-waitany.cpp

    Download s4u-comm-waitany.cpp

    /* Copyright (c) 2010-2021. 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_any() to wait for the first occurring event.
     *
     * 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 occur concurrently.
     *
     * The sender then loops until there is no ongoing communication. Using wait_any() ensures that the sender
     * will notice events as soon as they occur even if it does not follow the order of the container.
     *
     * Here, finalize messages will terminate earlier because their size is 0, so they travel faster than the
     * other messages of this application.  As expected, the trace shows that the finalize of worker 1 is
     * processed before 'Message 5' that is sent to worker 0.
     *
     */
    
    #include "simgrid/s4u.hpp"
    #include <cstdlib>
    #include <iostream>
    #include <string>
    namespace sg4 = simgrid::s4u;
    
    XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_comm_waitall, "Messages specific for this s4u example");
    
    class Sender {
      long messages_count;  /* - number of messages */
      long receivers_count; /* - number of receivers */
      long msg_size;        /* - message size in bytes */
    
    public:
      explicit Sender(std::vector<std::string> args)
      {
        xbt_assert(args.size() == 4, "Expecting 3 parameters from the XML deployment file but got %zu", args.size());
        messages_count  = std::stol(args[1]);
        msg_size        = std::stol(args[2]);
        receivers_count = std::stol(args[3]);
      }
      void operator()() const
      {
        /* Vector in which we store all ongoing communications */
        std::vector<sg4::CommPtr> pending_comms;
    
        /* Make a vector of the mailboxes to use */
        std::vector<sg4::Mailbox*> mboxes;
        for (int i = 0; i < receivers_count; i++)
          mboxes.push_back(sg4::Mailbox::by_name(std::string("receiver-") + std::to_string(i)));
    
        /* Start dispatching all messages to receivers, in a round robin fashion */
        for (int i = 0; i < messages_count; i++) {
          std::string msg_content = std::string("Message ") + std::to_string(i);
          // Copy the data we send: the 'msg_content' variable is not a stable storage location.
          // It will be destroyed when this actor leaves the loop, ie before the receiver gets it
          auto* payload = new std::string(msg_content);
    
          XBT_INFO("Send '%s' to '%s'", msg_content.c_str(), mboxes[i % receivers_count]->get_cname());
    
          /* Create a communication representing the ongoing communication, and store it in pending_comms */
          sg4::CommPtr comm = mboxes[i % receivers_count]->put_async(payload, msg_size);
          pending_comms.push_back(comm);
        }
    
        /* Start sending messages to let the workers know that they should stop */
        for (int i = 0; i < receivers_count; i++) {
          XBT_INFO("Send 'finalize' to 'receiver-%d'", i);
          sg4::CommPtr comm = mboxes[i]->put_async(new std::string("finalize"), 0);
          pending_comms.push_back(comm);
        }
        XBT_INFO("Done dispatching all messages");
    
        /* Now that all message exchanges were initiated, wait for their completion, in order of termination.
         *
         * This loop waits for first terminating message with wait_any() and remove it with erase(), until all comms are
         * terminated
         * Even in this simple example, the pending comms do not terminate in the exact same order of creation.
         */
        while (not pending_comms.empty()) {
          ssize_t changed_pos = sg4::Comm::wait_any(pending_comms);
          pending_comms.erase(pending_comms.begin() + changed_pos);
          if (changed_pos != 0)
            XBT_INFO("Remove the %zdth pending comm: it terminated earlier than another comm that was initiated first.",
                     changed_pos);
        }
    
        XBT_INFO("Goodbye now!");
      }
    };
    
    /* Receiver actor expects 1 argument: its ID */
    class Receiver {
      sg4::Mailbox* mbox;
    
    public:
      explicit Receiver(std::vector<std::string> args)
      {
        xbt_assert(args.size() == 2, "Expecting one parameter from the XML deployment file but got %zu", args.size());
        std::string mboxName = std::string("receiver-") + args[1];
        mbox                 = sg4::Mailbox::by_name(mboxName);
      }
      void operator()()
      {
        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());
          cont = (*received != "finalize"); // If it's a finalize message, we're done
          // Receiving the message was all we were supposed to do
        }
      }
    };
    
    int main(int argc, char* argv[])
    {
      xbt_assert(argc > 2, "Usage: %s platform_file deployment_file\n", argv[0]);
    
      sg4::Engine e(&argc, argv);
      e.register_actor<Sender>("sender");
      e.register_actor<Receiver>("receiver");
    
      e.load_platform(argv[1]);
      e.load_deployment(argv[2]);
      e.run();
    
      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-2021. 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");
    
    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 */
      simgrid::s4u::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. */
      simgrid::s4u::this_actor::execute(98095, 2);
      XBT_INFO("Done.");
    
      /* Note that the timings printed when executing this example are a bit misleading,
       * because the uneven sharing only last until the privileged actor ends.
       * After this point, the unprivileged one gets 100% of the CPU and finishes
       * quite quickly. */
    }
    
    int main(int argc, char* argv[])
    {
      simgrid::s4u::Engine e(&argc, argv);
      xbt_assert(argc > 1, "Usage: %s platform_file\n\tExample: %s ../platforms/small_platform.xml\n", argv[0], argv[0]);
    
      e.load_platform(argv[1]);
    
      simgrid::s4u::Actor::create("executor", e.host_by_name("Tremblay"), executor);
      simgrid::s4u::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-2021. 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");
    
    /* This actor simply waits for its activity completion after starting it.
     * That's exactly equivalent to synchronous execution. */
    static void waiter()
    {
      double computation_amount = simgrid::s4u::this_actor::get_host()->get_speed();
      XBT_INFO("Execute %g flops, should take 1 second.", computation_amount);
      simgrid::s4u::ExecPtr activity = simgrid::s4u::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 = simgrid::s4u::this_actor::get_host()->get_speed();
      XBT_INFO("Execute %g flops, should take 1 second.", computation_amount);
      simgrid::s4u::ExecPtr activity = simgrid::s4u::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());
        simgrid::s4u::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 = simgrid::s4u::this_actor::get_host()->get_speed();
    
      XBT_INFO("Execute %g flops, should take 1 second.", computation_amount);
      simgrid::s4u::ExecPtr activity = simgrid::s4u::this_actor::exec_async(computation_amount);
      simgrid::s4u::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[])
    {
      simgrid::s4u::Engine e(&argc, argv);
      e.load_platform(argv[1]);
    
      simgrid::s4u::Host* fafard  = e.host_by_name("Fafard");
      simgrid::s4u::Host* ginette = e.host_by_name("Ginette");
      simgrid::s4u::Host* boivin  = e.host_by_name("Boivin");
    
      simgrid::s4u::Actor::create("wait", fafard, waiter);
      simgrid::s4u::Actor::create("monitor", ginette, monitor);
      simgrid::s4u::Actor::create("cancel", boivin, canceller);
    
      e.run();
    
      XBT_INFO("Simulation time %g", simgrid::s4u::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.

    See also simgrid::s4u::Exec::set_host().

    View examples/cpp/exec-remote/s4u-exec-remote.cpp

    Download s4u-exec-remote.cpp

    /* Copyright (c) 2007-2021. 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");
    
    static void wizard()
    {
      const simgrid::s4u::Host* fafard = simgrid::s4u::Host::by_name("Fafard");
      simgrid::s4u::Host* ginette = simgrid::s4u::Host::by_name("Ginette");
      simgrid::s4u::Host* boivin  = simgrid::s4u::Host::by_name("Boivin");
    
      XBT_INFO("I'm a wizard! I can run an activity on the Ginette host from the Fafard one! Look!");
      simgrid::s4u::ExecPtr exec = simgrid::s4u::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).");
    
      simgrid::s4u::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 = simgrid::s4u::this_actor::exec_init(73293500)->set_host(ginette);
      exec->start();
    
      simgrid::s4u::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);
    
      simgrid::s4u::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 = simgrid::s4u::this_actor::exec_init(48.492e6)->set_host(ginette);
      exec->start();
    
      simgrid::s4u::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[])
    {
      simgrid::s4u::Engine e(&argc, argv);
      e.load_platform(argv[1]);
      simgrid::s4u::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-2021. 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");
    
    static void runner()
    {
      /* Retrieve the list of all hosts as an array of hosts */
      auto hosts         = simgrid::s4u::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
    
      simgrid::s4u::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
    
      simgrid::s4u::ExecPtr activity =
          simgrid::s4u::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
      simgrid::s4u::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();
      simgrid::s4u::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              = simgrid::s4u::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());
        simgrid::s4u::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              = simgrid::s4u::this_actor::exec_init(hosts, computation_amounts, communication_amounts);
      activity->start();
    
      simgrid::s4u::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};
      simgrid::s4u::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 = simgrid::s4u::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[])
    {
      simgrid::s4u::Engine e(&argc, argv);
    
      xbt_assert(argc == 2, "Usage: %s <platform file>", argv[0]);
    
      e.load_platform(argv[1]);
      simgrid::s4u::Actor::create("test", e.host_by_name("MyHost1"), runner);
    
      e.run();
      XBT_INFO("Simulation done.");
      return 0;
    }
    
  • Using Pstates on a host: 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-2021. 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");
    
    static int dvfs()
    {
      double workload = 100E6;
      simgrid::s4u::Host* host = simgrid::s4u::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
      simgrid::s4u::this_actor::execute(workload);
    
      double exec_time = simgrid::s4u::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
      simgrid::s4u::this_actor::execute(workload);
    
      exec_time = simgrid::s4u::Engine::get_clock() - exec_time;
      XBT_INFO("Computation2 duration: %.2f", exec_time);
    
      // Verify that the default pstate is set to 0
      host = simgrid::s4u::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[])
    {
      simgrid::s4u::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]);
    
      simgrid::s4u::Actor::create("dvfs_test", e.host_by_name("MyHost1"), dvfs);
      simgrid::s4u::Actor::create("dvfs_test", e.host_by_name("MyHost2"), dvfs);
    
      e.run();
    
      XBT_INFO("Total simulation time: %e", simgrid::s4u::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-2021. 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");
    
    static void host()
    {
      /* -Add an extra disk in a programmatic way */
      simgrid::s4u::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 ***", simgrid::s4u::Host::current()->get_cname());
    
      /* - Retrieve all disks from current host */
      std::vector<simgrid::s4u::Disk*> const& disk_list = simgrid::s4u::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 */
      simgrid::s4u::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 simgrid::s4u::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 ***");
    
      const auto* data = static_cast<std::string*>(disk->get_data());
    
      XBT_INFO("Get storage data: '%s'", data ? data->c_str() : "No user data");
    
      disk->set_data(new std::string("Some user data"));
      data = static_cast<std::string*>(disk->get_data());
      XBT_INFO("Set and get data: '%s'", data->c_str());
      delete data;
    }
    
    int main(int argc, char** argv)
    {
      simgrid::s4u::Engine e(&argc, argv);
      e.load_platform(argv[1]);
    
      /* - Display Host properties */
      for (auto h : e.get_all_hosts()) {
        XBT_INFO("*** %s properties ****", h->get_cname());
        for (auto const& kv : *h->get_properties())
          XBT_INFO("  %s -> %s", kv.first.c_str(), kv.second.c_str());
      }
    
      simgrid::s4u::Actor::create("", e.host_by_name("bob"), host);
    
      e.run();
      XBT_INFO("Simulated time: %g", simgrid::s4u::Engine::get_clock());
    
      return 0;
    }
    

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-2021. 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");
    
    class MyHost {
    public:
      void show_info(std::vector<simgrid::s4u::Disk*> const& disks) const
      {
        XBT_INFO("Storage info on %s:", simgrid::s4u::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<simgrid::s4u::Disk*> const& disks = simgrid::s4u::Host::current()->get_disks();
    
        show_info(disks);
    
        // Open a non-existing file to create it
        std::string filename     = "/scratch/tmp/data.txt";
        auto* file               = new simgrid::s4u::File(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"));
        const auto* file_data = static_cast<std::string*>(file->get_data());
        XBT_INFO("User data attached to the file: %s", file_data->c_str());
        delete file_data;
    
        // Close the file
        delete file;
    
        show_info(disks);
    
        // Reopen the file and then unlink it
        file = new simgrid::s4u::File("/scratch/tmp/simgrid.readme", nullptr);
        XBT_INFO("Unlink file: '%s'", file->get_path());
        file->unlink();
        delete file; // Unlinking the file on "disk" does not free the object
    
        show_info(disks);
      }
    };
    
    int main(int argc, char** argv)
    {
      simgrid::s4u::Engine e(&argc, argv);
      sg_storage_file_system_init();
      e.load_platform(argv[1]);
      simgrid::s4u::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-2021. 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");
    
    static void host(std::vector<std::string> args)
    {
      simgrid::s4u::File file(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(),
                   simgrid::s4u::Host::current()->get_cname(), args[2].c_str());
          file.remote_move(simgrid::s4u::Host::by_name(args[2]), args[3]);
        } else {
          XBT_INFO("Copy '%s' (of size %llu) from '%s' to '%s'", filename, file.size(),
                   simgrid::s4u::Host::current()->get_cname(), args[2].c_str());
          file.remote_copy(simgrid::s4u::Host::by_name(args[2]), args[3]);
        }
      }
    }
    
    int main(int argc, char** argv)
    {
      simgrid::s4u::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<simgrid::s4u::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", simgrid::s4u::Engine::get_clock());
      return 0;
    }
    

Classical synchronization objects

  • Barrier: Shows how to use simgrid::s4u::Barrier synchronization objects.

    View examples/cpp/synchro-barrier/s4u-synchro-barrier.cpp

    Download s4u-synchro-barrier.cpp

    /* Copyright (c) 2006-2021. The SimGrid Team. All rights reserved.          */
    
    /* This program is free software; you can redistribute it and/or modify it
     * under the terms of the license (GNU LGPL) which comes with this package. */
    
    #include "simgrid/s4u.hpp"
    
    XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "a sample log category");
    
    /// Wait on the barrier then leave
    static void worker(simgrid::s4u::BarrierPtr barrier)
    {
        XBT_INFO("Waiting on the barrier");
        barrier->wait();
    
        XBT_INFO("Bye");
    }
    
    /// Spawn actor_count-1 workers and do a barrier with them
    static void master(int actor_count)
    {
      simgrid::s4u::BarrierPtr barrier = simgrid::s4u::Barrier::create(actor_count);
    
      XBT_INFO("Spawning %d workers", actor_count - 1);
      for (int i = 0; i < actor_count - 1; i++) {
        simgrid::s4u::Actor::create("worker", simgrid::s4u::Host::by_name("Jupiter"), worker, barrier);
      }
    
        XBT_INFO("Waiting on the barrier");
        barrier->wait();
    
        XBT_INFO("Bye");
    }
    
    int main(int argc, char **argv)
    {
      simgrid::s4u::Engine e(&argc, argv);
    
      // Parameter: Number of actores in the barrier
      xbt_assert(argc >= 2, "Usage: %s <actor-count>\n", argv[0]);
      int actor_count = std::stoi(argv[1]);
      xbt_assert(actor_count > 0, "<actor-count> must be greater than 0");
    
      e.load_platform("../../platforms/two_hosts.xml");
      simgrid::s4u::Actor::create("master", e.host_by_name("Tremblay"), master, actor_count);
      e.run();
    
      return 0;
    }
    
  • Condition variable: basic usage Shows how to use simgrid::s4u::ConditionVariable synchronization objects.

    View examples/cpp/synchro-condition-variable/s4u-synchro-condition-variable.cpp

    Download s4u-synchro-condition-variable.cpp

    /* Copyright (c) 2006-2021. The SimGrid Team. All rights reserved.          */
    
    /* This program is free software; you can redistribute it and/or modify it
     * under the terms of the license (GNU LGPL) which comes with this package. */
    
    #include <mutex>           /* std::mutex and std::lock_guard */
    #include <simgrid/s4u.hpp> /* All of S4U */
    
    XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "a sample log category");
    
    std::string data;
    bool done = false;
    
    static void worker_fun(simgrid::s4u::ConditionVariablePtr cv, simgrid::s4u::MutexPtr mutex)
    {
      std::unique_lock<simgrid::s4u::Mutex> lock(*mutex);
    
      XBT_INFO("Start processing data which is '%s'.", data.c_str());
      data += std::string(" after processing");
    
      // Send data back to main()
      XBT_INFO("Signal to master that the data processing is completed, and exit.");
    
      done = true;
      cv->notify_one();
    }
    
    static void master_fun()
    {
      auto mutex  = simgrid::s4u::Mutex::create();
      auto cv     = simgrid::s4u::ConditionVariable::create();
      data        = std::string("Example data");
      auto worker = simgrid::s4u::Actor::create("worker", simgrid::s4u::Host::by_name("Jupiter"), worker_fun, cv, mutex);
    
      // wait for the worker
      cv->wait(std::unique_lock<simgrid::s4u::Mutex>(*mutex), []() { return done; });
      XBT_INFO("data is now '%s'.", data.c_str());
    
      worker->join();
    }
    
    int main(int argc, char** argv)
    {
      simgrid::s4u::Engine e(&argc, argv);
      e.load_platform("../../platforms/two_hosts.xml");
      simgrid::s4u::Actor::create("main", e.host_by_name("Tremblay"), master_fun);
      e.run();
    
      return 0;
    }
    
  • Condition variable: timeouts Shows how to specify timeouts when blocking on condition variables.

    View examples/cpp/synchro-condition-variable-waituntil/s4u-synchro-condition-variable-waituntil.cpp

    Download s4u-synchro-condition-variable-waituntil.cpp

    /* Copyright (c) 2006-2021. The SimGrid Team. All rights reserved.          */
    
    /* This program is free software; you can redistribute it and/or modify it
     * under the terms of the license (GNU LGPL) which comes with this package. */
    
    #include <mutex>           /* std::mutex and std::lock_guard */
    #include <simgrid/s4u.hpp> /* All of S4U */
    
    XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "a sample log category");
    
    simgrid::s4u::MutexPtr mtx = nullptr;
    simgrid::s4u::ConditionVariablePtr cv = nullptr;
    bool ready = false;
    
    static void competitor(int id)
    {
      XBT_INFO("Entering the race...");
      std::unique_lock<simgrid::s4u::Mutex> lck(*mtx);
      while (not ready) {
        auto now = simgrid::s4u::Engine::get_clock();
        if (cv->wait_until(lck, now + (id+1)*0.25) == std::cv_status::timeout) {
          XBT_INFO("Out of wait_until (timeout)");
        }
        else {
          XBT_INFO("Out of wait_until (YAY!)");
        }
      }
      XBT_INFO("Running!");
    }
    
    static void go()
    {
      XBT_INFO("Are you ready? ...");
      simgrid::s4u::this_actor::sleep_for(3);
      std::unique_lock<simgrid::s4u::Mutex> lck(*mtx);
      XBT_INFO("Go go go!");
      ready = true;
      cv->notify_all();
    }
    
    static void main_actor()
    {
      mtx = simgrid::s4u::Mutex::create();
      cv = simgrid::s4u::ConditionVariable::create();
    
      auto host = simgrid::s4u::this_actor::get_host();
      for (int i = 0; i < 10; ++i)
        simgrid::s4u::Actor::create("competitor", host, competitor, i);
      simgrid::s4u::Actor::create("go", host, go);
    }
    
    int main(int argc, char* argv[])
    {
      simgrid::s4u::Engine e(&argc, argv);
      e.load_platform("../../platforms/small_platform.xml");
    
      simgrid::s4u::Actor::create("main", e.host_by_name("Tremblay"), main_actor);
    
      e.run();
      return 0;
    }
    
  • Mutex: Shows how to use simgrid::s4u::Mutex synchronization objects.

    View examples/cpp/synchro-mutex/s4u-synchro-mutex.cpp

    Download s4u-synchro-mutex.cpp

    /* Copyright (c) 2006-2021. The SimGrid Team. All rights reserved.          */
    
    /* This program is free software; you can redistribute it and/or modify it
     * under the terms of the license (GNU LGPL) which comes with this package. */
    
    #include <mutex> /* std::mutex and std::lock_guard */
    #include "simgrid/s4u.hpp" /* All of S4U */
    
    constexpr int NB_ACTOR = 6;
    
    XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "a sample log category");
    
    /* This worker uses a classical mutex */
    static void worker(simgrid::s4u::MutexPtr mutex, int& result)
    {
      // lock the mutex before enter in the critical section
      mutex->lock();
    
      XBT_INFO("Hello s4u, I'm ready to compute after a regular lock");
      // And finally add it to the results
      result += 1;
      XBT_INFO("I'm done, good bye");
    
      // You have to unlock the mutex if you locked it manually.
      // Beware of exceptions preventing your unlock() from being executed!
      mutex->unlock();
    }
    
    static void workerLockGuard(simgrid::s4u::MutexPtr mutex, int& result)
    {
      // Simply use the std::lock_guard like this
      // It's like a lock() that would do the unlock() automatically when getting out of scope
      std::lock_guard<simgrid::s4u::Mutex> lock(*mutex);
    
      // then you are in a safe zone
      XBT_INFO("Hello s4u, I'm ready to compute after a lock_guard");
      // update the results
      result += 1;
      XBT_INFO("I'm done, good bye");
    
      // Nothing specific here: the unlock will be automatic
    }
    
    static void master()
    {
      int result = 0;
      simgrid::s4u::MutexPtr mutex = simgrid::s4u::Mutex::create();
    
      for (int i = 0; i < NB_ACTOR * 2 ; i++) {
        // To create a worker use the static method simgrid::s4u::Actor.
        if((i % 2) == 0 )
          simgrid::s4u::Actor::create("worker", simgrid::s4u::Host::by_name("Jupiter"), workerLockGuard, mutex,
                                      std::ref(result));
        else
          simgrid::s4u::Actor::create("worker", simgrid::s4u::Host::by_name("Tremblay"), worker, mutex, std::ref(result));
      }
    
      simgrid::s4u::this_actor::sleep_for(10);
      XBT_INFO("Results is -> %d", result);
    }
    
    int main(int argc, char **argv)
    {
      simgrid::s4u::Engine e(&argc, argv);
      e.load_platform("../../platforms/two_hosts.xml");
      simgrid::s4u::Actor::create("main", e.host_by_name("Tremblay"), master);
      e.run();
    
      return 0;
    }
    
  • Semaphore: Shows how to use simgrid::s4u::Semaphore synchronization objects.

    View examples/cpp/synchro-semaphore/s4u-synchro-semaphore.cpp

    Download s4u-synchro-semaphore.cpp

    /* Copyright (c) 2006-2021. The SimGrid Team. All rights reserved.          */
    
    /* This program is free software; you can redistribute it and/or modify it
     * under the terms of the license (GNU LGPL) which comes with this package. */
    
    // This example implements a simple producer/consumer schema,
    // passing a bunch of items from one to the other
    
    #include "simgrid/s4u.hpp"
    
    #include <memory>
    
    XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "a sample log category");
    
    const char* buffer;                                                        /* Where the data is exchanged */
    simgrid::s4u::SemaphorePtr sem_empty = simgrid::s4u::Semaphore::create(1); /* indicates whether the buffer is empty */
    simgrid::s4u::SemaphorePtr sem_full  = simgrid::s4u::Semaphore::create(0); /* indicates whether the buffer is full */
    
    static void producer(const std::vector<std::string>& args)
    {
      for (auto const& str : args) {
        sem_empty->acquire();
        XBT_INFO("Pushing '%s'", str.c_str());
        buffer = str.c_str();
        sem_full->release();
      }
    
      XBT_INFO("Bye!");
    }
    static void consumer()
    {
      std::string str;
      do {
        sem_full->acquire();
        str = buffer;
        XBT_INFO("Receiving '%s'", str.c_str());
        sem_empty->release();
      } while (str != "");
    
      XBT_INFO("Bye!");
    }
    
    int main(int argc, char **argv)
    {
      std::vector<std::string> args({"one", "two", "three", ""});
      simgrid::s4u::Engine e(&argc, argv);
      e.load_platform("../../platforms/two_hosts.xml");
      simgrid::s4u::Actor::create("producer", e.host_by_name("Tremblay"), producer, std::cref(args));
      simgrid::s4u::Actor::create("consumer", e.host_by_name("Jupiter"), consumer);
      e.run();
    
      return 0;
    }
    

Interacting with the Platform

  • User-defined properties: You can attach arbitrary information to most platform elements from the XML file, and then interact with these values from your program. Note that the changes are not written permanently on disk, in the XML file nor anywhere else. They only last until the end of your simulation.

    View examples/cpp/platform-properties/s4u-platform-properties.cpp

    Download s4u-platform-properties.cpp

    /* Copyright (c) 2017-2021. The SimGrid Team. All rights reserved.          */
    
    /* This program is free software; you can redistribute it and/or modify it
     * under the terms of the license (GNU LGPL) which comes with this package. */
    
    // TODO: also test the properties attached to links
    
    #include <algorithm>
    #include <simgrid/s4u.hpp>
    #include <string>
    
    XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Property test");
    
    static void test_host(const std::string& hostname)
    {
      simgrid::s4u::Host* thehost = simgrid::s4u::Host::by_name(hostname);
      const std::unordered_map<std::string, std::string>* hostprops = thehost->get_properties();
      const char* noexist = "Unknown";
      const char* exist   = "Hdd";
      const char* value;
    
      XBT_INFO("== Print the properties of the host '%s'", hostname.c_str());
      // Sort the properties before displaying them, so that the tests are perfectly reproducible
      std::vector<std::string> keys;
      for (auto const& kv : *hostprops)
        keys.push_back(kv.first);
      std::sort(keys.begin(), keys.end());
      for (const std::string& key : keys)
        XBT_INFO("  Host property: '%s' -> '%s'", key.c_str(), hostprops->at(key).c_str());
    
      XBT_INFO("== Try to get a host property that does not exist");
      value = thehost->get_property(noexist);
      xbt_assert(not value, "The key exists (it's not supposed to)");
    
      XBT_INFO("== Try to get a host property that does exist");
      value = thehost->get_property(exist);
      xbt_assert(value, "\tProperty %s is undefined (where it should)", exist);
      xbt_assert(strcmp(value, "180") == 0, "\tValue of property %s is defined to %s (where it should be 180)", exist,
                 value);
      XBT_INFO("   Property: %s old value: %s", exist, value);
    
      XBT_INFO("== Trying to modify a host property");
      thehost->set_property(exist, "250");
    
      /* Test if we have changed the value */
      value = thehost->get_property(exist);
      xbt_assert(value, "Property %s is undefined (where it should)", exist);
      xbt_assert(strcmp(value, "250") == 0, "Value of property %s is defined to %s (where it should be 250)", exist, value);
      XBT_INFO("   Property: %s old value: %s", exist, value);
    
      /* Restore the value for the next test */
      thehost->set_property(exist, "180");
    
      const auto* thezone = thehost->get_englobing_zone();
      XBT_INFO("== Print the properties of the zone '%s' that contains '%s'", thezone->get_cname(), hostname.c_str());
      const std::unordered_map<std::string, std::string>* zoneprops = thezone->get_properties();
      keys.clear();
      for (auto const& kv : *zoneprops)
        keys.push_back(kv.first);
      std::sort(keys.begin(), keys.end());
      for (const std::string& key : keys)
        XBT_INFO("  Zone property: '%s' -> '%s'", key.c_str(), zoneprops->at(key).c_str());
    }
    
    static void alice(std::vector<std::string> /*args*/)
    {
      /* Dump what we have on the current host */
      test_host("host1");
    }
    
    static void carole(std::vector<std::string> /*args*/)
    {
      /* Dump what we have on a remote host */
      simgrid::s4u::this_actor::sleep_for(1); // Wait for alice to be done with its experiment
      test_host("host1");
    }
    
    static void david(std::vector<std::string> /*args*/)
    {
      /* Dump what we have on a remote host */
      simgrid::s4u::this_actor::sleep_for(2); // Wait for alice and carole to be done with its experiment
      test_host("node-0.simgrid.org");
    }
    
    static void bob(std::vector<std::string> /*args*/)
    {
      /* this host also tests the properties of the AS*/
      const simgrid::s4u::NetZone* root = simgrid::s4u::Engine::get_instance()->get_netzone_root();
      XBT_INFO("== Print the properties of the root zone");
      XBT_INFO("   Zone property: filename -> %s", root->get_property("filename"));
      XBT_INFO("   Zone property: date -> %s", root->get_property("date"));
      XBT_INFO("   Zone property: author -> %s", root->get_property("author"));
    
      /* Get the property list of current bob actor */
      const std::unordered_map<std::string, std::string>* props = simgrid::s4u::Actor::self()->get_properties();
      const char* noexist = "UnknownProcessProp";
      XBT_ATTRIB_UNUSED const char* value;
    
      XBT_INFO("== Print the properties of the actor");
      for (const auto& kv : *props)
        XBT_INFO("   Actor property: %s -> %s", kv.first.c_str(), kv.second.c_str());
    
      XBT_INFO("== Try to get an actor property that does not exist");
    
      value = simgrid::s4u::Actor::self()->get_property(noexist);
      xbt_assert(not value, "The property is defined (it should not)");
    }
    
    int main(int argc, char* argv[])
    {
      simgrid::s4u::Engine e(&argc, argv);
      e.load_platform(argv[1]);
    
      e.register_function("alice", alice);
      e.register_function("bob", bob);
      e.register_function("carole", carole);
      e.register_function("david", david);
    
      size_t totalHosts = e.get_host_count();
    
      XBT_INFO("There are %zu hosts in the environment", totalHosts);
      std::vector<simgrid::s4u::Host*> hosts = e.get_all_hosts();
      for (simgrid::s4u::Host const* host : hosts)
        XBT_INFO("Host '%s' runs at %.0f flops/s", host->get_cname(), host->get_speed());
    
      e.load_deployment(argv[2]);
      e.run();
    
      return 0;
    }
    
  • Retrieving the netzones matching given criteria: Shows how to filter the cluster netzones.

    View examples/cpp/routing-get-clusters/s4u-routing-get-clusters.cpp

    Download s4u-routing-get-clusters.cpp

    /* Copyright (c) 2009-2021. The SimGrid Team. All rights reserved.          */
    
    /* This program is free software; you can redistribute it and/or modify it
     * under the terms of the license (GNU LGPL) which comes with this package. */
    
    #include "simgrid/kernel/routing/ClusterZone.hpp"
    #include "simgrid/kernel/routing/DragonflyZone.hpp"
    #include "simgrid/s4u.hpp"
    
    XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
    
    int main(int argc, char* argv[])
    {
      simgrid::s4u::Engine e(&argc, argv);
      e.load_platform(argv[1]);
    
      std::vector<simgrid::kernel::routing::ClusterZone*> clusters =
          e.get_filtered_netzones<simgrid::kernel::routing::ClusterZone>();
    
      for (auto c : clusters) {
        XBT_INFO("%s", c->get_cname());
        std::vector<simgrid::s4u::Host*> hosts = c->get_all_hosts();
        for (auto h : hosts)
          XBT_INFO("   %s", h->get_cname());
      }
    
      std::vector<simgrid::kernel::routing::DragonflyZone*> dragonfly_clusters =
          e.get_filtered_netzones<simgrid::kernel::routing::DragonflyZone>();
    
      for (auto d : dragonfly_clusters) {
        XBT_INFO("%s' dragonfly topology:", d->get_cname());
        for (int i = 0; i < d->get_host_count(); i++) {
          const simgrid::kernel::routing::DragonflyZone::Coords coords = d->rankId_to_coords(i);
          XBT_INFO("   %d: (%lu, %lu, %lu, %lu)", i, coords.group, coords.chassis, coords.blade, coords.node);
        }
      }
    
      return 0;
    }
    
  • Retrieving the list of hosts matching given criteria: Shows how to filter the actors that match given criteria.

    View examples/cpp/engine-filtering/s4u-engine-filtering.cpp

    Download s4u-engine-filtering.cpp

    /* Copyright (c) 2017-2021. The SimGrid Team. All rights reserved.          */
    
    /* This program is free software; you can redistribute it and/or modify it
     * under the terms of the license (GNU LGPL) which comes with this package. */
    
    #include <simgrid/s4u.hpp>
    
    /* This example shows how to use simgrid::s4u::Engine::get_filtered_hosts() to retrieve
     * all hosts that match a given criteria. This criteria can be specified either with:
     *   - an inlined callback
     *   - a boolean function, such as filter_speed_more_than_50Mf() below
     *   - a functor (= function object), that can either be stateless such as filter::SingleCore below, or that can save
     *     state such as filter::FrequencyChanged below
     *
     * This file provides examples for each of these categories. You should implement your own filters in your code.
     */
    
    XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_engine_filtering, "Messages specific for this s4u example");
    
    namespace filter {
    /* First example of thing that we can use as a filtering criteria: a simple boolean function */
    static bool filter_speed_more_than_50Mf(const simgrid::s4u::Host* host)
    {
      return host->get_speed() > 50E6;
    }
    
    /* Second kind of thing that we can use as a filtering criteria: a functor (=function object).
     * This one is a bit stupid: it's a lot of boilerplate over a dummy boolean function.
     */
    class SingleCore {
    public:
      bool operator()(const simgrid::s4u::Host* host) const { return host->get_core_count() == 1; }
    };
    
    /* This functor is a bit more complex, as it saves the current state when created.
     * Then, it allows one to easily retrieve the hosts which frequency changed since the functor creation.
     */
    class FrequencyChanged {
      std::map<simgrid::s4u::Host*, unsigned long> host_list;
    
    public:
      explicit FrequencyChanged(const simgrid::s4u::Engine& e)
      {
        std::vector<simgrid::s4u::Host*> list = e.get_all_hosts();
        for (auto& host : list) {
          host_list.insert({host, host->get_pstate()});
        }
      }
    
      bool operator()(simgrid::s4u::Host* host) { return host->get_pstate() != host_list.at(host); }
    
      unsigned long get_old_speed_state(simgrid::s4u::Host* host) { return host_list.at(host); }
    };
    }
    int main(int argc, char* argv[])
    {
      simgrid::s4u::Engine e(&argc, argv);
      e.load_platform(argv[1]);
    
      /* Use a lambda function to filter hosts: We only want multicore hosts */
      XBT_INFO("Hosts currently registered with this engine: %zu", e.get_host_count());
      std::vector<simgrid::s4u::Host*> list =
          e.get_filtered_hosts([](const simgrid::s4u::Host* host) { return host->get_core_count() > 1; });
    
      for (auto& host : list)
        XBT_INFO("The following hosts have more than one core: %s", host->get_cname());
    
      xbt_assert(list.size() == 1);
    
      /* Use a function object (functor) without memory */
      list = e.get_filtered_hosts(filter::SingleCore());
    
      for (auto& host : list)
        XBT_INFO("The following hosts are SingleCore: %s", host->get_cname());
    
      /* Use a function object that uses memory to filter */
      XBT_INFO("A simple example: Let's retrieve all hosts that changed their frequency");
      filter::FrequencyChanged filter(e);
      e.host_by_name("MyHost2")->set_pstate(2);
      list = e.get_filtered_hosts(filter);
    
      for (auto& host : list)
        XBT_INFO("The following hosts changed their frequency: %s (from %.1ff to %.1ff)", host->get_cname(),
                 host->get_pstate_speed(filter.get_old_speed_state(host)), host->get_speed());
    
      /* You can also just use any regular function (namespaced on need) to filter  */
      list = e.get_filtered_hosts(filter::filter_speed_more_than_50Mf);
    
      for (auto& host : list)
        XBT_INFO("The following hosts have a frequency > 50Mf: %s", host->get_cname());
    
      return 0;
    }
    
  • Specifying state profiles: shows how to specify when the resources must be turned off and on again, and how to react to such failures in your code. See also Modeling churn (e.g., in P2P).

    View examples/cpp/platform-failures/s4u-platform-failures.cpp

    Download s4u-platform-failures.cpp

    /* Copyright (c) 2007-2021. The SimGrid Team. All rights reserved.          */
    
    /* This program is free software; you can redistribute it and/or modify it
     * under the terms of the license (GNU LGPL) which comes with this package. */
    
    /* This example shows how to work with the state profile of a host or a link,
     * specifying when the resource must be turned on or off.
     *
     * To set such a profile, the first way is to use a file in the XML, while the second is to use the programmatic
     * interface. Once this profile is in place, the resource will automatically be turned on and off.
     *
     * The actors running on a host that is turned off are forcefully killed
     * once their on_exit callbacks are executed. They cannot avoid this fate.
     * Since we specified on_failure="RESTART" for each actors in the XML file,
     * they will be automatically restarted when the host starts again.
     *
     * Communications using failed links will .. fail.
     */
    
    #include "simgrid/s4u.hpp"
    
    XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_test, "Messages specific for this s4u example");
    
    static void master(std::vector<std::string> args)
    {
      xbt_assert(args.size() == 5, "Expecting one parameter");
    
      simgrid::s4u::Mailbox* mailbox;
      long number_of_tasks = std::stol(args[1]);
      double comp_size     = std::stod(args[2]);
      long comm_size       = std::stol(args[3]);
      long workers_count   = std::stol(args[4]);
    
      XBT_INFO("Got %ld workers and %ld tasks to process", workers_count, number_of_tasks);
    
      for (int i = 0; i < number_of_tasks; i++) {
        mailbox         = simgrid::s4u::Mailbox::by_name(std::string("worker-") + std::to_string(i % workers_count));
        auto* payload   = new double(comp_size);
        try {
          XBT_INFO("Send a message to %s", mailbox->get_cname());
          mailbox->put(payload, comm_size, 10.0);
          XBT_INFO("Send to %s completed", mailbox->get_cname());
        } catch (const simgrid::TimeoutException&) {
          delete payload;
          XBT_INFO("Mmh. Got timeouted while speaking to '%s'. Nevermind. Let's keep going!", mailbox->get_cname());
        } catch (const simgrid::NetworkFailureException&) {
          delete payload;
          XBT_INFO("Mmh. The communication with '%s' failed. Nevermind. Let's keep going!", mailbox->get_cname());
        }
      }
    
      XBT_INFO("All tasks have been dispatched. Let's tell everybody the computation is over.");
      for (int i = 0; i < workers_count; i++) {
        /* - Eventually tell all the workers to stop by sending a "finalize" task */
        mailbox         = simgrid::s4u::Mailbox::by_name(std::string("worker-") + std::to_string(i));
        auto* payload   = new double(-1.0);
        try {
          mailbox->put(payload, 0, 1.0);
        } catch (const simgrid::TimeoutException&) {
          delete payload;
          XBT_INFO("Mmh. Got timeouted while speaking to '%s'. Nevermind. Let's keep going!", mailbox->get_cname());
        } catch (const simgrid::NetworkFailureException&) {
          delete payload;
          XBT_INFO("Mmh. Something went wrong with '%s'. Nevermind. Let's keep going!", mailbox->get_cname());
        }
      }
    
      XBT_INFO("Goodbye now!");
    }
    
    static void worker(std::vector<std::string> args)
    {
      xbt_assert(args.size() == 2, "Expecting one parameter");
      long id                          = std::stol(args[1]);
      simgrid::s4u::Mailbox* mailbox   = simgrid::s4u::Mailbox::by_name(std::string("worker-") + std::to_string(id));
      while (true) {
        try {
          XBT_INFO("Waiting a message on %s", mailbox->get_cname());
          auto payload = mailbox->get_unique<double>();
          xbt_assert(payload != nullptr, "mailbox->get() failed");
          double comp_size = *payload;
          if (comp_size < 0) { /* - Exit when -1.0 is received */
            XBT_INFO("I'm done. See you!");
            break;
          }
          /*  - Otherwise, process the task */
          XBT_INFO("Start execution...");
          simgrid::s4u::this_actor::execute(comp_size);
          XBT_INFO("Execution complete.");
        } catch (const simgrid::NetworkFailureException&) {
          XBT_INFO("Mmh. Something went wrong. Nevermind. Let's keep going!");
        }
      }
    }
    
    int main(int argc, char* argv[])
    {
      simgrid::s4u::Engine e(&argc, argv);
      e.load_platform(argv[1]);
      e.register_function("master", master);
      e.register_function("worker", worker);
      e.load_deployment(argv[2]);
    
      e.run();
    
      XBT_INFO("Simulation time %g", simgrid::s4u::Engine::get_clock());
      return 0;
    }
    
  • Specifying speed profiles: shows how to specify an external load to resources, variating their peak speed over time.

    View examples/cpp/platform-profile/s4u-platform-profile.cpp

    Download s4u-platform-profile.cpp

    /* Copyright (c) 2017-2021. The SimGrid Team. All rights reserved.          */
    
    /* This program is free software; you can redistribute it and/or modify it
     * under the terms of the license (GNU LGPL) which comes with this package. */
    
    #include "simgrid/s4u.hpp"
    
    /* This example demonstrates how to attach a profile to a host or a link,
     * to specify external changes to the resource speed. The first way to do
     * so is to use a file in the XML, while the second is to use the programmatic interface.
     */
    
    XBT_LOG_NEW_DEFAULT_CATEGORY(s4u_platform_profile, "Messages specific for this s4u example");
    
    /* Main function of the Yielder actor */
    static void watcher()
    {
      const simgrid::s4u::Host* jupiter = simgrid::s4u::Host::by_name("Jupiter");
      const simgrid::s4u::Host* fafard  = simgrid::s4u::Host::by_name("Fafard");
      const simgrid::s4u::Link* link1   = simgrid::s4u::Link::by_name("1");
      const simgrid::s4u::Link* link2   = simgrid::s4u::Link::by_name("2");
    
      for (int i = 0; i < 10; i++) {
        XBT_INFO("Fafard: %.0fGflops, Jupiter: % 3.0fGflops, Link1: (%.2fMB/s %.0fms), Link2: (%.2fMB/s %.0fms)",
                 fafard->get_speed() * fafard->get_available_speed() / 1000000,
                 jupiter->get_speed() * jupiter->get_available_speed() / 1000000, link1->get_bandwidth() / 1000,
                 link1->get_latency() * 1000, link2->get_bandwidth() / 1000, link2->get_latency() * 1000);
        simgrid::s4u::this_actor::sleep_for(1);
      }
    }
    
    int main(int argc, char* argv[])
    {
      simgrid::s4u::Engine e(&argc, argv);
    
      xbt_assert(argc > 1, "Usage: %s platform_file\n\tExample: %s platform.xml deployment.xml\n", argv[0], argv[0]);
    
      e.load_platform(argv[1]);
    
      simgrid::s4u::Actor::create("watcher", e.host_by_name("Tremblay"), watcher);
    
      e.run(); /* - Run the simulation */
    
      return 0;
    }
    

Energy Simulation

  • Describing the energy profiles in the platform: The first platform file contains the energy profile of each link and host for a wired network, which is necessary to get energy consumption predictions. The second platform file is the equivalent for a wireless network. As usual, you should not trust our example, and you should strive to double-check that your instantiation matches your target platform.

    View examples/platforms/energy_platform.xml

    Download energy_platform.xml

    <?xml version='1.0' encoding='utf-8'?>
    <!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
    <platform version="4.1">
      <zone id="AS0" routing="Full">
        <!-- Multiple pstate processor capacities can be defined as a list of powers specified for a given host -->
        <!-- Attribute 'pstate' specifies the initially selected pstate (here, the lowest pstate corresponds to the highest
             processor speed) -->
        <host core="4" id="MyHost1" pstate="0" speed="100.0Mf,50.0Mf,20.0Mf">
          <!--  List of Idle:Epsilon:AllCores (in Watts) corresponding to the speed consumed when the processor is idle,
                when all cores have a tiny epsilon load, and when all cores are fully loaded -->
          <!--  The list must contain one energetic profile for each previously defined pstate-->
          <prop id="wattage_per_state" value="100.0:93.33333333333333:200.0, 93.0:90.0:170.0, 90.0:90.0:150.0" />
          <prop id="wattage_off" value="10" />
        </host>
        
        <host core="1" id="MyHost2" pstate="0" speed="100.0Mf,50.0Mf,20.0Mf">
          <!-- This host is mono-core and its consumption is either idle or full load (Epsilon=AllCores) -->
          <prop id="wattage_per_state" value="100.0:200.0:200.0, 93.0:170.0:170.0, 90.0:150.0:150.0" />
          <prop id="wattage_off" value="10" />
        </host>
        
        <host core="1" id="MyHost3" pstate="0" speed="100.0Mf,50.0Mf,20.0Mf">
          <!-- This host is mono-core and its consumption is either idle or full load (Epsilon=AllCores) -->
          <prop id="wattage_per_state" value="100.0:200.0:200.0, 93.0:170.0:170.0, 90.0:150.0:150.0" />
          <prop id="wattage_off" value="10" />
        </host>
     
        <link bandwidth="100kBps" id="bus" latency="0" sharing_policy="SHARED">
    <!--   REALISTIC VALUES                    <prop id="wattage_range" value="10.3581:10.7479" /> -->
    <!--  IREALISTIC VALUES FOR THE TEST -->   <prop id="wattage_range" value="1:3" /> 
        </link>
        <route dst="MyHost2" src="MyHost1">
          <link_ctn id="bus" />
        </route>
        <route dst="MyHost3" src="MyHost1">
          <link_ctn id="bus" />
        </route>
        <route dst="MyHost3" src="MyHost2">
          <link_ctn id="bus" />
        </route>
      </zone>
    </platform>
    

    View examples/platforms/wifi_energy.xml

    Download wifi_energy.xml

    <?xml version = '1.0'?>
    <!DOCTYPE platform SYSTEM "https://simgrid.org/simgrid.dtd">
    <platform version = "4.1">
      <zone id="WIFI zone" routing = "Wifi">
        <prop id = "access_point" value = "router" />
        
        <host id = "Station 1" speed = "100.0Mf,50.0Mf,20.0Mf" />
        <host id = "Station 2" speed = "100.0Mf,50.0Mf,20.0Mf" />
    
        <router id = "router"/>
    
        <link id = "AP1" sharing_policy = "WIFI" bandwidth = "54Mbps" latency="0ms">
          <prop id = "wifi_watt_values" value = "0:1:1:0"/>
          <prop id = "control_duration" value = "0"/>
        </link>
      </zone>
    </platform>	
    
  • Consumption due to the CPU: This example shows how to retrieve the amount of energy consumed by the CPU during computations, and the impact of the pstate.

    View examples/cpp/energy-exec/s4u-energy-exec.cpp

    Download s4u-energy-exec.cpp

    /* Copyright (c) 2007-2021. 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");
    
    static void dvfs()
    {
      simgrid::s4u::Host* host1 = simgrid::s4u::Host::by_name("MyHost1");
      simgrid::s4u::Host* host2 = simgrid::s4u::Host::by_name("MyHost2");
    
      XBT_INFO("Energetic profile: %s", host1->get_property("wattage_per_state"));
      XBT_INFO("Initial peak speed=%.0E flop/s; Energy dissipated =%.0E J", host1->get_speed(),
               sg_host_get_consumed_energy(host1));
    
      double start = simgrid::s4u::Engine::get_clock();
      XBT_INFO("Sleep for 10 seconds");
      simgrid::s4u::this_actor::sleep_for(10);
      XBT_INFO("Done sleeping (duration: %.2f s). Current peak speed=%.0E; Energy dissipated=%.2f J",
               simgrid::s4u::Engine::get_clock() - start, host1->get_speed(), sg_host_get_consumed_energy(host1));
    
      // Execute something
      start             = simgrid::s4u::Engine::get_clock();
      double flopAmount = 100E6;
      XBT_INFO("Run a computation of %.0E flops", flopAmount);
      simgrid::s4u::this_actor::execute(flopAmount);
      XBT_INFO(
          "Computation done (duration: %.2f s). Current peak speed=%.0E flop/s; Current consumption: from %.0fW to %.0fW"
          " depending on load; Energy dissipated=%.0f J",
          simgrid::s4u::Engine::get_clock() - start, host1->get_speed(), sg_host_get_wattmin_at(host1, host1->get_pstate()),
          sg_host_get_wattmax_at(host1, host1->get_pstate()), sg_host_get_consumed_energy(host1));
    
      // ========= Change power peak =========
      int pstate = 2;
      host1->set_pstate(pstate);
      XBT_INFO("========= Requesting pstate %d (speed should be of %.0E flop/s and is of %.0E flop/s)", pstate,
               host1->get_pstate_speed(pstate), host1->get_speed());
    
      // Run another computation
      start = simgrid::s4u::Engine::get_clock();
      XBT_INFO("Run a computation of %.0E flops", flopAmount);
      simgrid::s4u::this_actor::execute(flopAmount);
      XBT_INFO("Computation done (duration: %.2f s). Current peak speed=%.0E flop/s; Energy dissipated=%.0f J",
               simgrid::s4u::Engine::get_clock() - start, host1->get_speed(), sg_host_get_consumed_energy(host1));
    
      start = simgrid::s4u::Engine::get_clock();
      XBT_INFO("Sleep for 4 seconds"