Logo AND Algorithmique Numérique Distribuée

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