SimGrid comes with many examples provided in the examples/ directory. Those examples are described in section MSG examples . Those examples are commented and should be easy to understand. for a first step into SimGrid we also provide some more detailed examples in the sections below.
You should also check our online
tutorial section that contains a generic tutorial about using SimGrid.
Using MSG
You should also check our online
tutorial section that contains a dedicated tutorial.
Here are some examples on how to use MSG, the most used API.
tr MSG comes with an extensive set of examples. It is sometimes difficult to find the one you need. This list aims at helping you finding the example from which you can learn what you want to.
Basic examples and features
Asynchronous communications
Simulation of asynchronous communications between a sender and a receiver using a realistic platform and an external description of the deployment.
Code of the application
Preliminary declarations
#include <stdio.h>
#include "msg/msg.h"
#include "xbt/sysdep.h"
#include "xbt/log.h"
#include "xbt/asserts.h"
"Messages specific for this msg example");
int sender(int argc, char *argv[]);
int receiver(int argc, char *argv[]);
const char *application_file);
Sender function
The sender send to a receiver an asynchronous message with the function "MSG_task_isend()". Cause this function is non-blocking we have to make "MSG_comm_test()" to know if the communication is finished for finally destroy it with function "MSG_comm_destroy()". It also available to "make MSG_comm_wait()" which make both of them.
C style arguments (argc/argv) are interpreted as:
- the number of tasks to distribute
- the computation size of each task
- the size of the files associated to each task
- a list of host that will accept those tasks.
- the time to sleep at the beginning of the function
- This time defined the process sleep time if time = 0 use of MSG_comm_wait() if time > 0 use of MSG_comm_test()
int sender(int argc, char *argv[])
{
long number_of_tasks = atol(argv[1]);
double task_comp_size = atof(argv[2]);
double task_comm_size = atof(argv[3]);
long receivers_count = atol(argv[4]);
double sleep_start_time = atof(argv[5]);
double sleep_test_time = atof(argv[6]);
XBT_INFO(
"sleep_start_time : %f , sleep_test_time : %f", sleep_start_time,
sleep_test_time);
int i;
for (i = 0; i < number_of_tasks; i++) {
char mailbox[256];
char sprintf_buffer[256];
sprintf(mailbox, "receiver-%ld", i % receivers_count);
sprintf(sprintf_buffer, "Task_%d", i);
task =
NULL);
XBT_INFO(
"Send to receiver-%ld Task_%d", i % receivers_count, i);
if (sleep_test_time == 0) {
} else {
};
}
}
for (i = 0; i < receivers_count; i++) {
char mailbox[80];
sprintf(mailbox, "receiver-%ld", i % receivers_count);
XBT_INFO(
"Send to receiver-%ld finalize", i % receivers_count);
if (sleep_test_time == 0) {
} else {
};
}
}
return 0;
}
Receiver function
This function executes tasks when it receives them. As the receiving is asynchronous we have to test the communication to know if it is completed or not with "MSG_comm_test()" or wait for the completion "MSG_comm_wait()".
C style arguments (argc/argv) are interpreted as:
- the id to use for received the communication.
- the time to sleep at the beginning of the function
- This time defined the process sleep time if time = 0 use of MSG_comm_wait() if time > 0 use of MSG_comm_test()
int receiver(int argc, char *argv[])
{
int id = -1;
char mailbox[80];
double sleep_start_time = atof(argv[2]);
double sleep_test_time = atof(argv[3]);
XBT_INFO(
"sleep_start_time : %f , sleep_test_time : %f", sleep_start_time,
sleep_test_time);
_XBT_GNUC_UNUSED int read;
read = sscanf(argv[1], "%d", &id);
"Invalid argument %s\n", argv[1]);
sprintf(mailbox, "receiver-%d", id);
while (1) {
if (sleep_test_time == 0) {
} else {
};
}
break;
}
task = NULL;
}
return 0;
}
Simulation core
This function is the core of the simulation and is divided only into 3 parts thanks to MSG_create_environment() and MSG_launch_application().
- Simulation settings : MSG_create_environment() creates a realistic environment
- Application deployment : create the processes on the right locations with MSG_launch_application()
- The simulation is run with MSG_main()
Its arguments are:
- platform_file: the name of a file containing an valid surfxml platform description.
- application_file: the name of a file containing a valid surfxml application description
const char *application_file)
{
{
}
{
}
return res;
}
Main function
This initializes MSG, runs a simulation, and free all data-structures created by MSG.
int main(int argc, char *argv[])
{
if (argc < 3) {
printf("Usage: %s platform_file deployment_file\n", argv[0]);
printf("example: %s msg_platform.xml msg_deployment.xml\n", argv[0]);
exit(1);
}
res = test_all(argv[1], argv[2]);
return 0;
else
return 1;
}
Waitall function for sender
The use of this function permit to send all messages and wait for the completion of all in one time.
int sender(int argc, char *argv[])
{
long number_of_tasks = atol(argv[1]);
double task_comp_size = atof(argv[2]);
double task_comm_size = atof(argv[3]);
long receivers_count = atol(argv[4]);
int i;
for (i = 0; i < number_of_tasks; i++) {
char mailbox[256];
char sprintf_buffer[256];
sprintf(mailbox, "receiver-%ld", i % receivers_count);
sprintf(sprintf_buffer, "Task_%d", i);
task =
NULL);
XBT_INFO(
"Send to receiver-%ld Task_%d", i % receivers_count, i);
}
for (i = 0; i < receivers_count; i++) {
char mailbox[80];
sprintf(mailbox, "receiver-%ld", i % receivers_count);
XBT_INFO(
"Send to receiver-%ld finalize", i % receivers_count);
}
xbt_free(comm);
return 0;
}
Waitany function
The MSG_comm_waitany() function return the place of the first message send or receive from a xbt_dynar_t table.
From a sender
We can use this function to wait all sent messages.
int sender(int argc, char *argv[])
{
long number_of_tasks = atol(argv[1]);
double task_comp_size = atof(argv[2]);
double task_comm_size = atof(argv[3]);
long receivers_count = atol(argv[4]);
int diff_com = atol(argv[5]);
double coef = 0;
int i;
char mailbox[256];
char sprintf_buffer[256];
for (i = 0; i < number_of_tasks; i++) {
if (diff_com == 0)
coef = 1;
else
coef = (i + 1);
sprintf(mailbox, "receiver-%ld", (i % receivers_count));
sprintf(sprintf_buffer, "Task_%d", i);
task =
task_comm_size / coef, NULL);
XBT_INFO(
"Send to receiver-%ld %s comm_size %f", i % receivers_count,
sprintf_buffer, task_comm_size / coef);
}
}
sprintf(mailbox, "finalize");
for (i = 0; i < receivers_count; i++) {
task = NULL;
}
return 0;
}
From a receiver
We can also wait for the arrival of all messages.
int receiver(int argc, char *argv[])
{
int id = -1;
int i;
char mailbox[80];
int tasks = atof(argv[2]);
_XBT_GNUC_UNUSED int read;
read = sscanf(argv[1], "%d", &id);
xbt_assert(read,
"Invalid argument %s\n", argv[1]);
sprintf(mailbox, "receiver-%d", id);
for (i = 0; i < tasks; i++) {
task[i] = NULL;
}
}
xbt_free(task);
sprintf(mailbox, "finalize");
return 0;
}
Basic Master/Slaves
Simulation of a master-slave application using a realistic platform and an external description of the deployment.
Table of contents:
Preliminary declarations
#include <stdio.h>
#include "msg/msg.h"
#include "xbt/sysdep.h"
#include "xbt/log.h"
#include "xbt/asserts.h"
"Messages specific for this msg example");
int master(int argc, char *argv[]);
int slave(int argc, char *argv[]);
int forwarder(int argc, char *argv[]);
const char *application_file);
#define FINALIZE ((void*)221297)
int master(int argc, char *argv[])
{
int slaves_count = 0;
int number_of_tasks = 0;
double task_comp_size = 0;
double task_comm_size = 0;
int i;
_XBT_GNUC_UNUSED int res = sscanf(argv[1], "%d", &number_of_tasks);
res = sscanf(argv[2], "%lg", &task_comp_size);
xbt_assert(res,
"Invalid argument %s\n", argv[2]);
res = sscanf(argv[3], "%lg", &task_comm_size);
xbt_assert(res,
"Invalid argument %s\n", argv[3]);
{
char sprintf_buffer[64];
for (i = 0; i < number_of_tasks; i++) {
sprintf(sprintf_buffer, "Task_%d", i);
todo[i] =
NULL);
}
Master code
This function has to be assigned to a msg_process_t that will behave as the master. It should not be called directly but either given as a parameter to MSG_process_create() or registered as a public function through MSG_function_register() and then automatically assigned to a process through MSG_launch_application().
C style arguments (argc/argv) are interpreted as:
- the number of tasks to distribute
- the computation size of each task
- the size of the files associated to each task
- a list of host that will accept those tasks.
Tasks are dumbly sent in a round-robin style.
}
{
slaves_count = argc - 4;
for (i = 4; i < argc; i++) {
slaves[i - 4] = MSG_get_host_by_name(argv[i]);
xbt_assert(slaves[i - 4] != NULL,
"Unknown host %s. Stopping Now! ",
argv[i]);
}
}
XBT_INFO(
"Got %d slaves and %d tasks to process", slaves_count,
number_of_tasks);
for (i = 0; i < slaves_count; i++)
for (i = 0; i < number_of_tasks; i++) {
}
}
("All tasks have been dispatched. Let's tell everybody the computation is over.");
for (i = 0; i < slaves_count; i++) {
}
free(slaves);
free(todo);
return 0;
}
Slave code
This function has to be assigned to a msg_process_t that has to behave as a slave. Just like the master fuction (described in Master code), it should not be called directly.
This function keeps waiting for tasks and executes them as it receives them.
int slave(int argc, char *argv[])
{
_XBT_GNUC_UNUSED int res;
while (1) {
break;
}
task = NULL;
}
return 0;
}
Forwarder code
This function has to be assigned to a msg_process_t that has to behave as a forwarder. Just like the master function (described in Master code), it should not be called directly.
C style arguments (argc/argv) are interpreted as a list of host that will accept those tasks.
This function keeps waiting for tasks and dispathes them to its slaves.
int forwarder(int argc, char *argv[])
{
int i;
int slaves_count;
{
slaves_count = argc - 1;
for (i = 1; i < argc; i++) {
slaves[i - 1] = MSG_get_host_by_name(argv[i]);
if (slaves[i - 1] == NULL) {
XBT_INFO(
"Unknown host %s. Stopping Now! ", argv[i]);
abort();
}
}
}
i = 0;
while (1) {
int a;
("All tasks have been dispatched. Let's tell everybody the computation is over.");
for (i = 0; i < slaves_count; i++)
break;
}
i++;
} else {
}
}
xbt_free(slaves);
return 0;
}
Simulation core
This function is the core of the simulation and is divided only into 3 parts thanks to MSG_create_environment() and MSG_launch_application().
- Simulation settings : MSG_create_environment() creates a realistic environment
- Application deployment : create the processes on the right locations with MSG_launch_application()
- The simulation is run with MSG_main()
Its arguments are:
- platform_file: the name of a file containing an valid surfxml platform description.
- application_file: the name of a file containing a valid surfxml application description
const char *application_file)
{
{
}
{
}
return res;
}
Main() function
This initializes MSG, runs a simulation, and free all data-structures created by MSG.
int main(int argc, char *argv[])
{
if (argc < 3) {
printf("Usage: %s platform_file deployment_file\n", argv[0]);
printf("example: %s msg_platform.xml msg_deployment.xml\n", argv[0]);
exit(1);
}
res = test_all(argv[1], argv[2]);
return 0;
else
return 1;
}
Helping files
Example of application file
<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "http://simgrid.gforge.inria.fr/simgrid.dtd">
<platform version="3">
<!-- The master process (with some arguments) -->
<process host="Tremblay" function="master">
<argument value="20"/> <!-- Number of tasks -->
<argument value="50000000"/> <!-- Computation size of tasks -->
<argument value="1000000"/> <!-- Communication size of tasks -->
<argument value="Jupiter"/> <!-- First slave -->
<argument value="Fafard"/> <!-- Second slave -->
<argument value="Ginette"/> <!-- Third slave -->
<argument value="Bourassa"/> <!-- Last slave -->
<argument value="Tremblay"/> <!-- Me! I can work too! -->
</process>
<!-- The slave process (with no argument) -->
<process host="Tremblay" function="slave" on_failure="RESTART"/>
<process host="Jupiter" function="slave" on_failure="RESTART"/>
<process host="Fafard" function="slave" on_failure="RESTART"/>
<process host="Ginette" function="slave" on_failure="RESTART"/>
<process host="Bourassa" function="slave" on_failure="RESTART"/>
</platform>
Example of platform file
<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "http://simgrid.gforge.inria.fr/simgrid.dtd">
<platform version="3">
<AS id="AS0" routing="Full">
<!-- ljlkj -->
<host id="Tremblay" power="98095000"/>
<host id="Jupiter" power="76296000"/>
<host id="Fafard" power="76296000"/>
<host id="Ginette" power="48492000"/>
<host id="Bourassa" power="48492000"/>
<link id="6" bandwidth="41279125" latency="5.9904e-05"/>
<link id="11" bandwidth="252750" latency="0.00570455"/>
<link id="3" bandwidth="34285625" latency="0.000514433"/>
<link id="7" bandwidth="11618875" latency="0.00018998"/>
<link id="9" bandwidth="7209750" latency="0.001461517"/>
<link id="12" bandwidth="1792625" latency="0.007877863"/>
<link id="2" bandwidth="118682500" latency="0.000136931"/>
<link id="8" bandwidth="8158000" latency="0.000270544"/>
<link id="1" bandwidth="34285625" latency="0.000514433"/>
<link id="4" bandwidth="10099625" latency="0.00047978"/>
<link id="0" bandwidth="41279125" latency="5.9904e-05"/>
<link id="10" bandwidth="4679750" latency="0.000848712"/>
<link id="5" bandwidth="27946250" latency="0.000278066"/>
<link id="loopback" bandwidth="498000000" latency="0.000015" sharing_policy="FATPIPE"/>
<route src="Tremblay" dst="Tremblay"><link_ctn id="loopback"/></route>
<route src="Jupiter" dst="Jupiter"><link_ctn id="loopback"/></route>
<route src="Fafard" dst="Fafard"><link_ctn id="loopback"/></route>
<route src="Ginette" dst="Ginette"><link_ctn id="loopback"/></route>
<route src="Bourassa" dst="Bourassa"><link_ctn id="loopback"/></route>
<route src="Tremblay" dst="Jupiter">
<link_ctn id="9"/>
</route>
<route src="Tremblay" dst="Fafard">
<link_ctn id="4"/><link_ctn id="3"/><link_ctn id="2"/><link_ctn id="0"/><link_ctn id="1"/><link_ctn id="8"/>
</route>
<route src="Tremblay" dst="Ginette">
<link_ctn id="4"/><link_ctn id="3"/><link_ctn id="5"/>
</route>
<route src="Tremblay" dst="Bourassa">
<link_ctn id="4"/><link_ctn id="3"/><link_ctn id="2"/><link_ctn id="0"/><link_ctn id="1"/><link_ctn id="6"/><link_ctn id="7"/>
</route>
<route src="Jupiter" dst="Fafard">
<link_ctn id="9"/><link_ctn id="4"/><link_ctn id="3"/><link_ctn id="2"/><link_ctn id="0"/><link_ctn id="1"/><link_ctn id="8"/>
</route>
<route src="Jupiter" dst="Ginette">
<link_ctn id="9"/><link_ctn id="4"/><link_ctn id="3"/><link_ctn id="5"/>
</route>
<route src="Jupiter" dst="Bourassa">
<link_ctn id="9"/><link_ctn id="4"/><link_ctn id="3"/><link_ctn id="2"/><link_ctn id="0"/><link_ctn id="1"/><link_ctn id="6"/><link_ctn id="7"/>
</route>
<route src="Fafard" dst="Ginette">
<link_ctn id="8"/><link_ctn id="1"/><link_ctn id="0"/><link_ctn id="2"/><link_ctn id="5"/>
</route>
<route src="Fafard" dst="Bourassa">
<link_ctn id="8"/><link_ctn id="6"/><link_ctn id="7"/>
</route>
<route src="Ginette" dst="Bourassa">
<link_ctn id="5"/><link_ctn id="2"/><link_ctn id="0"/><link_ctn id="1"/><link_ctn id="6"/><link_ctn id="7"/>
</route>
</AS>
</platform>
Using GRAS
Here are some examples on how to use GRAS.
You should also check our online tutorial section that contains a dedicated tutorial.
There is for now rather few examples of GRAS, but it's better than nothing, isn't it?
Ping-Pong
This example implements the very classical ping-pong in GRAS. It involves a client (initiating the ping-pong) and a server (answering to client's requests).
It works the following way:
- Both the client and the server register all needed messages
- The server registers a callback to the ping message, which sends pong to the expeditor
- The client sends the ping message to the server, and waits for the pong message as an answer.
This example resides in the examples/gras/ping/ping.c file. Yes, both the code of the client and of the server is placed in the same file.
Table of contents of the ping example
1) Common code to the client and the server
1.a) Initial settings
Let's first load the module header and declare a logging category (see Logging support for more info on logging).
The module header ping.h reads:
#include "gras.h"
void ping_register_messages(void);
int server(int argc, char *argv[]);
int client(int argc, char *argv[]);
1.b) Register the messages
This function, called by both the client and the server is in charge of declaring the existing messages to GRAS. Since the payload does not involve any newly created types but only int, this is quite easy. (to exchange more complicated types, see Data description or A simple RPC for matrix multiplication for an example).
void ping_register_messages(void)
{
}
[Back to Table of contents of the ping example]
2) Server's code
2.a) The server's globals
In order to ensure the communication between the "main" and the callback of the server, we need to declare some globals. We have to put them in a struct definition so that they can be handled properly in GRAS.
typedef struct {
int endcondition;
} server_data_t;
2.b) The callback to the ping message
Here is the callback run when the server receives any ping message (this will be registered later by the server).
{
int msg = *(int *) payload;
globals->endcondition = 0;
XBT_INFO(
">>>>>>>> Got message PING(%d) from %s:%d <<<<<<<<",
msg,
msg = 4321;
}
RETHROWF(
"Unable answer with PONG: %s");
}
XBT_INFO(
">>>>>>>> Answered with PONG(4321) <<<<<<<<");
globals->endcondition = 1;
return 0;
}
2.c) The "main" of the server
This is the "main" of the server. You must not write any main() function yourself. Instead, you just have to write a regular function like this one which will act as a main.
int server(int argc, char *argv[])
{
server_data_t *globals;
int port = 4000;
if (argc == 2) {
port = atoi(argv[1]);
}
XBT_INFO(
"Launch server (port=%d)", port);
ping_register_messages();
ping_register_messages();
XBT_INFO(
">>>>>>>> Listening on port %d <<<<<<<<",
globals->endcondition = 0;
if (!globals->endcondition)
("An error occured, the endcondition was not set by the callback");
free(globals);
return 0;
}
[Back to Table of contents of the ping example]
3) Client's code
3.a) Client's "main" function
This function is quite straightforward, and the inlined comments should be enough to understand it.
static xbt_socket_t try_gras_socket_client(
const char *host,
int port)
{
}
RETHROWF(
"Unable to connect to the server: %s");
}
return sock;
}
int client(int argc, char *argv[])
{
int ping, pong;
const char *host = "127.0.0.1";
int port = 4000;
if (argc == 3) {
host = argv[1];
port = atoi(argv[2]);
}
XBT_INFO(
"Launch client (server on %s:%d)", host, port);
while (!(toserver = try_gras_socket_client(host, port)))
XBT_INFO(
"Connected to %s:%d.", host, port);
ping_register_messages();
XBT_INFO(
">>>>>>>> Connected to server which is on %s:%d <<<<<<<<",
ping = 1234;
}
RETHROWF(
"Failed to send PING to server: %s");
}
XBT_INFO(
">>>>>>>> Message PING(%d) sent to %s:%d <<<<<<<<",
}
RETHROWF(
"Why can't I get my PONG message like everyone else: %s");
}
XBT_INFO(
">>>>>>>> Got PONG(%d) from %s:%d <<<<<<<<",
return 0;
}
[Back to Table of contents of the ping example]
Token Ring example
This example implements the token ring algorithm. It involves several nodes arranged in a ring (each of them have a left and a right neighbour) and exchanging a "token". This algorithm is one of the solution to ensure the mutual exclusion between distributed processes. There is only one token at any time, so the process in its possession is ensured to be the only one having it. So, if there is an action you want all processes to do alternativly, but you cannot afford to have two processes doing it at the same time, let the process having the token doing it.
Actually, there is a lot of different token ring algorithms in the litterature, so this example implements one of them: the simplest one. The ring is static (no new node can join it, and you'll get trouble if one node dies or leaves), and nothing is done for the case in which the token is lost.
1) Deployment file
Here is the deployment file:
<?xml version='1.0'?>
<!DOCTYPE platform SYSTEM "http://simgrid.gforge.inria.fr/simgrid.dtd">
<platform version="3">
<process host="Tremblay" function="node">
<argument value="4000"/> <!-- port on which I am listening -->
<argument value="Fafard"/><argument value="4000"/> <!-- peer (successor) host id and port-->
<argument value="--create-token"/> <!-- I'm first client, ie I have to create the token -->
</process>
<process host="Fafard" function="
node">
<argument value="4000"/> <!-- port on which I am listening -->
<argument value="Jupiter"/><argument value="4000"/><!-- peer (successor) host id and port-->
</process>
<process host="Jupiter" function="
node">
<argument value="4000"/> <!-- port on which I am listening -->
<argument value="Ginette"/><argument value="4000"/> <!-- peer (successor) host id and port-->
</process>
<process host="Ginette" function="
node">
<argument value="4000"/> <!-- port on which I am listening -->
<argument value="Bourassa"/><argument value="4000"/><!-- peer (successor) host id and port-->
</process>
<process host="Bourassa" function="
node">
<argument value="4000"/> <!-- port on which I am listening -->
<argument value="Tremblay"/><argument value="4000"/><!-- peer (successor) host id and port-->
</process>
</platform>
The neighbour of each node is given at startup as command line argument. Moreover, one of the nodes is instructed by a specific argument (the one on Tremblay here) to create the token at the begining of the algorithm.
2) Global definition
The token is incarned by a specific message, which circulates from node to node (the payload is an integer incremented at each hop). So, the most important part of the code is the message callback, which forwards the message to the next node. That is why we have to store all variable in a global, as explained in the Globals section.
typedef struct {
int remaining_loop;
int create;
double start_time;
} node_data_t;
3) The callback
Even if this is the core of this algorithm, this function is quite straightforward.
{
int msg = *(int *) payload;
int supersteps = 1;
if (NBLOOPS >= 1000) {
supersteps = 100;
} else if (NBLOOPS >= 100) {
supersteps = 10;
} else if (NBLOOPS <= 10) {
supersteps = 1;
}
if (globals->create && (!(globals->remaining_loop % supersteps))) {
XBT_INFO(
"Begin a new loop. Still to do: %d", globals->remaining_loop);
} else if (!(globals->remaining_loop % supersteps)) {
XBT_VERB(
"Got token(%d) from %s remaining_loop=%d",
}
if (globals->remaining_loop > 0) {
msg += 1;
}
RETHROWF(
"Unable to forward token: %s");
}
}
globals->remaining_loop -= 1;
if (globals->remaining_loop == -1 && globals->create) {
XBT_INFO(
"Shut down the token-ring. There was %d hops.", msg);
}
return 0;
}
4) The main function
This function is splited in two parts: The first one performs all the needed initialisations (points 1-7) while the end (point 8. below) calls gras_msg_handle() as long as the planned amount of ring loops are not performed.
int node(
int argc,
char *argv[])
{
node_data_t *globals;
const char *host;
int myport;
int peerport;
host = "127.0.0.1";
myport = 4000;
peerport = 4000;
if (argc >= 4) {
myport = atoi(argv[1]);
host = argv[2];
peerport = atoi(argv[3]);
}
globals->remaining_loop = NBLOOPS;
globals->create = 0;
globals->tosuccessor = NULL;
XBT_INFO(
"Launch node listening on %d (successor on %s:%d)",
myport, host, peerport);
XBT_DEBUG(
"Connect to my successor on %s:%d", host, peerport);
if (argc >= 5
&& !strncmp("--create-token", argv[4], strlen("--create-token")))
globals->create = 1;
if (globals->create) {
int token = 0;
globals->remaining_loop = NBLOOPS - 1;
XBT_INFO(
"Create the token (with value %d) and send it to %s:%d",
token, host, peerport);
}
RETHROWF(
"Unable to send the freshly created token: %s");
}
}
while (globals->remaining_loop > (globals->create ? -1 : 0)) {
XBT_DEBUG(
"looping (remaining_loop=%d)", globals->remaining_loop);
}
free(globals);
return 0;
}
A simple RPC for matrix multiplication
This example implements a remote matrix multiplication. It involves a client (creating the matrices and sending the multiplications requests) and a server (computing the multiplication on client's behalf).
This example also constitutes a more advanced example of data description mechanisms, since the message payload type is a bit more complicated than in other examples such as the ping one (Ping-Pong).
It works the following way (not very different from the ping example):
- Both the client and the server register all needed messages and datatypes
- The server registers a callback to the "request" message, which computes what needs to be and returns the result to the expeditor.
- The client creates two matrices, ask for their multiplication and check the server's answer.
This example resides in the examples/gras/mmrpc/mmrpc.c file.
Table of contents of the mmrpc example
1) Common code to the client and the server (mmrpc_common.c and mmrpc.h)
1.a) Module header (mmrpc.h)
This loads the gras header and declare the function's prototypes as well as the matrix size.
#include "gras.h"
#define MATSIZE 128
void mmrpc_register_messages(void);
int server(int argc, char *argv[]);
int client(int argc, char *argv[]);
1.b) Register the data types (mmrpc.h)
The messages involved in a matrix of double. This type is automatically known by the GRAS mecanism, using the gras_datadesc_matrix() function of the xbt/matrix module.
1.c) Logging category definition (mmrpc_common.c)
Let's first load the module header and declare a logging category (see Logging support for more info on logging). This logging category does live in this file (ie the required symbols are defined here and declared as "extern" in any other file using them). That is why we use XBT_LOG_NEW_DEFAULT_CATEGORY here and XBT_LOG_EXTERNAL_DEFAULT_CATEGORY in mmrpc_client.c and mmrpc_server.c.
1.d) Register the messages (mmrpc_common.c)
This function, called by both the client and the server is in charge of declaring the existing messages to GRAS.
The datatype description builded that way can then be used to build an array datatype or to declare messages.
void mmrpc_register_messages(void)
{
matrix_type =
request_type =
}
[Back to Table of contents of the mmrpc example]
2) Server's code (mmrpc_server.c)
2.a) Server intial settings
All module symbols live in the mmrpc_common.c file. We thus have to define XBT_DEFINE_TYPE_EXTERN to the preprocessor so that the XBT_DEFINE_TYPE symbols don't get included here. Likewise, we use XBT_LOG_EXTERNAL_DEFAULT_CATEGORY to get the log category in here.
#define XBT_DEFINE_TYPE_EXTERN
#include "xbt/matrix.h"
#include "mmrpc.h"
2.b) The callback to the mmrpc message
Here is the callback run when the server receives any mmrpc message (this will be registered later by the server). Note the way we get the message payload. In the ping example, there was one additional level of pointer indirection (see 2.b) The callback to the ping message). This is because the payload is an array here (ie a pointer) whereas it is a scalar in the ping example.
void *payload_data)
{
xbt_matrix_t *request = (xbt_matrix_t *) payload_data;
xbt_matrix_t result;
result = xbt_matrix_double_new_mult(request[0], request[1]);
xbt_matrix_free(request[0]);
xbt_matrix_free(request[1]);
xbt_matrix_free(result);
return 0;
}
2.c) The "main" of the server
This is the "main" of the server. You must not write any main() function yourself. Instead, you just have to write a regular function like this one which will act as a main.
{
}
RETHROWF(
"Unable to establish a server socket: %s");
}
return sock;
}
int server(int argc, char *argv[])
{
int port = 4002;
if (argc == 2) {
port = atoi(argv[1]);
}
mmrpc_register_messages();
XBT_INFO(
"Launch server (port=%d)", port);
sock = try_gras_socket_server(port);
return 0;
}
[Back to Table of contents of the mmrpc example]
3) Client's code (mmrpc_client.c)
2.a) Server intial settings
As for the server, some extra love is needed to make sure that automatic datatype parsing and log categories do work even if we are using several files.
#define XBT_DEFINE_TYPE_EXTERN
#include "xbt/matrix.h"
#include "mmrpc.h"
3.b) Client's "main" function
This function is quite straightforward, and the inlined comments should be enough to understand it.
int client(int argc, char *argv[])
{
xbt_matrix_t request[2], answer;
int i;
_XBT_GNUC_UNUSED int j;
const char *host = "127.0.0.1";
int port = 4000;
if (argc == 3) {
host = argv[1];
port = atoi(argv[2]);
}
XBT_INFO(
"Launch client (server on %s:%d)", host, port);
mmrpc_register_messages();
while (!(toserver = try_gras_socket_client(host, port)))
XBT_INFO(
"Connected to %s:%d.", host, port);
XBT_INFO(
">>>>>>>> Connected to server which is on %s:%d <<<<<<<<",
request[0] = xbt_matrix_double_new_id(MATSIZE, MATSIZE);
request[1] = xbt_matrix_double_new_rand(MATSIZE, MATSIZE);
xbt_matrix_free(request[0]);
XBT_INFO(
">>>>>>>> Request sent to %s:%d <<<<<<<<",
for (i = 0; i < MATSIZE; i++)
for (j = 0; i < MATSIZE; i++)
xbt_assert(xbt_matrix_get_as(answer, i, j,
double) ==
xbt_matrix_get_as(request[1], i, j, double),
"Answer does not match expectations. Found %f at cell %d,%d instead of %f",
xbt_matrix_get_as(answer, i, j, double), i, j,
xbt_matrix_get_as(request[1], i, j, double));
XBT_INFO(
">>>>>>>> Got answer from %s:%d (values are right) <<<<<<<<",
xbt_matrix_free(request[1]);
xbt_matrix_free(answer);
return 0;
}
[Back to Table of contents of the mmrpc example]
Some timer games
This example fools around with the GRAS timers (Timers). It is mainly a regression test, since it uses almost all timer features.
The main program registers a repetititive task and a delayed one, and then loops until the still_to_do variables of its globals reach 0. The delayed task set it to 5, and the repetititive one decrease it each time. Here is an example of output:
Initialize GRAS
Initialize XBT
[1108335471] Programming the repetitive_action with a frequency of 1.000000 sec
[1108335471] Programming the delayed_action for after 2.000000 sec
[1108335471] Have a rest
[1108335472] Canceling the delayed_action.
[1108335472] Re-programming the delayed_action for after 2.000000 sec
[1108335472] Repetitive_action has nothing to do yet
[1108335473] Repetitive_action has nothing to do yet
[1108335473] delayed_action setting globals->still_to_do to 5
[1108335474] repetitive_action decrementing globals->still_to_do. New value: 4
[1108335475] repetitive_action decrementing globals->still_to_do. New value: 3
[1108335476] repetitive_action decrementing globals->still_to_do. New value: 2
[1108335477] repetitive_action decrementing globals->still_to_do. New value: 1
[1108335478] repetitive_action decrementing globals->still_to_do. New value: 0
Exiting GRAS
Source code:
1. Declarations and headers
#include "gras.h"
#define REPEAT_INTERVAL 1.0
#define DELAY_INTERVAL 2.0
#define LOOP_COUNT 5
typedef struct {
int still_to_do;
} my_globals;
2. Source code of the delayed action
static void repetitive_action(void)
{
if (globals->still_to_do <= 0) {
XBT_INFO(
"Repetitive_action has nothing to do yet");
return;
}
if (globals->still_to_do == 1) {
}
("repetitive_action decrementing globals->still_to_do. New value: %d",
globals->still_to_do - 1);
globals->still_to_do--;
}
3. Source code of the repetitive action
static void delayed_action(void)
{
XBT_INFO(
"delayed_action setting globals->still_to_do to %d", LOOP_COUNT);
globals->still_to_do = LOOP_COUNT;
}
4. Source code of main function
int client(int argc, char *argv[])
{
my_globals *globals;
globals->still_to_do = -1;
XBT_INFO(
"Programming the repetitive_action with a frequency of %f sec",
REPEAT_INTERVAL);
XBT_INFO(
"Programming the delayed_action for after %f sec", DELAY_INTERVAL);
XBT_INFO(
"Canceling the delayed_action.");
XBT_INFO(
"Re-programming the delayed_action for after %f sec",
DELAY_INTERVAL);
while (globals->still_to_do == -1 ||
globals->still_to_do >
0 ) {
XBT_DEBUG(
"Prepare to handle messages for 5 sec (still_to_do=%d)",
globals->still_to_do);
}
xbt_free(globals);
return 0;
}
Using SMPI
You should check our online tutorial section that contains a dedicated tutorial.
Using Model Checking
You should check our online tutorial section that contains a dedicated tutorial.
Using Trace
Check out the Tracing Simulations for Visualization section.
You should check our online tutorial section that contains a dedicated tutorial.
Using SimDag
You should check our online tutorial section that contains a dedicated tutorial.
Using SIMIX
You should check our online tutorial section that contains a dedicated tutorial.