From d4cc1617632cd9d23affa1338ba0bf7d4b0efd0e Mon Sep 17 00:00:00 2001 From: Martin Quinson Date: Sat, 18 Aug 2018 01:20:26 +0200 Subject: [PATCH] tuto_s4u: add an exercise about the Mailboxes, and improve related doc --- docs/source/conf.py | 3 +- docs/source/tuto_s4u.rst | 157 ++++++++++++------ docs/source/tuto_s4u/deployment1.xml | 27 +++ docs/source/tuto_s4u/deployment2.xml | 10 ++ .../s4u-app-masterworkers-fun.cpp | 2 +- include/simgrid/s4u/Actor.hpp | 11 +- include/simgrid/s4u/Mailbox.hpp | 52 ++++-- 7 files changed, 184 insertions(+), 78 deletions(-) create mode 100644 docs/source/tuto_s4u/deployment1.xml create mode 100644 docs/source/tuto_s4u/deployment2.xml diff --git a/docs/source/conf.py b/docs/source/conf.py index 8fd2797e59..70f5238758 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -83,7 +83,8 @@ exhale_args = { XBT_ATTRIB_DEPRECATED_v322(m)= \ XBT_ATTRIB_DEPRECATED_v323(m)= \ XBT_ATTRIB_DEPRECATED_v324(m)= - """ + + """ } # For cross-ref generation diff --git a/docs/source/tuto_s4u.rst b/docs/source/tuto_s4u.rst index 6a15168afd..2b5eeace9e 100644 --- a/docs/source/tuto_s4u.rst +++ b/docs/source/tuto_s4u.rst @@ -64,21 +64,18 @@ The Actors .......... Let's start with the code of the worker. It is represented by the -*master* function below. This simple function takes 4 parameters, -given as a vector of strings: - - - the number of workers managed by the master. - - the number of tasks to dispatch - - the computational size (in flops to compute) of each task - - the communication size (in bytes to exchange) of each task +*master* function below. This simple function takes at least 3 +parameters (the amount of tasks to dispatch, their computational size +in flops to compute and their communication size in bytes to +exchange). Every parameter after the third one must be the name of an +host on which a worker is waiting for something to compute. Then, the tasks are sent one after the other, each on a mailbox named -"worker-XXX" where XXX is the number of an existing worker. On the -other side, a given worker (which code is given below) wait for -incoming tasks on its own mailbox. Notice how this mailbox mechanism -allow the actors to find each other without having all information: -the master don't have to know the actors nor even where they are, it -simply pushes the messages on mailbox which name is predetermined. +after the worker's hosts. On the other side, a given worker (which +code is given below) wait for incoming tasks on its own +mailbox. + + At the end, once all tasks are dispatched, the master dispatches another task per worker, but this time with a negative amount of flops @@ -97,12 +94,16 @@ are nicely logged along with the simulated time and actor name. :start-after: master-begin :end-before: master-end -Here comes the code of the worker actors. This function expects only one -parameter from its vector of strings: its identifier so that it knows -on which mailbox its incoming tasks will arrive. Its code is very -simple: as long as it gets valid computation requests (whose -compute_amount is positive), it compute this task and waits for the -next one. +Here comes the code of the worker actors. This function expects no +parameter from its vector of strings. Its code is very simple: it +expects messages on the mailbox that is named after its own host. As long as it gets valid +computation requests (whose compute_amount is positive), it compute +this task and waits for the next one. + +The worker retrieves its own host with +:cpp:func:`simgrid::s4u::this_actor::get_host`. The +:ref:`simgrid::s4u::this_actor ` +namespace contains many such helping functions. .. literalinclude:: ../../examples/s4u/app-masterworkers/s4u-app-masterworkers-fun.cpp :language: c++ @@ -113,11 +114,11 @@ Starting the Simulation ....................... And this is it. In only a few lines, we defined the algorithm of our -master/workers examples. Well, this is true, but an algorithm alone is -not enough to define a simulation. +master/workers examples. -First, SimGrid is a library, not a program. So you need to define your -own `main()` function, as follows. This function is in charge of +That being said, an algorithm alone is not enough to define a +simulation: SimGrid is a library, not a program. So you need to define +your own ``main()`` function as follows. This function is in charge of creating a SimGrid simulation engine (on line 3), register the actor functions to the engine (on lines 7 and 8), load the virtual platform from its description file (on line 11), map actors onto that platform @@ -129,8 +130,8 @@ from its description file (on line 11), map actors onto that platform :end-before: main-end :linenos: -After that, the missing pieces are the platform and deployment -files. +As you can see, this also requires a platform file and a deployment +file. Platform File ............. @@ -145,9 +146,8 @@ Such files can get rather long and boring, so the example below is only an excerpts of the full ``examples/platforms/small_platform.xml`` file. For example, most routing information are missing, and only the route between the hosts Tremblay and Fafard is given. This path -traverses 6 links (4, 3, 2, 0, 1 and 8). The full file, along with -other examples, can be found in the archive under -``examples/platforms``. +traverses 6 links (named 4, 3, 2, 0, 1 and 8). There are several +examples of platforms in the archive under ``examples/platforms``. .. |api_s4u_NetZone| image:: /images/extlink.png :align: middle @@ -161,7 +161,7 @@ other examples, can be found in the archive under .. literalinclude:: ../../examples/platforms/small_platform.xml :language: xml - :lines: 1-10,12-20,56-63,192- + :lines: 1-10,12-20,56-62,192- :caption: (excerpts of the small_platform.xml file) Deployment File @@ -256,7 +256,7 @@ This very simple setting raises many interesting questions: **SimGrid was invented to answer such questions.** Do not believe the fools saying that all you need to study such settings is a simple discrete event simulator. Do you really want to reinvent the wheel, -debug your own tool, optimize it and validate its models against real +debug and optimize your own tool, and validate its models against real settings for ages, or do you prefer to sit on the shoulders of a giant? With SimGrid, you can focus on your algorithm. The whole simulation mechanism is already working. @@ -328,28 +328,76 @@ you can find it in /bin/colorize. Exercise 1: Simplifying the deployment file ........................................... -In the provided example, the deployment file is tightly connected to -the platform file ``small_platform.xml`` and adding more workers -quickly becomes a pain: You need to start them (at the bottom of the -file), add to inform the master that they are available by increasing -the right parameter. - -Instead, modify the simulator ``master-workers.c`` into -``master-workers-exo1.c`` so that the master launches a worker process -on `all` the other machines at startup. The new deployment file should -be as simple as: - -.. code-block:: xml - - - - - - - - - - +In the provided example, adding more workers quickly becomes a pain: +You need to start them (at the bottom of the file), and to inform the +master of its availability with an extra parameter. This is mandatory +if you want to inform the master of where the workers are running. But +actually, the master does not need to have this information. + +We could leverage the mailbox mechanism flexibility, and use a sort of +yellow page system: Instead of sending data to the worker running on +Fafard, the master could send data to the third worker. Ie, instead of +using the worker location (which should be filled in two locations), +we could use their ID (which should be filled in one location +only). + +This could be done with the following deployment file. It's clearly +not shorter than the previous one, but it's still simpler because the +information is only written once. It thus follows the `DRY +`_ `SPOT +`_ design principle. + +.. literalinclude:: tuto_s4u/deployment1.xml + :language: xml + + +Copy your ``master-workers.cpp`` into ``master-workers-exo1.cpp`` and +add a new executable into ``CMakeLists.txt``. Then modify your worker +function so that it gets its mailbox name not from the name of its +host, but from the string passed as ``args[1]``. The master will send +messages to all workers based on their number, for example as follows: + +.. code-block:: cpp + + for (int i = 0; i < tasks_count; i++) { + std::string worker_rank = std::to_string(i % workers_count); + std::string mailbox_name = std::string("worker-") + worker_rank; + simgrid::s4u::MailboxPtr mailbox = simgrid::s4u::Mailbox::by_name(mailbox_name); + + mailbox->put(...); + + ... + } + + +Wrap up +^^^^^^^ + +The mailboxes are a very powerful mechanism in SimGrid, allowing many +interesting application settings. They may feel surprising if you are +used to BSD sockets or other classical systems, but you will soon +appreciate their power. They are only used to match the +communications, but have no impact on the communication +timing. ``put()`` and ``get()`` are matched regardless of their +initiators' location and then the real communication occures between +the involved parties. + +Please refer to the full `API of Mailboxes +`_ +|api_s4u_Mailbox|_ for more details. + + +Exercise 2: Using the whole platform +.................................... + +It is now easier to add a new worker, but you still has to do it +manually. It would be much easier if the master could start the +workers on its own, one per available host in the platform. The new +deployment file should be as simple as: + +.. literalinclude:: tuto_s4u/deployment2.xml + :language: xml + Creating the workers from the master ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -411,9 +459,8 @@ Wrap up In this exercise, we reduced the amount of configuration that our simulator requests. This is both a good idea, and a dangerous -trend. This simplification is an application of the good old DRY/SPOT -programming principle (Don't Repeat Yourself / Single Point Of Truth --- `more on wikipedia +trend. This simplification is another application of the good old DRY/SPOT +programming principle (`Don't Repeat Yourself / Single Point Of Truth `_), and you really want your programming artefacts to follow these software engineering principles. diff --git a/docs/source/tuto_s4u/deployment1.xml b/docs/source/tuto_s4u/deployment1.xml new file mode 100644 index 0000000000..3812cf4ebc --- /dev/null +++ b/docs/source/tuto_s4u/deployment1.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/source/tuto_s4u/deployment2.xml b/docs/source/tuto_s4u/deployment2.xml new file mode 100644 index 0000000000..e9163f217f --- /dev/null +++ b/docs/source/tuto_s4u/deployment2.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/examples/s4u/app-masterworkers/s4u-app-masterworkers-fun.cpp b/examples/s4u/app-masterworkers/s4u-app-masterworkers-fun.cpp index 1ccb48c963..e3727c031e 100644 --- a/examples/s4u/app-masterworkers/s4u-app-masterworkers-fun.cpp +++ b/examples/s4u/app-masterworkers/s4u-app-masterworkers-fun.cpp @@ -49,7 +49,7 @@ static void worker(std::vector args) { xbt_assert(args.size() == 1, "The worker expects no argument"); - auto my_host = simgrid::s4u::this_actor::get_host(); + simgrid::s4u::Host* my_host = simgrid::s4u::this_actor::get_host(); simgrid::s4u::MailboxPtr mailbox = simgrid::s4u::Mailbox::by_name(my_host->get_name()); double compute_cost; diff --git a/include/simgrid/s4u/Actor.hpp b/include/simgrid/s4u/Actor.hpp index 6ee97c4ab3..fd9a6a4751 100644 --- a/include/simgrid/s4u/Actor.hpp +++ b/include/simgrid/s4u/Actor.hpp @@ -442,16 +442,18 @@ XBT_PUBLIC bool is_suspended(); /** @brief kill the actor. */ XBT_PUBLIC void exit(); -#ifndef DOXYGEN -/** @deprecated Please use std::function for first parameter */ -XBT_ATTRIB_DEPRECATED_v323("Please use std::function for first parameter.") XBT_PUBLIC - void on_exit(int_f_pvoid_pvoid_t fun, void* data); /** @brief Add a function to the list of "on_exit" functions. */ XBT_PUBLIC void on_exit(std::function fun, void* data); /** @brief Migrate the actor to a new host. */ XBT_PUBLIC void migrate(Host* new_host); +/** @} */ + +#ifndef DOXYGEN +/** @deprecated Please use std::function for first parameter */ +XBT_ATTRIB_DEPRECATED_v323("Please use std::function for first parameter.") XBT_PUBLIC + void on_exit(int_f_pvoid_pvoid_t fun, void* data); /** @deprecated See this_actor::get_name() */ XBT_ATTRIB_DEPRECATED_v323("Please use this_actor::get_name()") XBT_PUBLIC std::string getName(); /** @deprecated See this_actor::get_cname() */ @@ -473,7 +475,6 @@ XBT_ATTRIB_DEPRECATED_v324("Please use this_actor::exit()") XBT_PUBLIC void kill #endif } -/** @} */ }} // namespace simgrid::s4u diff --git a/include/simgrid/s4u/Mailbox.hpp b/include/simgrid/s4u/Mailbox.hpp index 78950269a5..ebc45a4317 100644 --- a/include/simgrid/s4u/Mailbox.hpp +++ b/include/simgrid/s4u/Mailbox.hpp @@ -15,11 +15,8 @@ namespace simgrid { namespace s4u { /** @brief Mailboxes: Network rendez-vous points. - * @ingroup s4u_api * - * @tableofcontents - * - * @section s4u_mb_what What are mailboxes + * What are mailboxes? * * Rendez-vous point for network communications, similar to URLs on * which you could post and retrieve data. Actually, the mailboxes are @@ -31,27 +28,43 @@ namespace s4u { * find the receiver. The twitter hashtag, which help senders and * receivers to find each others. In TCP, the pair {host name, host * port} to which you can connect to find your interlocutor. In HTTP, - * URLs through which the clients can connect to the servers. + * URLs through which the clients can connect to the servers. In ZeroMQ + * and other queuing systems, the queues are used to match senders + * and receivers. * - * One big difference with most of these systems is that usually, 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 + * 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. This is a big difference to the socket ports for * example, that are definitely exclusive in receiving. * + * Mailboxes can optionally have a @i receiver with `simgrid::s4u::Mailbox::set_receiver()`. + * It means that the data exchange starts as soon as the sender has + * done the `put()`, even before the corresponding `get()` + * (usually, it starts as soon as both `put()` and `get()` are posted). + * This is closer to the BSD semantic and can thus help to improve + * the timing accuracy, but this is not mandatory at all. + * * 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 coming 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 + * mailbox you are subscribed onto. + * * The mailboxes are not located on the network, and you can access * them without any latency. The network delay 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 dialling this number. + * once you start the communication by dialing this number. * - * @section s4u_mb_howto How to use mailboxes? + * How to use mailboxes? * * Any existing mailbox can be retrieve from its name (which are * unique strings, just like with twitter tags). This results in a @@ -60,7 +73,7 @@ namespace s4u { * * For something close to classical socket communications, use * "hostname:port" as mailbox names, and make sure that only one actor - * reads into that mailbox. It's hard to build a prefectly realistic + * reads into that mailbox. It's hard to build a perfectly realistic * model of the TCP sockets, but most of the time, this system is too * cumbersome for your simulations anyway. You probably want something * simpler, that turns our to be easy to build with the mailboxes. @@ -74,16 +87,16 @@ namespace s4u { * the first relevant actor that can deal with the request will handle * it. * - * @section s4u_mb_matching How are sends and receives matched? + * How are sends and receives matched? * * The matching algorithm is as simple as a first come, first * serve. When a new send arrives, it matches the oldest enqueued - * receive. If no receive is currently enqueued, then the incomming + * 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. * - * @section s4u_mb_receiver Declaring a receiving actor + * 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 @@ -99,7 +112,8 @@ namespace s4u { * start as soon as possible, and the data will already be there on * the receiver host when the receiver actor posts its receive(). * - * @section s4u_mb_api The API + * The API + * */ class XBT_PUBLIC Mailbox { #ifndef DOXYGEN @@ -143,7 +157,13 @@ public: * * It means that the communications sent to this mailbox will start flowing to * its host even before he does a recv(). This models the real behavior of TCP - * and MPI communications, amongst other. + * and MPI communications, amongst other. It will improve the accuracy of + * predictions, in particular if your application exhibits swarms of small messages. + * + * SimGrid does not enforces any kind of ownership over the mailbox. Even if a receiver + * was declared, any other actors can still get() data from the mailbox. The timings + * will then probably be off tracks, so you should strive on your side to not get data + * from someone else's mailbox. */ void set_receiver(ActorPtr actor); -- 2.20.1