Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
RawContext: add comments for x86 raw_swapcontext()
[simgrid.git] / src / simix / RawContext.cpp
1 /* Copyright (c) 2009-2015. 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 /** \file RawContext.cpp 
8   * Fast context switching inspired from SystemV ucontexts.
9   *
10   * In contrast to System V context, it does not touch the signal mask
11   * which avoids making a system call (at least on Linux).
12   */
13
14 #include <math.h>
15
16 #include <utility>
17 #include <functional>
18
19 #include "src/internal_config.h" 
20
21 #include "xbt/log.h"
22 #include "xbt/parmap.h"
23 #include "xbt/dynar.h"
24
25 #include "smx_private.h"
26 #include "smx_private.hpp"
27 #include "mc/mc.h"
28
29 XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
30
31 // ***** Class definitions
32
33 namespace simgrid {
34 namespace simix {
35
36 class RawContext;
37 class RawContextFactory;
38
39 class RawContext : public Context {
40 protected:
41   void* stack_ = nullptr; 
42   /** pointer to top the stack stack */
43   void* stack_top_ = nullptr;
44 public:
45   friend class RawContextFactory;
46   RawContext(std::function<void()> code,
47           void_pfn_smxprocess_t cleanup_func,
48           smx_process_t process);
49   ~RawContext();
50 public:
51   static void wrapper(void* arg);
52   void stop() override;
53   void suspend() override;
54   void resume();
55 private:
56   void suspend_serial();
57   void suspend_parallel();
58   void resume_serial();
59   void resume_parallel();
60 };
61
62 class RawContextFactory : public ContextFactory {
63 public:
64   RawContextFactory();
65   ~RawContextFactory();
66   RawContext* create_context(std::function<void()> code,
67     void_pfn_smxprocess_t, smx_process_t process) override;
68   void run_all() override;
69 private:
70   void run_all_adaptative();
71   void run_all_serial();
72   void run_all_parallel();
73 };
74
75 ContextFactory* raw_factory()
76 {
77   XBT_VERB("Using raw contexts. Because the glibc is just not good enough for us.");
78   return new RawContextFactory();
79 }
80
81 }
82 }
83
84 // ***** Loads of static stuff
85
86 #if HAVE_THREAD_CONTEXTS
87 static xbt_parmap_t raw_parmap;
88 static simgrid::simix::RawContext** raw_workers_context;    /* space to save the worker context in each thread */
89 static uintptr_t raw_threads_working;     /* number of threads that have started their work */
90 static xbt_os_thread_key_t raw_worker_id_key; /* thread-specific storage for the thread id */
91 #endif
92 #ifdef ADAPTIVE_THRESHOLD
93 #define SCHED_ROUND_LIMIT 5
94 static xbt_os_timer_t round_time;
95 static double par_time,seq_time;
96 static double par_ratio,seq_ratio;
97 static int reached_seq_limit, reached_par_limit;
98 static unsigned int par_proc_that_ran = 0,seq_proc_that_ran = 0;  /* Counters of processes that have run in SCHED_ROUND_LIMIT scheduling rounds */
99 static unsigned int seq_sched_round=0, par_sched_round=0; /* Amount of SR that ran serial/parallel*/
100 /*Varables used to calculate running variance and mean*/
101 static double prev_avg_par_proc=0,prev_avg_seq_proc=0;
102 static double delta=0;
103 static double s_par_proc=0,s_seq_proc=0; /*Standard deviation of number of processes computed in par/seq during the current simulation*/
104 static double avg_par_proc=0,sd_par_proc=0;
105 static double avg_seq_proc=0,sd_seq_proc=0;
106 static long long par_window=(long long)HUGE_VAL,seq_window=0;
107 #endif
108 static unsigned long raw_process_index = 0;   /* index of the next process to run in the
109                                                * list of runnable processes */
110 static simgrid::simix::RawContext* raw_maestro_context;
111
112 static bool raw_context_parallel = false;
113 #ifdef ADAPTIVE_THRESHOLD
114 static bool raw_context_adaptative = false;
115 #endif
116
117 // ***** Raw context routines
118
119 typedef void (*rawctx_entry_point_t)(void *);
120
121 typedef void* raw_stack_t;
122 extern "C" raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
123                                    rawctx_entry_point_t entry_point, void* arg);
124 extern "C" void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context);
125
126 #if SIMGRID_PROCESSOR_x86_64
127 __asm__ (
128 #if defined(__APPLE__)
129    ".text\n"
130    ".globl _raw_makecontext\n"
131    "_raw_makecontext:\n"
132 #elif defined(_WIN32)
133    ".text\n"
134    ".globl raw_makecontext\n"
135    "raw_makecontext:\n"
136 #else
137    ".text\n"
138    ".globl raw_makecontext\n"
139    ".type raw_makecontext,@function\n"
140    "raw_makecontext:\n"/* Calling convention sets the arguments in rdi, rsi, rdx and rcx, respectively */
141 #endif
142    "   mov %rdi,%rax\n"      /* stack */
143    "   add %rsi,%rax\n"      /* size  */
144    "   andq $-16, %rax\n"    /* align stack */
145    "   movq $0,   -8(%rax)\n" /* @return for func */
146    "   mov %rdx,-16(%rax)\n" /* func */
147    "   mov %rcx,-24(%rax)\n" /* arg/rdi */
148    "   movq $0,  -32(%rax)\n" /* rsi */
149    "   movq $0,  -40(%rax)\n" /* rdx */
150    "   movq $0,  -48(%rax)\n" /* rcx */
151    "   movq $0,  -56(%rax)\n" /* r8  */
152    "   movq $0,  -64(%rax)\n" /* r9  */
153    "   movq $0,  -72(%rax)\n" /* rbp */
154    "   movq $0,  -80(%rax)\n" /* rbx */
155    "   movq $0,  -88(%rax)\n" /* r12 */
156    "   movq $0,  -96(%rax)\n" /* r13 */
157    "   movq $0, -104(%rax)\n" /* r14 */
158    "   movq $0, -112(%rax)\n" /* r15 */
159    "   sub $112,%rax\n"
160    "   ret\n"
161 );
162
163 __asm__ (
164 #if defined(__APPLE__)
165    ".text\n"
166    ".globl _raw_swapcontext\n"
167    "_raw_swapcontext:\n"
168 #elif defined(_WIN32)
169    ".text\n"
170    ".globl raw_swapcontext\n"
171    "raw_swapcontext:\n"
172 #else
173    ".text\n"
174    ".globl raw_swapcontext\n"
175    ".type raw_swapcontext,@function\n"
176    "raw_swapcontext:\n" /* Calling convention sets the arguments in rdi and rsi, respectively */
177 #endif
178    "   push %rdi\n"
179    "   push %rsi\n"
180    "   push %rdx\n"
181    "   push %rcx\n"
182    "   push %r8\n"
183    "   push %r9\n"
184    "   push %rbp\n"
185    "   push %rbx\n"
186    "   push %r12\n"
187    "   push %r13\n"
188    "   push %r14\n"
189    "   push %r15\n"
190    "   mov %rsp,(%rdi)\n" /* old */
191    "   mov %rsi,%rsp\n" /* new */
192    "   pop %r15\n"
193    "   pop %r14\n"
194    "   pop %r13\n"
195    "   pop %r12\n"
196    "   pop %rbx\n"
197    "   pop %rbp\n"
198    "   pop %r9\n"
199    "   pop %r8\n"
200    "   pop %rcx\n"
201    "   pop %rdx\n"
202    "   pop %rsi\n"
203    "   pop %rdi\n"
204    "   ret\n"
205 );
206 #elif SIMGRID_PROCESSOR_i686
207 __asm__ (
208 #if defined(__APPLE__) || defined(_WIN32)
209    ".text\n"
210    ".globl _raw_makecontext\n"
211    "_raw_makecontext:\n"
212 #else
213    ".text\n"
214    ".globl raw_makecontext\n"
215    ".type raw_makecontext,@function\n"
216    "raw_makecontext:\n"
217 #endif
218    "   movl 4(%esp),%eax\n"   /* stack */
219    "   addl 8(%esp),%eax\n"   /* size  */
220    "   andl $-16, %eax\n"     /* align stack */
221    "   movl 12(%esp),%ecx\n"  /* func  */
222    "   movl 16(%esp),%edx\n"  /* arg   */
223    "   movl %edx, -4(%eax)\n"
224    "   movl $0,   -8(%eax)\n" /* @return for func */
225    "   movl %ecx,-12(%eax)\n"
226    "   movl $0,  -16(%eax)\n" /* ebp */
227    "   movl $0,  -20(%eax)\n" /* ebx */
228    "   movl $0,  -24(%eax)\n" /* esi */
229    "   movl $0,  -28(%eax)\n" /* edi */
230    "   subl $28,%eax\n"
231    "   retl\n"
232 );
233
234 __asm__ (
235 #if defined(__APPLE__) || defined(_WIN32)
236    ".text\n"
237    ".globl _raw_swapcontext\n"
238    "_raw_swapcontext:\n"
239 #else
240    ".text\n"
241    ".globl raw_swapcontext\n"
242    ".type raw_swapcontext,@function\n"
243    "raw_swapcontext:\n"
244 #endif
245    // Fetch the parameters:
246    "   movl 4(%esp),%eax\n" /* old (raw_stack_t*) */
247    "   movl 8(%esp),%edx\n" /* new (raw_stack_t)  */
248    // Save registers of the current context on the stack:
249    "   pushl %ebp\n"
250    "   pushl %ebx\n"
251    "   pushl %esi\n"
252    "   pushl %edi\n"
253    // Save the current context (stack pointer) in *old:
254    "   movl %esp,(%eax)\n"
255    // Switch to the stack of the new context:
256    "   movl %edx,%esp\n"
257    // Pop the values of the new context:
258    "   popl %edi\n"
259    "   popl %esi\n"
260    "   popl %ebx\n"
261    "   popl %ebp\n"
262    // Return using the return address of the new context:
263    "   retl\n"
264 );
265 #else
266
267
268 /* If you implement raw contexts for other processors, don't forget to
269    update the definition of HAVE_RAW_CONTEXTS in tools/cmake/CompleteInFiles.cmake */
270
271 raw_stack_t raw_makecontext(void* malloced_stack, int stack_size,
272                             rawctx_entry_point_t entry_point, void* arg) {
273    THROW_UNIMPLEMENTED;
274 }
275
276 void raw_swapcontext(raw_stack_t* old, raw_stack_t new_context) {
277    THROW_UNIMPLEMENTED;
278 }
279
280 #endif
281
282 // ***** Method definitions
283
284 namespace simgrid {
285 namespace simix {
286
287 RawContextFactory::RawContextFactory()
288   : ContextFactory("RawContextFactory")
289 {
290 #ifdef ADAPTIVE_THRESHOLD
291   raw_context_adaptative = (SIMIX_context_get_parallel_threshold() > 1);
292 #endif
293   raw_context_parallel = SIMIX_context_is_parallel();
294   if (raw_context_parallel) {
295 #if HAVE_THREAD_CONTEXTS
296     int nthreads = SIMIX_context_get_nthreads();
297     xbt_os_thread_key_create(&raw_worker_id_key);
298     // TODO, lazily init
299     raw_parmap = nullptr;
300     raw_workers_context = xbt_new(RawContext*, nthreads);
301     raw_maestro_context = nullptr;
302 #endif
303     // TODO, if(SIMIX_context_get_parallel_threshold() > 1) => choose dynamically
304   }
305 #ifdef ADAPTIVE_THRESHOLD
306   round_time = xbt_os_timer_new();
307   reached_seq_limit = 0;
308   reached_par_limit = 0;
309 #endif
310 }
311
312 RawContextFactory::~RawContextFactory()
313 {
314 #if HAVE_THREAD_CONTEXTS
315   if (raw_parmap)
316     xbt_parmap_destroy(raw_parmap);
317   xbt_free(raw_workers_context);
318 #endif
319 }
320
321 RawContext* RawContextFactory::create_context(std::function<void()> code,
322     void_pfn_smxprocess_t cleanup, smx_process_t process)
323 {
324   return this->new_context<RawContext>(std::move(code),
325     cleanup, process);
326 }
327
328 void RawContext::wrapper(void* arg)
329 {
330   RawContext* context = (RawContext*) arg;
331   (*context)();
332   context->stop();
333 }
334
335 RawContext::RawContext(std::function<void()> code,
336     void_pfn_smxprocess_t cleanup, smx_process_t process)
337   : Context(std::move(code), cleanup, process)
338 {
339    if (has_code()) {
340      this->stack_ = SIMIX_context_stack_new();
341      this->stack_top_ = raw_makecontext(this->stack_,
342                          smx_context_usable_stack_size,
343                          RawContext::wrapper,
344                          this);
345    } else {
346      if(process != nullptr && raw_maestro_context == nullptr)
347        raw_maestro_context = this;
348      if (MC_is_active())
349        MC_ignore_heap(
350          &raw_maestro_context->stack_top_,
351          sizeof(raw_maestro_context->stack_top_));
352    }
353 }
354
355 RawContext::~RawContext()
356 {
357   SIMIX_context_stack_delete(this->stack_);
358 }
359
360 void RawContext::stop()
361 {
362   Context::stop();
363   this->suspend();
364 }
365
366 void RawContextFactory::run_all()
367 {
368 #ifdef ADAPTIVE_THRESHOLD
369   if (raw_context_adaptative)
370     run_all_adaptative();
371   else
372 #endif
373   if (raw_context_parallel)
374     run_all_parallel();
375   else
376     run_all_serial();
377 }
378
379 void RawContextFactory::run_all_serial()
380 {
381   smx_process_t first_process =
382       xbt_dynar_get_as(simix_global->process_to_run, 0, smx_process_t);
383   raw_process_index = 1;
384   static_cast<RawContext*>(first_process->context)->resume_serial();
385 }
386
387 void RawContextFactory::run_all_parallel()
388 {
389 #if HAVE_THREAD_CONTEXTS
390   raw_threads_working = 0;
391   if (raw_parmap == nullptr)
392     raw_parmap = xbt_parmap_new(
393       SIMIX_context_get_nthreads(), SIMIX_context_get_parallel_mode());
394   xbt_parmap_apply(raw_parmap,
395       [](void* arg) {
396         smx_process_t process = static_cast<smx_process_t>(arg);
397         RawContext* context = static_cast<RawContext*>(process->context);
398         context->resume_parallel();
399       },
400       simix_global->process_to_run);
401 #else
402   xbt_die("You asked for a parallel execution, but you don't have any threads.");
403 #endif
404 }
405
406 void RawContext::suspend()
407 {
408   if (raw_context_parallel)
409     RawContext::suspend_parallel();
410   else
411     RawContext::suspend_serial();
412 }
413
414 void RawContext::suspend_serial()
415 {
416   /* determine the next context */
417   RawContext* next_context = nullptr;
418   unsigned long int i;
419   i = raw_process_index++;
420   if (i < xbt_dynar_length(simix_global->process_to_run)) {
421     /* execute the next process */
422     XBT_DEBUG("Run next process");
423     next_context = (RawContext*) xbt_dynar_get_as(
424         simix_global->process_to_run, i, smx_process_t)->context;
425   }
426   else {
427     /* all processes were run, return to maestro */
428     XBT_DEBUG("No more process to run");
429     next_context = (RawContext*) raw_maestro_context;
430   }
431   SIMIX_context_set_current(next_context);
432   raw_swapcontext(&this->stack_top_, next_context->stack_top_);
433 }
434
435 void RawContext::suspend_parallel()
436 {
437 #if HAVE_THREAD_CONTEXTS
438   /* determine the next context */
439   smx_process_t next_work = (smx_process_t) xbt_parmap_next(raw_parmap);
440   RawContext* next_context = nullptr;
441
442   if (next_work != NULL) {
443     /* there is a next process to resume */
444     XBT_DEBUG("Run next process");
445     next_context = (RawContext*) next_work->context;
446   }
447   else {
448     /* all processes were run, go to the barrier */
449     XBT_DEBUG("No more processes to run");
450     uintptr_t worker_id = (uintptr_t)
451       xbt_os_thread_get_specific(raw_worker_id_key);
452     next_context = (RawContext*) raw_workers_context[worker_id];
453     XBT_DEBUG("Restoring worker stack %zu (working threads = %zu)",
454         worker_id, raw_threads_working);
455   }
456
457   SIMIX_context_set_current(next_context);
458   raw_swapcontext(&this->stack_top_, next_context->stack_top_);
459 #endif
460 }
461
462 void RawContext::resume()
463 {
464   if (raw_context_parallel)
465     resume_parallel();
466   else
467     resume_serial();
468 }
469
470 void RawContext::resume_serial()
471 {
472   SIMIX_context_set_current(this);
473   raw_swapcontext(&raw_maestro_context->stack_top_, this->stack_top_);
474 }
475
476 void RawContext::resume_parallel()
477 {
478 #if HAVE_THREAD_CONTEXTS
479   uintptr_t worker_id = __sync_fetch_and_add(&raw_threads_working, 1);
480   xbt_os_thread_set_specific(raw_worker_id_key, (void*) worker_id);
481   RawContext* worker_context = (RawContext*) SIMIX_context_self();
482   raw_workers_context[worker_id] = worker_context;
483   XBT_DEBUG("Saving worker stack %zu", worker_id);
484   SIMIX_context_set_current(this);
485   raw_swapcontext(&worker_context->stack_top_, this->stack_top_);
486 #else
487   xbt_die("Parallel execution disabled");
488 #endif
489 }
490
491 /**
492  * \brief Resumes all processes ready to run.
493  */
494 #ifdef ADAPTIVE_THRESHOLD
495 void RawContectFactory::run_all_adaptative()
496 {
497   unsigned long nb_processes = xbt_dynar_length(simix_global->process_to_run);
498   unsigned long threshold = SIMIX_context_get_parallel_threshold();
499   reached_seq_limit = (seq_sched_round % SCHED_ROUND_LIMIT == 0);
500   reached_par_limit = (par_sched_round % SCHED_ROUND_LIMIT == 0);
501
502   if(reached_seq_limit && reached_par_limit){
503     par_ratio = (par_proc_that_ran != 0) ? (par_time / (double)par_proc_that_ran) : 0;
504     seq_ratio = (seq_proc_that_ran != 0) ? (seq_time / (double)seq_proc_that_ran) : 0;
505     if(seq_ratio > par_ratio){
506        if(nb_processes < avg_par_proc) {
507           threshold = (threshold>2) ? threshold - 1 : threshold ;
508           SIMIX_context_set_parallel_threshold(threshold);
509         }
510     } else {
511         if(nb_processes > avg_seq_proc){
512           SIMIX_context_set_parallel_threshold(threshold+1);
513         }
514     }
515   }
516
517   if (nb_processes >= SIMIX_context_get_parallel_threshold()) {
518     simix_global->context_factory->suspend = smx_ctx_raw_suspend_parallel;
519     if (nb_processes < par_window){
520       par_sched_round++;
521       xbt_os_walltimer_start(round_time);
522       smx_ctx_raw_runall_parallel();
523       xbt_os_walltimer_stop(round_time);
524       par_time += xbt_os_timer_elapsed(round_time);
525
526       prev_avg_par_proc = avg_par_proc;
527       delta = nb_processes - avg_par_proc;
528       avg_par_proc = (par_sched_round==1) ? nb_processes : avg_par_proc + delta / (double) par_sched_round;
529
530       if(par_sched_round>=2){
531         s_par_proc = s_par_proc + (nb_processes - prev_avg_par_proc) * delta;
532         sd_par_proc = sqrt(s_par_proc / (par_sched_round-1));
533         par_window = (int) (avg_par_proc + sd_par_proc);
534       }else{
535         sd_par_proc = 0;
536       }
537
538       par_proc_that_ran += nb_processes;
539     } else{
540       smx_ctx_raw_runall_parallel();
541     }
542   } else {
543     simix_global->context_factory->suspend = smx_ctx_raw_suspend_serial;
544     if(nb_processes > seq_window){
545       seq_sched_round++;
546       xbt_os_walltimer_start(round_time);
547       smx_ctx_raw_runall_serial();
548       xbt_os_walltimer_stop(round_time);
549       seq_time += xbt_os_timer_elapsed(round_time);
550
551       prev_avg_seq_proc = avg_seq_proc;
552       delta = (nb_processes-avg_seq_proc);
553       avg_seq_proc = (seq_sched_round==1) ? nb_processes : avg_seq_proc + delta / (double) seq_sched_round;
554
555       if(seq_sched_round>=2){
556         s_seq_proc = s_seq_proc + (nb_processes - prev_avg_seq_proc)*delta;
557         sd_seq_proc = sqrt(s_seq_proc / (seq_sched_round-1));
558         seq_window = (int) (avg_seq_proc - sd_seq_proc);
559       } else {
560         sd_seq_proc = 0;
561       }
562
563       seq_proc_that_ran += nb_processes;
564     } else {
565       smx_ctx_raw_runall_serial();
566     }
567   }
568 }
569
570 #else
571
572 // TODO
573 void RawContextFactory::run_all_adaptative()
574 {
575   unsigned long nb_processes = xbt_dynar_length(simix_global->process_to_run);
576   if (SIMIX_context_is_parallel()
577     && (unsigned long) SIMIX_context_get_parallel_threshold() < nb_processes) {
578         raw_context_parallel = true;
579         XBT_DEBUG("Runall // %lu", nb_processes);
580         this->run_all_parallel();
581     } else {
582         XBT_DEBUG("Runall serial %lu", nb_processes);
583         raw_context_parallel = false;
584         this->run_all_serial();
585     }
586 }
587 #endif
588
589 }
590 }