Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
f81d6fcf8f0d0cde4c8e1734c858331fd06fb1f3
[simgrid.git] / src / smpi / smpi_global.cpp
1 /* Copyright (c) 2007-2017. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #include "mc/mc.h"
7 #include "private.h"
8 #include "private.hpp"
9 #include "simgrid/s4u/Mailbox.hpp"
10 #include "smpi/smpi_shared_malloc.hpp"
11 #include "simgrid/sg_config.h"
12 #include "src/kernel/activity/SynchroComm.hpp"
13 #include "src/mc/mc_record.h"
14 #include "src/mc/mc_replay.h"
15 #include "src/msg/msg_private.h"
16 #include "src/simix/smx_private.h"
17 #include "surf/surf.h"
18 #include "xbt/replay.hpp"
19 #include <xbt/config.hpp>
20
21 #include <float.h> /* DBL_MAX */
22 #include <fstream>
23 #include <map>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string>
28 #include <vector>
29
30 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(smpi_kernel, smpi, "Logging specific to SMPI (kernel)");
31 #include <boost/tokenizer.hpp>
32 #include <boost/algorithm/string.hpp> /* trim_right / trim_left */
33
34 #if HAVE_PAPI
35 #include "papi.h"
36 const char* papi_default_config_name = "default";
37
38 struct papi_process_data {
39   papi_counter_t counter_data;
40   int event_set;
41 };
42
43 #endif
44 std::unordered_map<std::string, double> location2speedup;
45
46 simgrid::smpi::Process **process_data = nullptr;
47 int process_count = 0;
48 int smpi_universe_size = 0;
49 int* index_to_process_data = nullptr;
50 extern double smpi_total_benched_time;
51 xbt_os_timer_t global_timer;
52 MPI_Comm MPI_COMM_WORLD = MPI_COMM_UNINITIALIZED;
53 MPI_Errhandler *MPI_ERRORS_RETURN = nullptr;
54 MPI_Errhandler *MPI_ERRORS_ARE_FATAL = nullptr;
55 MPI_Errhandler *MPI_ERRHANDLER_NULL = nullptr;
56 static simgrid::config::Flag<double> smpi_wtime_sleep(
57   "smpi/wtime", "Minimum time to inject inside a call to MPI_Wtime", 0.0);
58 static simgrid::config::Flag<double> smpi_init_sleep(
59   "smpi/init", "Time to inject inside a call to MPI_Init", 0.0);
60
61 void (*smpi_comm_copy_data_callback) (smx_activity_t, void*, size_t) = &smpi_comm_copy_buffer_callback;
62
63
64
65 int smpi_process_count()
66 {
67   return process_count;
68 }
69
70 simgrid::smpi::Process* smpi_process()
71 {
72   simgrid::MsgActorExt* msgExt = static_cast<simgrid::MsgActorExt*>(SIMIX_process_self()->data);
73   return static_cast<simgrid::smpi::Process*>(msgExt->data);
74 }
75
76 simgrid::smpi::Process* smpi_process_remote(int index)
77 {
78   return process_data[index_to_process_data[index]];
79 }
80
81 MPI_Comm smpi_process_comm_self(){
82   return smpi_process()->comm_self();
83 }
84
85 void smpi_process_init(int *argc, char ***argv){
86   simgrid::smpi::Process::init(argc, argv);
87 }
88
89 int smpi_process_index(){
90   return smpi_process()->index();
91 }
92
93
94 int smpi_global_size()
95 {
96   char *value = getenv("SMPI_GLOBAL_SIZE");
97   xbt_assert(value,"Please set env var SMPI_GLOBAL_SIZE to the expected number of processes.");
98
99   return xbt_str_parse_int(value, "SMPI_GLOBAL_SIZE contains a non-numerical value: %s");
100 }
101
102 void smpi_comm_set_copy_data_callback(void (*callback) (smx_activity_t, void*, size_t))
103 {
104   smpi_comm_copy_data_callback = callback;
105 }
106
107 void print(std::vector<std::pair<size_t, size_t>> vec) {
108     fprintf(stderr, "{");
109     for(auto elt: vec) {
110         fprintf(stderr, "(0x%x, 0x%x),", elt.first, elt.second);
111     }
112     stderr, fprintf(stderr, "}\n");
113 }
114 void memcpy_private(void *dest, const void *src, size_t n, std::vector<std::pair<size_t, size_t>> &private_blocks) {
115   for(auto block : private_blocks) {
116     memcpy((uint8_t*)dest+block.first, (uint8_t*)src+block.first, block.second-block.first);
117   }
118 }
119
120 void check_blocks(std::vector<std::pair<size_t, size_t>> &private_blocks, size_t buff_size) {
121   for(auto block : private_blocks) {
122     xbt_assert(block.first >= 0 && block.second <= buff_size, "Oops, bug in shared malloc.");
123   }
124 }
125
126 void smpi_comm_copy_buffer_callback(smx_activity_t synchro, void *buff, size_t buff_size)
127 {
128   simgrid::kernel::activity::Comm *comm = dynamic_cast<simgrid::kernel::activity::Comm*>(synchro);
129   int src_shared=0, dst_shared=0;
130   size_t src_offset=0, dst_offset=0;
131   std::vector<std::pair<size_t, size_t>> src_private_blocks;
132   std::vector<std::pair<size_t, size_t>> dst_private_blocks;
133   XBT_DEBUG("Copy the data over");
134   if(src_shared=smpi_is_shared(buff, src_private_blocks, &src_offset)) {
135     XBT_DEBUG("Sender %p is shared. Let's ignore it.", buff);
136     src_private_blocks = shift_and_frame_private_blocks(src_private_blocks, src_offset, buff_size);
137   }
138   else {
139     src_private_blocks.clear();
140     src_private_blocks.push_back(std::make_pair(0, buff_size));
141   }
142   if(dst_shared=smpi_is_shared((char*)comm->dst_buff, dst_private_blocks, &dst_offset)) {
143     XBT_DEBUG("Receiver %p is shared. Let's ignore it.", (char*)comm->dst_buff);
144     dst_private_blocks = shift_and_frame_private_blocks(dst_private_blocks, dst_offset, buff_size);
145   }
146   else {
147     dst_private_blocks.clear();
148     dst_private_blocks.push_back(std::make_pair(0, buff_size));
149   }
150 /*
151   fprintf(stderr, "size: 0x%x\n", buff_size);
152   fprintf(stderr, "src: ");
153   print(src_private_blocks);
154   fprintf(stderr, "src_offset = 0x%x\n", src_offset);
155   fprintf(stderr, "dst: ");
156   print(dst_private_blocks);
157   fprintf(stderr, "dst_offset = 0x%x\n", dst_offset);
158 */
159   check_blocks(src_private_blocks, buff_size);
160   check_blocks(dst_private_blocks, buff_size);
161   auto private_blocks = merge_private_blocks(src_private_blocks, dst_private_blocks);
162 /*
163   fprintf(stderr, "Private blocks: ");
164   print(private_blocks);
165 */
166   check_blocks(private_blocks, buff_size);
167   void* tmpbuff=buff;
168   if((smpi_privatize_global_variables) && (static_cast<char*>(buff) >= smpi_start_data_exe)
169       && (static_cast<char*>(buff) < smpi_start_data_exe + smpi_size_data_exe )
170     ){
171        XBT_DEBUG("Privatization : We are copying from a zone inside global memory... Saving data to temp buffer !");
172
173        smpi_switch_data_segment(
174            (static_cast<simgrid::smpi::Process*>((static_cast<simgrid::MsgActorExt*>(comm->src_proc->data)->data))->index()));
175        tmpbuff = static_cast<void*>(xbt_malloc(buff_size));
176        memcpy_private(tmpbuff, buff, buff_size, private_blocks);
177   }
178
179   if((smpi_privatize_global_variables) && ((char*)comm->dst_buff >= smpi_start_data_exe)
180       && ((char*)comm->dst_buff < smpi_start_data_exe + smpi_size_data_exe )){
181        XBT_DEBUG("Privatization : We are copying to a zone inside global memory - Switch data segment");
182        smpi_switch_data_segment(
183            (static_cast<simgrid::smpi::Process*>((static_cast<simgrid::MsgActorExt*>(comm->dst_proc->data)->data))->index()));
184   }
185
186   XBT_DEBUG("Copying %zu bytes from %p to %p", buff_size, tmpbuff,comm->dst_buff);
187   memcpy_private(comm->dst_buff, tmpbuff, buff_size, private_blocks);
188
189   if (comm->detached) {
190     // if this is a detached send, the source buffer was duplicated by SMPI
191     // sender to make the original buffer available to the application ASAP
192     xbt_free(buff);
193     //It seems that the request is used after the call there this should be free somewhere else but where???
194     //xbt_free(comm->comm.src_data);// inside SMPI the request is kept inside the user data and should be free
195     comm->src_buff = nullptr;
196   }
197   if(tmpbuff!=buff)xbt_free(tmpbuff);
198
199 }
200
201 void smpi_comm_null_copy_buffer_callback(smx_activity_t comm, void *buff, size_t buff_size)
202 {
203   /* nothing done in this version */
204 }
205
206 static void smpi_check_options(){
207   //check correctness of MPI parameters
208
209    xbt_assert(xbt_cfg_get_int("smpi/async-small-thresh") <= xbt_cfg_get_int("smpi/send-is-detached-thresh"));
210
211    if (xbt_cfg_is_default_value("smpi/host-speed")) {
212      XBT_INFO("You did not set the power of the host running the simulation.  "
213               "The timings will certainly not be accurate.  "
214               "Use the option \"--cfg=smpi/host-speed:<flops>\" to set its value."
215               "Check http://simgrid.org/simgrid/latest/doc/options.html#options_smpi_bench for more information.");
216    }
217
218    xbt_assert(xbt_cfg_get_double("smpi/cpu-threshold") >=0,
219        "The 'smpi/cpu-threshold' option cannot have negative values [anymore]. If you want to discard "
220        "the simulation of any computation, please use 'smpi/simulate-computation:no' instead.");
221 }
222
223 int smpi_enabled() {
224   return process_data != nullptr;
225 }
226
227 void smpi_global_init()
228 {
229   int i;
230   MPI_Group group;
231   int smpirun=0;
232
233   if (!MC_is_active()) {
234     global_timer = xbt_os_timer_new();
235     xbt_os_walltimer_start(global_timer);
236   }
237
238   if (xbt_cfg_get_string("smpi/comp-adjustment-file")[0] != '\0') { 
239     std::string filename {xbt_cfg_get_string("smpi/comp-adjustment-file")};
240     std::ifstream fstream(filename);
241     if (!fstream.is_open()) {
242       xbt_die("Could not open file %s. Does it exist?", filename.c_str());
243     }
244
245     std::string line;
246     typedef boost::tokenizer< boost::escaped_list_separator<char>> Tokenizer;
247     std::getline(fstream, line); // Skip the header line
248     while (std::getline(fstream, line)) {
249       Tokenizer tok(line);
250       Tokenizer::iterator it  = tok.begin();
251       Tokenizer::iterator end = std::next(tok.begin());
252
253       std::string location = *it;
254       boost::trim(location);
255       location2speedup.insert(std::pair<std::string, double>(location, std::stod(*end)));
256     }
257   }
258
259 #if HAVE_PAPI
260   // This map holds for each computation unit (such as "default" or "process1" etc.)
261   // the configuration as given by the user (counter data as a pair of (counter_name, counter_counter))
262   // and the (computed) event_set.
263   std::map</* computation unit name */ std::string, papi_process_data> units2papi_setup;
264
265   if (xbt_cfg_get_string("smpi/papi-events")[0] != '\0') {
266     if (PAPI_library_init(PAPI_VER_CURRENT) != PAPI_VER_CURRENT)
267       XBT_ERROR("Could not initialize PAPI library; is it correctly installed and linked?"
268                 " Expected version is %i",
269                 PAPI_VER_CURRENT);
270
271     typedef boost::tokenizer<boost::char_separator<char>> Tokenizer;
272     boost::char_separator<char> separator_units(";");
273     std::string str = std::string(xbt_cfg_get_string("smpi/papi-events"));
274     Tokenizer tokens(str, separator_units);
275
276     // Iterate over all the computational units. This could be
277     // processes, hosts, threads, ranks... You name it. I'm not exactly
278     // sure what we will support eventually, so I'll leave it at the
279     // general term "units".
280     for (auto& unit_it : tokens) {
281       boost::char_separator<char> separator_events(":");
282       Tokenizer event_tokens(unit_it, separator_events);
283
284       int event_set = PAPI_NULL;
285       if (PAPI_create_eventset(&event_set) != PAPI_OK) {
286         // TODO: Should this let the whole simulation die?
287         XBT_CRITICAL("Could not create PAPI event set during init.");
288       }
289
290       // NOTE: We cannot use a map here, as we must obey the order of the counters
291       // This is important for PAPI: We need to map the values of counters back
292       // to the event_names (so, when PAPI_read() has finished)!
293       papi_counter_t counters2values;
294
295       // Iterate over all counters that were specified for this specific
296       // unit.
297       // Note that we need to remove the name of the unit
298       // (that could also be the "default" value), which always comes first.
299       // Hence, we start at ++(events.begin())!
300       for (Tokenizer::iterator events_it = ++(event_tokens.begin()); events_it != event_tokens.end(); events_it++) {
301
302         int event_code   = PAPI_NULL;
303         char* event_name = const_cast<char*>((*events_it).c_str());
304         if (PAPI_event_name_to_code(event_name, &event_code) == PAPI_OK) {
305           if (PAPI_add_event(event_set, event_code) != PAPI_OK) {
306             XBT_ERROR("Could not add PAPI event '%s'. Skipping.", event_name);
307             continue;
308           } else {
309             XBT_DEBUG("Successfully added PAPI event '%s' to the event set.", event_name);
310           }
311         } else {
312           XBT_CRITICAL("Could not find PAPI event '%s'. Skipping.", event_name);
313           continue;
314         }
315
316         counters2values.push_back(
317             // We cannot just pass *events_it, as this is of type const basic_string
318             std::make_pair<std::string, long long>(std::string(*events_it), 0));
319       }
320
321       std::string unit_name    = *(event_tokens.begin());
322       papi_process_data config = {.counter_data = std::move(counters2values), .event_set = event_set};
323
324       units2papi_setup.insert(std::make_pair(unit_name, std::move(config)));
325     }
326   }
327 #endif
328   if (process_count == 0){
329     process_count = SIMIX_process_count();
330     smpirun=1;
331   }
332   smpi_universe_size = process_count;
333   process_data       = new simgrid::smpi::Process*[process_count];
334   for (i = 0; i < process_count; i++) {
335     process_data[i]                       = new simgrid::smpi::Process(i);
336   }
337   //if the process was launched through smpirun script we generate a global mpi_comm_world
338   //if not, we let MPI_COMM_NULL, and the comm world will be private to each mpi instance
339   if(smpirun){
340     group = new  simgrid::smpi::Group(process_count);
341     MPI_COMM_WORLD = new  simgrid::smpi::Comm(group, nullptr);
342     MPI_Attr_put(MPI_COMM_WORLD, MPI_UNIVERSE_SIZE, reinterpret_cast<void *>(process_count));
343     msg_bar_t bar = MSG_barrier_init(process_count);
344
345     for (i = 0; i < process_count; i++) {
346       group->set_mapping(i, i);
347       process_data[i]->set_finalization_barrier(bar);
348     }
349   }
350 }
351
352 void smpi_global_destroy()
353 {
354   int count = smpi_process_count();
355
356   smpi_bench_destroy();
357   smpi_shared_destroy();
358   if (MPI_COMM_WORLD != MPI_COMM_UNINITIALIZED){
359       delete MPI_COMM_WORLD->group();
360       MSG_barrier_destroy(process_data[0]->finalization_barrier());
361   }else{
362       smpi_deployment_cleanup_instances();
363   }
364   for (int i = 0; i < count; i++) {
365     if(process_data[i]->comm_self()!=MPI_COMM_NULL){
366       simgrid::smpi::Comm::destroy(process_data[i]->comm_self());
367     }
368     if(process_data[i]->comm_intra()!=MPI_COMM_NULL){
369       simgrid::smpi::Comm::destroy(process_data[i]->comm_intra());
370     }
371     xbt_os_timer_free(process_data[i]->timer());
372     xbt_mutex_destroy(process_data[i]->mailboxes_mutex());
373     delete process_data[i];
374   }
375   delete[] process_data;
376   process_data = nullptr;
377
378   if (MPI_COMM_WORLD != MPI_COMM_UNINITIALIZED){
379     MPI_COMM_WORLD->cleanup_smp();
380     MPI_COMM_WORLD->cleanup_attr<simgrid::smpi::Comm>();
381     if(simgrid::smpi::Colls::smpi_coll_cleanup_callback!=nullptr)
382       simgrid::smpi::Colls::smpi_coll_cleanup_callback();
383     delete MPI_COMM_WORLD;
384   }
385
386   MPI_COMM_WORLD = MPI_COMM_NULL;
387
388   if (!MC_is_active()) {
389     xbt_os_timer_free(global_timer);
390   }
391
392   xbt_free(index_to_process_data);
393   if(smpi_privatize_global_variables)
394     smpi_destroy_global_memory_segments();
395   smpi_free_static();
396 }
397
398 extern "C" {
399
400 #ifndef WIN32
401
402 void __attribute__ ((weak)) user_main_()
403 {
404   xbt_die("Should not be in this smpi_simulated_main");
405 }
406
407 int __attribute__ ((weak)) smpi_simulated_main_(int argc, char **argv)
408 {
409   simgrid::smpi::Process::init(&argc, &argv);
410   user_main_();
411   return 0;
412 }
413
414 inline static int smpi_main_wrapper(int argc, char **argv){
415   int ret = smpi_simulated_main_(argc,argv);
416   if(ret !=0){
417     XBT_WARN("SMPI process did not return 0. Return value : %d", ret);
418     smpi_process()->set_return_value(ret);
419   }
420   return 0;
421 }
422
423 int __attribute__ ((weak)) main(int argc, char **argv)
424 {
425   return smpi_main(smpi_main_wrapper, argc, argv);
426 }
427
428 #endif
429
430 static void smpi_init_logs(){
431
432   /* Connect log categories.  See xbt/log.c */
433
434   XBT_LOG_CONNECT(smpi);  /* Keep this line as soon as possible in this function: xbt_log_appender_file.c depends on it
435                              DO NOT connect this in XBT or so, or it will be useless to xbt_log_appender_file.c */
436   XBT_LOG_CONNECT(instr_smpi);
437   XBT_LOG_CONNECT(smpi_bench);
438   XBT_LOG_CONNECT(smpi_coll);
439   XBT_LOG_CONNECT(smpi_colls);
440   XBT_LOG_CONNECT(smpi_comm);
441   XBT_LOG_CONNECT(smpi_datatype);
442   XBT_LOG_CONNECT(smpi_dvfs);
443   XBT_LOG_CONNECT(smpi_group);
444   XBT_LOG_CONNECT(smpi_kernel);
445   XBT_LOG_CONNECT(smpi_mpi);
446   XBT_LOG_CONNECT(smpi_memory);
447   XBT_LOG_CONNECT(smpi_op);
448   XBT_LOG_CONNECT(smpi_pmpi);
449   XBT_LOG_CONNECT(smpi_request);
450   XBT_LOG_CONNECT(smpi_replay);
451   XBT_LOG_CONNECT(smpi_rma);
452   XBT_LOG_CONNECT(smpi_shared);
453   XBT_LOG_CONNECT(smpi_utils);
454 }
455 }
456
457 static void smpi_init_options(){
458
459     simgrid::smpi::Colls::set_collectives();
460     simgrid::smpi::Colls::smpi_coll_cleanup_callback=nullptr;
461     smpi_cpu_threshold = xbt_cfg_get_double("smpi/cpu-threshold");
462     smpi_host_speed = xbt_cfg_get_double("smpi/host-speed");
463     smpi_privatize_global_variables = xbt_cfg_get_boolean("smpi/privatize-global-variables");
464     if (smpi_cpu_threshold < 0)
465       smpi_cpu_threshold = DBL_MAX;
466
467     char* val = xbt_cfg_get_string("smpi/shared-malloc");
468     if (!strcasecmp(val, "yes") || !strcmp(val, "1") || !strcasecmp(val, "on") || !strcasecmp(val, "global")) {
469       smpi_cfg_shared_malloc = shmalloc_global;
470     } else if (!strcasecmp(val, "local")) {
471       smpi_cfg_shared_malloc = shmalloc_local;
472     } else if (!strcasecmp(val, "no") || !strcmp(val, "0") || !strcasecmp(val, "off")) {
473       smpi_cfg_shared_malloc = shmalloc_none;
474     } else {
475       xbt_die("Invalid value '%s' for option smpi/shared-malloc. Possible values: 'on' or 'global', 'local', 'off'",
476               val);
477     }
478 }
479
480 int smpi_main(int (*realmain) (int argc, char *argv[]), int argc, char *argv[])
481 {
482   srand(SMPI_RAND_SEED);
483
484   if (getenv("SMPI_PRETEND_CC") != nullptr) {
485     /* Hack to ensure that smpicc can pretend to be a simple compiler. Particularly handy to pass it to the
486      * configuration tools */
487     return 0;
488   }
489   smpi_init_logs();
490
491   TRACE_global_init(&argc, argv);
492   TRACE_add_start_function(TRACE_smpi_alloc);
493   TRACE_add_end_function(TRACE_smpi_release);
494
495   SIMIX_global_init(&argc, argv);
496   MSG_init(&argc,argv);
497
498   SMPI_switch_data_segment = &smpi_switch_data_segment;
499
500   smpi_init_options();
501
502   // parse the platform file: get the host list
503   SIMIX_create_environment(argv[1]);
504   SIMIX_comm_set_copy_data_callback(smpi_comm_copy_data_callback);
505   SIMIX_function_register_default(realmain);
506   SIMIX_launch_application(argv[2]);
507
508   smpi_global_init();
509
510   smpi_check_options();
511
512   if(smpi_privatize_global_variables)
513     smpi_initialize_global_memory_segments();
514
515   /* Clean IO before the run */
516   fflush(stdout);
517   fflush(stderr);
518
519   if (MC_is_active()) {
520     MC_run();
521   } else {
522   
523     SIMIX_run();
524
525     xbt_os_walltimer_stop(global_timer);
526     if (xbt_cfg_get_boolean("smpi/display-timing")){
527       double global_time = xbt_os_timer_elapsed(global_timer);
528       XBT_INFO("Simulated time: %g seconds. \n\n"
529           "The simulation took %g seconds (after parsing and platform setup)\n"
530           "%g seconds were actual computation of the application",
531           SIMIX_get_clock(), global_time , smpi_total_benched_time);
532           
533       if (smpi_total_benched_time/global_time>=0.75)
534       XBT_INFO("More than 75%% of the time was spent inside the application code.\n"
535       "You may want to use sampling functions or trace replay to reduce this.");
536     }
537   }
538   int count = smpi_process_count();
539   int i, ret=0;
540   for (i = 0; i < count; i++) {
541     if(process_data[i]->return_value()!=0){
542       ret=process_data[i]->return_value();//return first non 0 value
543       break;
544     }
545   }
546   smpi_global_destroy();
547
548   TRACE_end();
549
550   return ret;
551 }
552
553 // This function can be called from extern file, to initialize logs, options, and processes of smpi
554 // without the need of smpirun
555 void SMPI_init(){
556   smpi_init_logs();
557   smpi_init_options();
558   smpi_global_init();
559   smpi_check_options();
560   if (TRACE_is_enabled() && TRACE_is_configured())
561     TRACE_smpi_alloc();
562   if(smpi_privatize_global_variables)
563     smpi_initialize_global_memory_segments();
564 }
565
566 void SMPI_finalize(){
567   smpi_global_destroy();
568 }
569
570 void smpi_mpi_init() {
571   if(smpi_init_sleep > 0) 
572     simcall_process_sleep(smpi_init_sleep);
573 }
574
575 double smpi_mpi_wtime(){
576   double time;
577   if (smpi_process()->initialized() != 0 && smpi_process()->finalized() == 0 && smpi_process()->sampling() == 0) {
578     smpi_bench_end();
579     time = SIMIX_get_clock();
580     // to avoid deadlocks if used as a break condition, such as
581     //     while (MPI_Wtime(...) < time_limit) {
582     //       ....
583     //     }
584     // because the time will not normally advance when only calls to MPI_Wtime
585     // are made -> deadlock (MPI_Wtime never reaches the time limit)
586     if(smpi_wtime_sleep > 0) 
587       simcall_process_sleep(smpi_wtime_sleep);
588     smpi_bench_begin();
589   } else {
590     time = SIMIX_get_clock();
591   }
592   return time;
593 }
594