Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
a8963ea681ea35f0942dc6d1a1eaf107a28a1eb9
[simgrid.git] / examples / msg / actions / actions.c
1 /* Copyright (c) 2009, 2010. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include "msg/msg.h"            /* Yeah! If you want to use msg, you need to include msg/msg.h */
10 #include "msg/mailbox.h"        /* we play funny tricks with mailboxes and rdv points */
11 #include "simix/simix.h"        /* semaphores for the barrier */
12 #include "xbt.h"                /* calloc, printf */
13 #include "instr/instr_private.h"
14
15 #include "msg/private.h" /* You don't want to know why, trust us */
16 #include "simix/private.h"
17
18 void SIMIX_ctx_raw_factory_init(smx_context_factory_t *factory);
19
20 XBT_LOG_NEW_DEFAULT_CATEGORY(actions,
21                              "Messages specific for this msg example");
22 int communicator_size = 0;
23
24 static void action_Isend(const char *const *action);
25
26 typedef struct  {
27   int last_Irecv_sender_id;
28   int bcast_counter;
29   int reduce_counter;
30   int allReduce_counter;
31   xbt_dynar_t isends; /* of msg_comm_t */
32   /* Used to implement irecv+wait */
33   xbt_dynar_t irecvs; /* of msg_comm_t */
34   xbt_dynar_t tasks; /* of m_task_t */
35 } s_process_globals_t, *process_globals_t;
36
37 /* Helper function */
38 static double parse_double(const char *string)
39 {
40   double value;
41   char *endptr;
42
43   value = strtod(string, &endptr);
44   if (*endptr != '\0')
45     THROW1(unknown_error, 0, "%s is not a double", string);
46   return value;
47 }
48
49 static int get_rank (const char *process_name)
50 {
51   return atoi(&(process_name[1]));
52
53
54 static void asynchronous_cleanup(void) {
55   process_globals_t globals = (process_globals_t) MSG_process_get_data(MSG_process_self());
56
57   /* Destroy any isend which correspond to completed communications */
58   int found;
59   msg_comm_t comm;
60   while ((found = MSG_comm_testany(globals->isends)) != -1) {
61     xbt_dynar_remove_at(globals->isends,found,&comm);
62     MSG_comm_destroy(comm);
63   }
64 }
65
66 /* My actions */
67 static void action_send(const char *const *action)
68 {
69   char *name = NULL;
70   char to[250];
71   const char *size_str = action[3];
72   double size=parse_double(size_str);
73   double clock = MSG_get_clock(); /* this "call" is free thanks to inlining */
74
75   sprintf(to, "%s_%s", MSG_process_get_name(MSG_process_self()),action[2]);
76
77   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
78     name = xbt_str_join_array(action, " ");
79
80 #ifdef HAVE_TRACING
81   int rank = get_rank(MSG_process_get_name(MSG_process_self()));
82   int dst_traced = get_rank(action[2]);
83   TRACE_smpi_ptp_in(rank, rank, dst_traced, "send");
84   TRACE_smpi_send(rank, rank, dst_traced);
85 #endif
86
87   XBT_DEBUG("Entering Send: %s (size: %lg)", name, size);
88    if (size<65536) {
89      action_Isend(action);
90    } else {
91      MSG_task_send(MSG_task_create(name, 0, size, NULL), to);
92    }
93    
94    XBT_VERB("%s %f", name, MSG_get_clock() - clock);
95
96   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
97     free(name);
98
99 #ifdef HAVE_TRACING
100   TRACE_smpi_ptp_out(rank, rank, dst_traced, "send");
101 #endif
102
103   asynchronous_cleanup();
104 }
105
106 static void action_Isend(const char *const *action)
107 {
108   char to[250];
109   const char *size = action[3];
110   double clock = MSG_get_clock();
111   process_globals_t globals = (process_globals_t) MSG_process_get_data(MSG_process_self());
112
113
114   sprintf(to, "%s_%s", MSG_process_get_name(MSG_process_self()),action[2]);
115   m_task_t task = MSG_task_create(to,0,parse_double(size),NULL);
116   msg_comm_t comm = MSG_task_isend_with_matching(task, to, /*matching madness*/NULL,task);
117   xbt_dynar_push(globals->isends,&comm);
118
119   if (task->simdata->message_size < 65536) {
120     /* Close your eyes, it burns ! */
121     comm->s_comm->comm.dst_proc = SIMIX_process_get_by_name(action[2]);
122     comm->s_comm->comm.dst_buff = NULL;
123     comm->s_comm->comm.dst_buff_size = NULL;
124     comm->s_comm->comm.dst_data = NULL;
125     comm->s_comm->state = SIMIX_READY;
126     comm->s_comm->comm.refcount++;
127     SIMIX_comm_start(comm->s_comm);
128   }
129
130   XBT_DEBUG("Isend on %s", MSG_process_get_name(MSG_process_self()));
131   XBT_VERB("%s %f", xbt_str_join_array(action, " "), MSG_get_clock() - clock);
132
133   asynchronous_cleanup();
134 }
135
136 static int task_matching(void*ignored,void*sent_task) {
137   m_task_t t = (m_task_t)sent_task;
138   if (t!=NULL && MSG_task_get_data_size(t)<65536)
139     return 1; /* that's supposed to be already arrived */
140   return 0; /* rendez-vous mode: it's not there yet */
141 }
142
143 static void action_recv(const char *const *action)
144 {
145   char *name = NULL;
146   char mailbox_name[250];
147   m_task_t task = NULL;
148   double clock = MSG_get_clock();
149
150   sprintf(mailbox_name, "%s_%s", action[2],
151           MSG_process_get_name(MSG_process_self()));
152
153   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
154     name = xbt_str_join_array(action, " ");
155
156   /* The next chunk is to deal with the fact that for short messages,
157    * if the send occurs before the receive, the message is already sent and
158    * buffered on receiver side when the recv() occurs.
159    *
160    * So the next chunk detects this fact and cancel the simix communication instead.
161    */
162
163   /* make sure the rdv is created on need by asking to MSG instead of simix directly */
164   smx_rdv_t rdv = MSG_mailbox_get_by_alias(mailbox_name);
165   smx_action_t act = SIMIX_comm_get_send_match(rdv, task_matching, NULL);
166   if (act!=NULL){
167     /* FIXME account for the memcopy time if needed */
168     task = act->comm.src_data;
169
170     if (task->simdata->message_size < 65536) {
171       act->comm.refcount--; /* See action_send for more pain */
172       if(act->state == SIMIX_DONE)
173         SIMIX_comm_finish(act);
174       else
175         SIMIX_req_comm_wait(act, -1.0);
176       return;
177     }
178   }
179
180 #ifdef HAVE_TRACING
181   int rank = get_rank(MSG_process_get_name(MSG_process_self()));
182   int src_traced = get_rank(action[2]);
183   TRACE_smpi_ptp_in(rank, src_traced, rank, "recv");
184 #endif
185
186   XBT_DEBUG("Receiving: %s", name);
187   MSG_task_receive(&task, mailbox_name);
188   //  MSG_task_receive(&task, MSG_process_get_name(MSG_process_self()));
189   XBT_VERB("%s %f", name, MSG_get_clock() - clock);
190   MSG_task_destroy(task);
191
192   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
193     free(name);
194 #ifdef HAVE_TRACING
195   TRACE_smpi_ptp_out(rank, src_traced, rank, "recv");
196   TRACE_smpi_recv(rank, src_traced, rank);
197 #endif
198
199   asynchronous_cleanup();
200 }
201
202 static void action_Irecv(const char *const *action)
203 {
204   char mailbox[250];
205   double clock = MSG_get_clock();
206   process_globals_t globals = (process_globals_t) MSG_process_get_data(MSG_process_self());
207
208   XBT_DEBUG("Irecv on %s", MSG_process_get_name(MSG_process_self()));
209 #ifdef HAVE_TRACING
210   int rank = get_rank(MSG_process_get_name(MSG_process_self()));
211   int src_traced = get_rank(action[2]);
212   globals->last_Irecv_sender_id = src_traced;
213   MSG_process_set_data(MSG_process_self(), (void *) globals);
214
215   TRACE_smpi_ptp_in(rank, src_traced, rank, "Irecv");
216 #endif
217
218   sprintf(mailbox, "%s_%s", action[2],
219           MSG_process_get_name(MSG_process_self()));
220   m_task_t t=NULL;
221   xbt_dynar_push(globals->tasks,&t);
222   msg_comm_t c =
223       MSG_task_irecv(
224           xbt_dynar_get_ptr(globals->tasks, xbt_dynar_length(globals->tasks)-1),
225           mailbox);
226   xbt_dynar_push(globals->irecvs,&c);
227
228   XBT_VERB("%s %f", xbt_str_join_array(action, " "), MSG_get_clock() - clock);
229
230 #ifdef HAVE_TRACING
231   TRACE_smpi_ptp_out(rank, src_traced, rank, "Irecv");
232 #endif
233
234   asynchronous_cleanup();
235 }
236
237
238 static void action_wait(const char *const *action)
239 {
240   char *name = NULL;
241   m_task_t task = NULL;
242   msg_comm_t comm;
243   double clock = MSG_get_clock();
244   process_globals_t globals = (process_globals_t) MSG_process_get_data(MSG_process_self());
245
246   xbt_assert1(xbt_dynar_length(globals->irecvs),
247       "action wait not preceded by any irecv: %s", xbt_str_join_array(action," "));
248
249   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
250     name = xbt_str_join_array(action, " ");
251 #ifdef HAVE_TRACING
252   process_globals_t counters = (process_globals_t) MSG_process_get_data(MSG_process_self());
253   int src_traced = counters->last_Irecv_sender_id;
254   int rank = get_rank(MSG_process_get_name(MSG_process_self()));
255   TRACE_smpi_ptp_in(rank, src_traced, rank, "wait");
256 #endif
257
258   XBT_DEBUG("Entering %s", name);
259   comm = xbt_dynar_pop_as(globals->irecvs,msg_comm_t);
260   MSG_comm_wait(comm,-1);
261   task = xbt_dynar_pop_as(globals->tasks,m_task_t);
262   MSG_comm_destroy(comm);
263   MSG_task_destroy(task);
264
265   XBT_VERB("%s %f", name, MSG_get_clock() - clock);
266   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
267     free(name);
268 #ifdef HAVE_TRACING
269   TRACE_smpi_ptp_out(rank, src_traced, rank, "wait");
270   TRACE_smpi_recv(rank, src_traced, rank);
271 #endif
272
273 }
274
275 /* FIXME: that's a poor man's implementation: we should take the message exchanges into account */
276 static void action_barrier(const char *const *action)
277 {
278   char *name = NULL;
279   static smx_mutex_t mutex = NULL;
280   static smx_cond_t cond = NULL;
281   static int processes_arrived_sofar=0;
282
283   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
284     name = xbt_str_join_array(action, " ");
285
286   if (mutex == NULL) {       // first arriving on the barrier
287     mutex = SIMIX_req_mutex_init();
288     cond = SIMIX_req_cond_init();
289     processes_arrived_sofar=0;
290   }
291   XBT_DEBUG("Entering barrier: %s (%d already there)", name,processes_arrived_sofar);
292
293   SIMIX_req_mutex_lock(mutex);
294   if (++processes_arrived_sofar == communicator_size) {
295     SIMIX_req_cond_broadcast(cond);
296     SIMIX_req_mutex_unlock(mutex);
297   } else {
298     SIMIX_req_cond_wait(cond,mutex);
299     SIMIX_req_mutex_unlock(mutex);
300   }
301
302   XBT_DEBUG("Exiting barrier: %s", name);
303
304   processes_arrived_sofar--;
305   if (!processes_arrived_sofar) {
306     SIMIX_req_cond_destroy(cond);
307     SIMIX_req_mutex_destroy(mutex);
308     mutex=NULL;
309   }
310
311   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
312     free(name);
313
314 }
315
316 static void action_reduce(const char *const *action)
317 {
318         int i;
319         char *reduce_identifier;
320         char mailbox[80];
321         double comm_size = parse_double(action[2]);
322         double comp_size = parse_double(action[3]);
323         m_task_t comp_task = NULL;
324         const char *process_name;
325         double clock = MSG_get_clock();
326
327         process_globals_t counters = (process_globals_t) MSG_process_get_data(MSG_process_self());
328
329         xbt_assert0(communicator_size, "Size of Communicator is not defined, "
330                         "can't use collective operations");
331
332         process_name = MSG_process_get_name(MSG_process_self());
333
334         reduce_identifier = bprintf("reduce_%d", counters->reduce_counter++);
335
336         if (!strcmp(process_name, "p0")) {
337                 XBT_DEBUG("%s: %s is the Root", reduce_identifier, process_name);
338
339                 msg_comm_t *comms = xbt_new0(msg_comm_t,communicator_size-1);
340             m_task_t *tasks = xbt_new0(m_task_t,communicator_size-1);
341             for (i = 1; i < communicator_size; i++) {
342               sprintf(mailbox, "%s_p%d_p0", reduce_identifier, i);
343               comms[i-1] = MSG_task_irecv(&(tasks[i-1]),mailbox);
344             }
345             MSG_comm_waitall(comms,communicator_size-1,-1);
346             for (i = 1; i < communicator_size; i++) {
347                 MSG_comm_destroy(comms[i-1]);
348                 MSG_task_destroy(tasks[i-1]);
349             }
350             free(tasks);
351
352             comp_task = MSG_task_create("reduce_comp", comp_size, 0, NULL);
353             XBT_DEBUG("%s: computing 'reduce_comp'", reduce_identifier);
354             MSG_task_execute(comp_task);
355             MSG_task_destroy(comp_task);
356             XBT_DEBUG("%s: computed", reduce_identifier);
357
358         } else {
359                 XBT_DEBUG("%s: %s sends", reduce_identifier, process_name);
360                 sprintf(mailbox, "%s_%s_p0", reduce_identifier, process_name);
361             XBT_DEBUG("put on %s", mailbox);
362             MSG_task_send(MSG_task_create(reduce_identifier, 0, comm_size, NULL),
363                           mailbox);
364         }
365
366         XBT_VERB("%s %f", xbt_str_join_array(action, " "), MSG_get_clock() - clock);
367         free(reduce_identifier);
368 }
369
370 static void action_bcast(const char *const *action)
371 {
372         int i;
373         char *bcast_identifier;
374         char mailbox[80];
375         double comm_size = parse_double(action[2]);
376         m_task_t task = NULL;
377         const char *process_name;
378         double clock = MSG_get_clock();
379
380         process_globals_t counters = (process_globals_t) MSG_process_get_data(MSG_process_self());
381
382         xbt_assert0(communicator_size, "Size of Communicator is not defined, "
383                         "can't use collective operations");
384
385         process_name = MSG_process_get_name(MSG_process_self());
386
387         bcast_identifier = bprintf("bcast_%d", counters->bcast_counter++);
388
389         if (!strcmp(process_name, "p0")) {
390                 XBT_DEBUG("%s: %s is the Root", bcast_identifier, process_name);
391
392             msg_comm_t *comms = xbt_new0(msg_comm_t,communicator_size-1);
393
394             for (i = 1; i < communicator_size; i++) {
395               sprintf(mailbox, "%s_p0_p%d", bcast_identifier, i);
396               comms[i-1] =
397                   MSG_task_isend(MSG_task_create(mailbox,0,comm_size,NULL),
398                       mailbox);
399             }
400             MSG_comm_waitall(comms,communicator_size-1,-1);
401                 for (i = 1; i < communicator_size; i++)
402                MSG_comm_destroy(comms[i-1]);
403             free(comms);
404
405             XBT_DEBUG("%s: all messages sent by %s have been received",
406                    bcast_identifier, process_name);
407
408         } else {
409             sprintf(mailbox, "%s_p0_%s", bcast_identifier, process_name);
410             MSG_task_receive(&task, mailbox);
411             MSG_task_destroy(task);
412             XBT_DEBUG("%s: %s has received", bcast_identifier, process_name);
413         }
414
415         XBT_VERB("%s %f", xbt_str_join_array(action, " "), MSG_get_clock() - clock);
416         free(bcast_identifier);
417 }
418
419
420 static void action_sleep(const char *const *action)
421 {
422   char *name = NULL;
423   const char *duration = action[2];
424   double clock = MSG_get_clock();
425
426   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
427     name = xbt_str_join_array(action, " ");
428
429   XBT_DEBUG("Entering %s", name);
430   MSG_process_sleep(parse_double(duration));
431   XBT_VERB("%s %f ", name, MSG_get_clock() - clock);
432
433   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
434     free(name);
435 }
436
437 static void action_allReduce(const char *const *action) {
438   int i;
439   char *allreduce_identifier;
440   char mailbox[80];
441   double comm_size = parse_double(action[2]);
442   double comp_size = parse_double(action[3]);
443   m_task_t task = NULL, comp_task = NULL;
444   const char *process_name;
445   double clock = MSG_get_clock();
446
447   process_globals_t counters = (process_globals_t) MSG_process_get_data(MSG_process_self());
448
449   xbt_assert0(communicator_size, "Size of Communicator is not defined, "
450               "can't use collective operations");
451
452   process_name = MSG_process_get_name(MSG_process_self());
453
454   allreduce_identifier = bprintf("allReduce_%d", counters->allReduce_counter++);
455
456   if (!strcmp(process_name, "p0")) {
457     XBT_DEBUG("%s: %s is the Root", allreduce_identifier, process_name);
458
459     msg_comm_t *comms = xbt_new0(msg_comm_t,communicator_size-1);
460     m_task_t *tasks = xbt_new0(m_task_t,communicator_size-1);
461     for (i = 1; i < communicator_size; i++) {
462       sprintf(mailbox, "%s_p%d_p0", allreduce_identifier, i);
463       comms[i-1] = MSG_task_irecv(&(tasks[i-1]),mailbox);
464     }
465     MSG_comm_waitall(comms,communicator_size-1,-1);
466     for (i = 1; i < communicator_size; i++) {
467       MSG_comm_destroy(comms[i-1]);
468       MSG_task_destroy(tasks[i-1]);
469     }
470     free(tasks);
471
472     comp_task = MSG_task_create("allReduce_comp", comp_size, 0, NULL);
473     XBT_DEBUG("%s: computing 'reduce_comp'", allreduce_identifier);
474     MSG_task_execute(comp_task);
475     MSG_task_destroy(comp_task);
476     XBT_DEBUG("%s: computed", allreduce_identifier);
477
478     for (i = 1; i < communicator_size; i++) {
479       sprintf(mailbox, "%s_p0_p%d", allreduce_identifier, i);
480       comms[i-1] =
481           MSG_task_isend(MSG_task_create(mailbox,0,comm_size,NULL),
482               mailbox);
483     }
484     MSG_comm_waitall(comms,communicator_size-1,-1);
485     for (i = 1; i < communicator_size; i++)
486        MSG_comm_destroy(comms[i-1]);
487     free(comms);
488
489     XBT_DEBUG("%s: all messages sent by %s have been received",
490            allreduce_identifier, process_name);
491
492   } else {
493     XBT_DEBUG("%s: %s sends", allreduce_identifier, process_name);
494     sprintf(mailbox, "%s_%s_p0", allreduce_identifier, process_name);
495     XBT_DEBUG("put on %s", mailbox);
496     MSG_task_send(MSG_task_create(allreduce_identifier, 0, comm_size, NULL),
497                   mailbox);
498
499     sprintf(mailbox, "%s_p0_%s", allreduce_identifier, process_name);
500     MSG_task_receive(&task, mailbox);
501     MSG_task_destroy(task);
502     XBT_DEBUG("%s: %s has received", allreduce_identifier, process_name);
503   }
504
505   XBT_VERB("%s %f", xbt_str_join_array(action, " "), MSG_get_clock() - clock);
506   free(allreduce_identifier);
507 }
508
509 static void action_comm_size(const char *const *action)
510 {
511   char *name = NULL;
512   const char *size = action[2];
513   double clock = MSG_get_clock();
514
515   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
516     name = xbt_str_join_array(action, " ");
517   communicator_size = parse_double(size);
518   XBT_VERB("%s %f", name, MSG_get_clock() - clock);
519   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
520     free(name);
521 }
522
523 static void action_compute(const char *const *action)
524 {
525   char *name = NULL;
526   const char *amout = action[2];
527   m_task_t task = MSG_task_create(name, parse_double(amout), 0, NULL);
528   double clock = MSG_get_clock();
529
530   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
531     name = xbt_str_join_array(action, " ");
532   XBT_DEBUG("Entering %s", name);
533   MSG_task_execute(task);
534   MSG_task_destroy(task);
535   XBT_VERB("%s %f", name, MSG_get_clock() - clock);
536   if (XBT_LOG_ISENABLED(actions, xbt_log_priority_verbose))
537     free(name);
538 }
539
540 static void action_init(const char *const *action)
541
542 #ifdef HAVE_TRACING
543   TRACE_smpi_init(get_rank(MSG_process_get_name(MSG_process_self())));
544 #endif
545   XBT_DEBUG("Initialize the counters");
546   process_globals_t globals = (process_globals_t) calloc(1, sizeof(s_process_globals_t));
547   globals->isends = xbt_dynar_new(sizeof(msg_comm_t),NULL);
548   globals->irecvs = xbt_dynar_new(sizeof(msg_comm_t),NULL);
549   globals->tasks  = xbt_dynar_new(sizeof(m_task_t),NULL);
550   MSG_process_set_data(MSG_process_self(),globals);
551
552 }
553
554 static void action_finalize(const char *const *action)
555 {
556 #ifdef HAVE_TRACING
557   TRACE_smpi_finalize(get_rank(MSG_process_get_name(MSG_process_self())));
558 #endif
559   process_globals_t globals = (process_globals_t) MSG_process_get_data(MSG_process_self());
560   if (globals){
561     xbt_dynar_free_container(&(globals->isends));
562     xbt_dynar_free_container(&(globals->irecvs));
563     xbt_dynar_free_container(&(globals->tasks));
564     free(globals);
565   }
566 }
567
568 /** Main function */
569 int main(int argc, char *argv[])
570 {
571   MSG_error_t res = MSG_OK;
572
573   smx_factory_initializer_to_use = SIMIX_ctx_raw_factory_init;
574
575   /* Check the given arguments */
576   MSG_global_init(&argc, argv);
577   if (argc < 3) {
578     printf("Usage: %s platform_file deployment_file [action_files]\n",
579            argv[0]);
580     printf
581         ("example: %s msg_platform.xml msg_deployment.xml actions # if all actions are in the same file\n",
582          argv[0]);
583     printf
584         ("example: %s msg_platform.xml msg_deployment.xml # if actions are in separate files, specified in deployment\n",
585          argv[0]);
586     exit(1);
587   }
588
589   /*  Simulation setting */
590   MSG_create_environment(argv[1]);
591
592   /* No need to register functions as in classical MSG programs: the actions get started anyway */
593   MSG_launch_application(argv[2]);
594
595   /*   Action registration */
596   MSG_action_register("init",     action_init);
597   MSG_action_register("finalize", action_finalize);
598   MSG_action_register("comm_size",action_comm_size);
599   MSG_action_register("send",     action_send);
600   MSG_action_register("Isend",    action_Isend);
601   MSG_action_register("recv",     action_recv);
602   MSG_action_register("Irecv",    action_Irecv);
603   MSG_action_register("wait",     action_wait);
604   MSG_action_register("barrier",  action_barrier);
605   MSG_action_register("bcast",    action_bcast);
606   MSG_action_register("reduce",   action_reduce);
607   MSG_action_register("allReduce",action_allReduce);
608   MSG_action_register("sleep",    action_sleep);
609   MSG_action_register("compute",  action_compute);
610
611
612   /* Actually do the simulation using MSG_action_trace_run */
613   res = MSG_action_trace_run(argv[3]);  // it's ok to pass a NULL argument here
614
615   XBT_INFO("Simulation time %g", MSG_get_clock());
616   MSG_clean();
617
618   if (res == MSG_OK)
619     return 0;
620   else
621     return 1;
622 }                               /* end_of_main */