Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
oups! forgot to rest some parts ...
[simgrid.git] / src / smpi / smpi_bench.c
1 /* Copyright (c) 2007, 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 "private.h"
8 #include "xbt/dict.h"
9 #include "xbt/sysdep.h"
10 #include "xbt/ex.h"
11 #include "surf/surf.h"
12 #include "simgrid/sg_config.h"
13
14 #ifndef WIN32
15 #include <sys/mman.h>
16 #endif
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <math.h> // sqrt
22 #include <unistd.h>
23 #include <string.h>
24 #include <stdio.h>
25
26 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(smpi_bench, smpi,
27                                 "Logging specific to SMPI (benchmarking)");
28
29 /* Shared allocations are handled through shared memory segments.
30  * Associated data and metadata are used as follows:
31  *
32  *                                                                    mmap #1
33  *    `allocs' dict                                                     ---- -.
34  *    ----------      shared_data_t               shared_metadata_t   / |  |  |
35  * .->| <name> | ---> -------------------- <--.   -----------------   | |  |  |
36  * |  ----------      | fd of <name>     |    |   | size of mmap  | --| |  |  |
37  * |                  | count (2)        |    |-- | data          |   \ |  |  |
38  * `----------------- | <name>           |    |   -----------------     ----  |
39  *                    --------------------    |   ^                           |
40  *                                            |   |                           |
41  *                                            |   |   `allocs_metadata' dict  |
42  *                                            |   |   ----------------------  |
43  *                                            |   `-- | <addr of mmap #1>  |<-'
44  *                                            |   .-- | <addr of mmap #2>  |<-.
45  *                                            |   |   ----------------------  |
46  *                                            |   |                           |
47  *                                            |   |                           |
48  *                                            |   |                           |
49  *                                            |   |                   mmap #2 |
50  *                                            |   v                     ---- -'
51  *                                            |   shared_metadata_t   / |  |
52  *                                            |   -----------------   | |  |
53  *                                            |   | size of mmap  | --| |  |
54  *                                            `-- | data          |   | |  |
55  *                                                -----------------   | |  |
56  *                                                                    \ |  |
57  *                                                                      ----
58  */
59
60 #define PTR_STRLEN (2 + 2 * sizeof(void*) + 1)
61
62 xbt_dict_t allocs = NULL;          /* Allocated on first use */
63 xbt_dict_t allocs_metadata = NULL; /* Allocated on first use */
64 xbt_dict_t samples = NULL;         /* Allocated on first use */
65 xbt_dict_t calls = NULL;           /* Allocated on first use */
66 __thread int smpi_current_rank = 0;      /* Updated after each MPI call */
67
68 typedef struct {
69   int fd;
70   int count;
71   char* loc;
72 } shared_data_t;
73
74 typedef struct  {
75   size_t size;
76   shared_data_t* data;
77 } shared_metadata_t;
78
79 static size_t shm_size(int fd) {
80   struct stat st;
81
82   if(fstat(fd, &st) < 0) {
83     xbt_die("Could not stat fd %d: %s", fd, strerror(errno));
84   }
85   return (size_t)st.st_size;
86 }
87
88 #ifndef WIN32
89 static void* shm_map(int fd, size_t size, shared_data_t* data) {
90   void* mem;
91   char loc[PTR_STRLEN];
92   shared_metadata_t* meta;
93
94   if(size > shm_size(fd)) {
95     if(ftruncate(fd, (off_t)size) < 0) {
96       xbt_die("Could not truncate fd %d to %zu: %s", fd, size, strerror(errno));
97     }
98   }
99
100   mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
101   if(mem == MAP_FAILED) {
102     xbt_die("Could not map fd %d: %s", fd, strerror(errno));
103   }
104   if(!allocs_metadata) {
105     allocs_metadata = xbt_dict_new();
106   }
107   snprintf(loc, PTR_STRLEN, "%p", mem);
108   meta = xbt_new(shared_metadata_t, 1);
109   meta->size = size;
110   meta->data = data;
111   xbt_dict_set(allocs_metadata, loc, meta, &free);
112   XBT_DEBUG("MMAP %zu to %p", size, mem);
113   return mem;
114 }
115 #endif
116
117 void smpi_bench_destroy(void)
118 {
119   xbt_dict_free(&allocs);
120   xbt_dict_free(&samples);
121   xbt_dict_free(&calls);
122 }
123
124 void smpi_execute_flops(double flops) {
125   smx_action_t action;
126   smx_host_t host;
127   host = SIMIX_host_self();
128
129   XBT_DEBUG("Handle real computation time: %f flops", flops);
130   action = simcall_host_execute("computation", host, flops, 1);
131 #ifdef HAVE_TRACING
132   simcall_set_category (action, TRACE_internal_smpi_get_category());
133 #endif
134   simcall_host_execution_wait(action);
135 }
136
137 static void smpi_execute(double duration)
138 {
139   /* FIXME: a global variable would be less expensive to consult than a call to xbt_cfg_get_double() right on the critical path */
140   if (duration >= sg_cfg_get_double("smpi/cpu_threshold")) {
141     XBT_DEBUG("Sleep for %f to handle real computation time", duration);
142     smpi_execute_flops(duration *
143                 sg_cfg_get_double("smpi/running_power"));
144   } else {
145     XBT_DEBUG("Real computation took %f while option smpi/cpu_threshold is set to %f => ignore it",
146         duration, sg_cfg_get_double("smpi/cpu_threshold"));
147   }
148 }
149
150 void smpi_bench_begin(void)
151 {
152   xbt_os_timer_start(smpi_process_timer());
153   smpi_current_rank = smpi_process_index();
154 }
155
156 void smpi_bench_end(void)
157 {
158   xbt_os_timer_t timer = smpi_process_timer();
159
160   xbt_os_timer_stop(timer);
161   smpi_execute(xbt_os_timer_elapsed(timer));
162 }
163
164 unsigned int smpi_sleep(unsigned int secs)
165 {
166   smpi_bench_end();
167   smpi_execute((double) secs);
168   smpi_bench_begin();
169   return secs;
170 }
171
172 int smpi_gettimeofday(struct timeval *tv)
173 {
174   double now;
175   smpi_bench_end();
176   now = SIMIX_get_clock();
177   if (tv) {
178     tv->tv_sec = (time_t)now;
179 #ifdef WIN32
180     tv->tv_usec = (useconds_t)((now - tv->tv_sec) * 1e6);
181 #else
182     tv->tv_usec = (suseconds_t)((now - tv->tv_sec) * 1e6);
183 #endif
184   }
185   smpi_bench_begin();
186   return 0;
187 }
188
189 extern double sg_maxmin_precision;
190 unsigned long long smpi_rastro_resolution (void)
191 {
192   smpi_bench_end();
193   double resolution = (1/sg_maxmin_precision);
194   smpi_bench_begin();
195   return (unsigned long long)resolution;
196 }
197
198 unsigned long long smpi_rastro_timestamp (void)
199 {
200   smpi_bench_end();
201   double now = SIMIX_get_clock();
202
203   unsigned long long sec = (unsigned long long)now;
204   unsigned long long pre = (now - sec) * smpi_rastro_resolution();
205   smpi_bench_begin();
206   return (unsigned long long)sec * smpi_rastro_resolution() + pre;
207 }
208
209 /* ****************************** Functions related to the SMPI_SAMPLE_ macros ************************************/
210 typedef struct {
211   int iters;        /* amount of requested iterations */
212   int count;        /* amount of iterations done so far */
213   double threshold; /* maximal stderr requested (if positive) */
214   double relstderr; /* observed stderr so far */
215   double mean;      /* mean of benched times, to be used if the block is disabled */
216   double sum;       /* sum of benched times (to compute the mean and stderr) */
217   double sum_pow2;  /* sum of the square of the benched times (to compute the stderr) */
218   int benching;     /* 1: we are benchmarking; 0: we have enough data, no bench anymore */
219 } local_data_t;
220
221 static char *sample_location(int global, const char *file, int line) {
222   if (global) {
223     return bprintf("%s:%d", file, line);
224   } else {
225     return bprintf("%s:%d:%d", file, line, smpi_process_index());
226   }
227 }
228 static int sample_enough_benchs(local_data_t *data) {
229   int res = data->count >= data->iters;
230   if (data->threshold>0.0) {
231     if (data->count <2)
232       res = 0; // not enough data
233     if (data->relstderr > data->threshold)
234       res = 0; // stderr too high yet
235   }
236   XBT_DEBUG("%s (count:%d iter:%d stderr:%f thres:%f mean:%fs)",
237       (res?"enough benchs":"need more data"),
238       data->count, data->iters, data->relstderr, data->threshold, data->mean);
239   return res;
240 }
241
242 void smpi_sample_1(int global, const char *file, int line, int iters, double threshold)
243 {
244   char *loc = sample_location(global, file, line);
245   local_data_t *data;
246
247   smpi_bench_end();     /* Take time from previous, unrelated computation into account */
248   if (!samples)
249     samples = xbt_dict_new_homogeneous(free);
250
251   data = xbt_dict_get_or_null(samples, loc);
252   if (!data) {
253     xbt_assert(threshold>0 || iters>0,
254         "You should provide either a positive amount of iterations to bench, or a positive maximal stderr (or both)");
255     data = (local_data_t *) xbt_new(local_data_t, 1);
256     data->count = 0;
257     data->sum = 0.0;
258     data->sum_pow2 = 0.0;
259     data->iters = iters;
260     data->threshold = threshold;
261     data->benching = 1; // If we have no data, we need at least one
262     data->mean = 0;
263     xbt_dict_set(samples, loc, data, NULL);
264     XBT_DEBUG("XXXXX First time ever on benched nest %s.",loc);
265   } else {
266     if (data->iters != iters || data->threshold != threshold) {
267       XBT_ERROR("Asked to bench block %s with different settings %d, %f is not %d, %f. How did you manage to give two numbers at the same line??",
268           loc, data->iters, data->threshold, iters,threshold);
269       THROW_IMPOSSIBLE;
270     }
271
272     // if we already have some data, check whether sample_2 should get one more bench or whether it should emulate the computation instead
273     data->benching = !sample_enough_benchs(data);
274     XBT_DEBUG("XXXX Re-entering the benched nest %s. %s",loc, (data->benching?"more benching needed":"we have enough data, skip computes"));
275   }
276   free(loc);
277 }
278
279 int smpi_sample_2(int global, const char *file, int line)
280 {
281   char *loc = sample_location(global, file, line);
282   local_data_t *data;
283
284   xbt_assert(samples, "Y U NO use SMPI_SAMPLE_* macros? Stop messing directly with smpi_sample_* functions!");
285   data = xbt_dict_get(samples, loc);
286   XBT_DEBUG("sample2 %s",loc);
287   free(loc);
288
289   if (data->benching==1) {
290     // we need to run a new bench
291     XBT_DEBUG("benchmarking: count:%d iter:%d stderr:%f thres:%f; mean:%f",
292         data->count, data->iters, data->relstderr, data->threshold, data->mean);
293     smpi_bench_begin();
294     return 1;
295   } else {
296     // Enough data, no more bench (either we got enough data from previous visits to this benched nest, or we just ran one bench and need to bail out now that our job is done).
297     // Just sleep instead
298     XBT_DEBUG("No benchmark (either no need, or just ran one): count >= iter (%d >= %d) or stderr<thres (%f<=%f). apply the %fs delay instead",
299         data->count, data->iters, data->relstderr, data->threshold, data->mean);
300     smpi_execute(data->mean);
301
302     smpi_bench_begin(); // prepare to capture future, unrelated computations
303     return 0;
304   }
305 }
306
307
308 void smpi_sample_3(int global, const char *file, int line)
309 {
310   char *loc = sample_location(global, file, line);
311   local_data_t *data;
312
313   xbt_assert(samples, "Y U NO use SMPI_SAMPLE_* macros? Stop messing directly with smpi_sample_* functions!");
314   data = xbt_dict_get(samples, loc);
315   XBT_DEBUG("sample3 %s",loc);
316
317   if (data->benching==0) {
318     THROW_IMPOSSIBLE;
319   }
320
321   // ok, benchmarking this loop is over
322   xbt_os_timer_stop(smpi_process_timer());
323
324   // update the stats
325   double sample, n;
326   data->count++;
327   sample = xbt_os_timer_elapsed(smpi_process_timer());
328   data->sum += sample;
329   data->sum_pow2 += sample * sample;
330   n = (double)data->count;
331   data->mean = data->sum / n;
332   data->relstderr = sqrt((data->sum_pow2 / n - data->mean * data->mean) / n) / data->mean;
333   if (!sample_enough_benchs(data)) {
334     data->mean = sample; // Still in benching process; We want sample_2 to simulate the exact time of this loop occurrence before leaving, not the mean over the history
335   }
336   XBT_DEBUG("Average mean after %d steps is %f, relative standard error is %f (sample was %f)", data->count,
337       data->mean, data->relstderr, sample);
338
339   // That's enough for now, prevent sample_2 to run the same code over and over
340   data->benching = 0;
341 }
342
343 #ifndef WIN32
344 void *smpi_shared_malloc(size_t size, const char *file, int line)
345 {
346   char *loc = bprintf("%zu_%s_%d", (size_t)getpid(), file, line);
347   size_t len = strlen(loc);
348   size_t i;
349   int fd;
350   void* mem;
351   shared_data_t *data;
352
353   for(i = 0; i < len; i++) {
354     /* Make the 'loc' ID be a flat filename */
355     if(loc[i] == '/') {
356       loc[i] = '_';
357     }
358   }
359   if (!allocs) {
360     allocs = xbt_dict_new_homogeneous(free);
361   }
362   data = xbt_dict_get_or_null(allocs, loc);
363   if(!data) {
364     fd = shm_open(loc, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
365     if(fd < 0) {
366       switch(errno) {
367         case EEXIST:
368           xbt_die("Please cleanup /dev/shm/%s", loc);
369         default:
370           xbt_die("An unhandled error occured while opening %s: %s", loc, strerror(errno));
371       }
372     }
373     data = xbt_new(shared_data_t, 1);
374     data->fd = fd;
375     data->count = 1;
376     data->loc = loc;
377     mem = shm_map(fd, size, data);
378     if(shm_unlink(loc) < 0) {
379       XBT_WARN("Could not early unlink %s: %s", loc, strerror(errno));
380     }
381     xbt_dict_set(allocs, loc, data, NULL);
382     XBT_DEBUG("Mapping %s at %p through %d", loc, mem, fd);
383   } else {
384     mem = shm_map(data->fd, size, data);
385     data->count++;
386   }
387   XBT_DEBUG("Malloc %zu in %p (metadata at %p)", size, mem, data);
388   return mem;
389 }
390 void smpi_shared_free(void *ptr)
391 {
392   char loc[PTR_STRLEN];
393   shared_metadata_t* meta;
394   shared_data_t* data;
395
396   if (!allocs) {
397     XBT_WARN("Cannot free: nothing was allocated");
398     return;
399   }
400   if(!allocs_metadata) {
401     XBT_WARN("Cannot free: no metadata was allocated");
402   }
403   snprintf(loc, PTR_STRLEN, "%p", ptr);
404   meta = (shared_metadata_t*)xbt_dict_get_or_null(allocs_metadata, loc);
405   if (!meta) {
406     XBT_WARN("Cannot free: %p was not shared-allocated by SMPI", ptr);
407     return;
408   }
409   data = meta->data;
410   if(!data) {
411     XBT_WARN("Cannot free: something is broken in the metadata link");
412     return;
413   }
414   if(munmap(ptr, meta->size) < 0) {
415     XBT_WARN("Unmapping of fd %d failed: %s", data->fd, strerror(errno));
416   }
417   data->count--;
418   if (data->count <= 0) {
419     close(data->fd);
420     xbt_dict_remove(allocs, data->loc);
421     free(data->loc);
422   }
423 }
424 #endif
425
426 int smpi_shared_known_call(const char* func, const char* input) {
427    char* loc = bprintf("%s:%s", func, input);
428    xbt_ex_t ex;
429    int known;
430
431    if(!calls) {
432       calls = xbt_dict_new_homogeneous(NULL);
433    }
434    TRY {
435       xbt_dict_get(calls, loc); /* Succeed or throw */
436       known = 1;
437    }
438    CATCH(ex) {
439       if(ex.category == not_found_error) {
440          known = 0;
441          xbt_ex_free(ex);
442       } else {
443          RETHROW;
444       }
445    }
446    free(loc);
447    return known;
448 }
449
450 void* smpi_shared_get_call(const char* func, const char* input) {
451    char* loc = bprintf("%s:%s", func, input);
452    void* data;
453
454    if(!calls) {
455       calls = xbt_dict_new_homogeneous(NULL);
456    }
457    data = xbt_dict_get(calls, loc);
458    free(loc);
459    return data;
460 }
461
462 void* smpi_shared_set_call(const char* func, const char* input, void* data) {
463    char* loc = bprintf("%s:%s", func, input);
464
465    if(!calls) {
466       calls = xbt_dict_new_homogeneous(NULL);
467    }
468    xbt_dict_set(calls, loc, data, NULL);
469    free(loc);
470    return data;
471 }