Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
add option "smpi/use_shared_malloc", with true as default, to allow disabling the...
[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_threadtimer_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_threadtimer_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   xbt_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   xbt_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   xbt_free(loc);
317
318   if (data->benching==0) {
319     THROW_IMPOSSIBLE;
320   }
321
322   // ok, benchmarking this loop is over
323   xbt_os_threadtimer_stop(smpi_process_timer());
324
325   // update the stats
326   double sample, n;
327   data->count++;
328   sample = xbt_os_timer_elapsed(smpi_process_timer());
329   data->sum += sample;
330   data->sum_pow2 += sample * sample;
331   n = (double)data->count;
332   data->mean = data->sum / n;
333   data->relstderr = sqrt((data->sum_pow2 / n - data->mean * data->mean) / n) / data->mean;
334   if (!sample_enough_benchs(data)) {
335     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
336   }
337   XBT_DEBUG("Average mean after %d steps is %f, relative standard error is %f (sample was %f)", data->count,
338       data->mean, data->relstderr, sample);
339
340   // That's enough for now, prevent sample_2 to run the same code over and over
341   data->benching = 0;
342 }
343
344 #ifndef WIN32
345 void *smpi_shared_malloc(size_t size, const char *file, int line)
346 {
347   char *loc = bprintf("%zu_%s_%d", (size_t)getpid(), file, line);
348   size_t len = strlen(loc);
349   size_t i;
350   int fd;
351   void* mem;
352   shared_data_t *data;
353   if (sg_cfg_get_boolean("smpi/use_shared_malloc")){
354     for(i = 0; i < len; i++) {
355       /* Make the 'loc' ID be a flat filename */
356       if(loc[i] == '/') {
357         loc[i] = '_';
358       }
359     }
360     if (!allocs) {
361       allocs = xbt_dict_new_homogeneous(free);
362     }
363     data = xbt_dict_get_or_null(allocs, loc);
364     if(!data) {
365       fd = shm_open(loc, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
366       if(fd < 0) {
367         switch(errno) {
368           case EEXIST:
369             xbt_die("Please cleanup /dev/shm/%s", loc);
370           default:
371             xbt_die("An unhandled error occured while opening %s: %s", loc, strerror(errno));
372         }
373       }
374       data = xbt_new(shared_data_t, 1);
375       data->fd = fd;
376       data->count = 1;
377       data->loc = loc;
378       mem = shm_map(fd, size, data);
379       if(shm_unlink(loc) < 0) {
380         XBT_WARN("Could not early unlink %s: %s", loc, strerror(errno));
381       }
382       xbt_dict_set(allocs, loc, data, NULL);
383       XBT_DEBUG("Mapping %s at %p through %d", loc, mem, fd);
384     } else {
385       mem = shm_map(data->fd, size, data);
386       data->count++;
387     }
388     XBT_DEBUG("Shared malloc %zu in %p (metadata at %p)", size, mem, data);
389   }else{
390     mem = xbt_malloc(size);
391     XBT_DEBUG("Classic malloc %zu in %p", size, mem);
392   }
393
394   return mem;
395 }
396 void smpi_shared_free(void *ptr)
397 {
398   char loc[PTR_STRLEN];
399   shared_metadata_t* meta;
400   shared_data_t* data;
401   if (sg_cfg_get_boolean("smpi/use_shared_malloc")){
402   
403     if (!allocs) {
404       XBT_WARN("Cannot free: nothing was allocated");
405       return;
406     }
407     if(!allocs_metadata) {
408       XBT_WARN("Cannot free: no metadata was allocated");
409     }
410     snprintf(loc, PTR_STRLEN, "%p", ptr);
411     meta = (shared_metadata_t*)xbt_dict_get_or_null(allocs_metadata, loc);
412     if (!meta) {
413       XBT_WARN("Cannot free: %p was not shared-allocated by SMPI", ptr);
414       return;
415     }
416     data = meta->data;
417     if(!data) {
418       XBT_WARN("Cannot free: something is broken in the metadata link");
419       return;
420     }
421     if(munmap(ptr, meta->size) < 0) {
422       XBT_WARN("Unmapping of fd %d failed: %s", data->fd, strerror(errno));
423     }
424     data->count--;
425     XBT_DEBUG("Shared free - no removal - of %p, count = %d", ptr, data->count);
426     if (data->count <= 0) {
427       close(data->fd);
428       xbt_dict_remove(allocs, data->loc);
429       free(data->loc);
430       XBT_DEBUG("Shared free - with removal - of %p", ptr);
431     }
432   }else{
433     XBT_DEBUG("Classic free of %p", ptr);
434     xbt_free(ptr);
435   }
436 }
437 #endif
438
439 int smpi_shared_known_call(const char* func, const char* input) {
440    char* loc = bprintf("%s:%s", func, input);
441    xbt_ex_t ex;
442    int known;
443
444    if(!calls) {
445       calls = xbt_dict_new_homogeneous(NULL);
446    }
447    TRY {
448       xbt_dict_get(calls, loc); /* Succeed or throw */
449       known = 1;
450    }
451    CATCH(ex) {
452       if(ex.category == not_found_error) {
453          known = 0;
454          xbt_ex_free(ex);
455       } else {
456          RETHROW;
457       }
458    }
459    free(loc);
460    return known;
461 }
462
463 void* smpi_shared_get_call(const char* func, const char* input) {
464    char* loc = bprintf("%s:%s", func, input);
465    void* data;
466
467    if(!calls) {
468       calls = xbt_dict_new_homogeneous(NULL);
469    }
470    data = xbt_dict_get(calls, loc);
471    free(loc);
472    return data;
473 }
474
475 void* smpi_shared_set_call(const char* func, const char* input, void* data) {
476    char* loc = bprintf("%s:%s", func, input);
477
478    if(!calls) {
479       calls = xbt_dict_new_homogeneous(NULL);
480    }
481    xbt_dict_set(calls, loc, data, NULL);
482    free(loc);
483    return data;
484 }