Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Fix initialization order.
[simgrid.git] / src / simix / README_attempt_without_stack
1 This file was the README in a directory constituting an attempt to
2 code a new trace replayer for MPI actions, aiming at maximal
3 performances. Modifying it is not for faint of heart, since it could
4 be compared to a mixure of the assembly and basic programming
5 philosophy (in worse) reserved to SimGrid experts.
6
7 It was so difficult to use that this has been removed from the SVN.
8 This file is thus mainly for history.
9
10 Shiny side: glance at interface
11 ===============================
12
13 It uses a new simix context factory: state_machine. Each user process
14 is a state machine. There is no system mystery such as pthread or
15 ucontexts to save its stack. As a result, there is no stack. Each
16 user process only have a user-provided structure describing its state,
17 and only compute its next state based on that. Your main() can be as
18 simple as:
19
20   #include "replay.h"
21   
22   int main() {
23     SG_replay_init(&argc,argv);
24     SG_replay_set_functions(init_fun, run_fun, fini_fun);
25     SG_replay("platform.xml","deployment.xml");
26     return 0;
27   }
28
29  * init_fun: user function in charge of creating the structure for
30              each process in the simulation.
31  * run_fun: user function called each time that a process must run. It 
32             takes as first argument the structure describing the
33             current process.
34  * fini_fun: user function in charge of freeing the memory allocated to
35              the structure describing a process.
36
37 This way of organizing the code saves a *huge amount* of memory
38 (regular contexts have 128kb stacks per user process, threads are
39 even more expensive) and greatly speeds things up (there is absolutely
40 no nothing to ask to the system, and everything can be done in user
41 space).
42
43 A simple to use and efficient trace parser is also provided:
44   /* constructor/destructor */
45   replay_trace_reader_t replay_trace_reader_new(const char*filename);
46   void replay_trace_reader_free(replay_trace_reader_t *reader);
47   /* get a new event. Don't free the content, strdup what you want to
48      keep after next call to reader_get() */
49   const char **replay_trace_reader_get(replay_trace_reader_t r);
50   /* return a "file:pos" description of the last thing we read. */
51   const char *replay_trace_reader_position(replay_trace_reader_t r);
52 Check replay_trace_reader.c for souce code, and replay_MPI.c for
53 example of use.
54
55
56
57 Dark side: restrictions on user code
58 ====================================
59
60 The incredible performance of this approach comes at a price: using
61 SimGrid this way is a *real* pain in the ass. You cannot use MSG nor
62 SMPI nor nothing (because none of these interfaces were coded
63 with the *extrem* requirement of the state_machine in mind), and you
64 can only rely on SIMIX. From SIMIX, you can only use simcalls (ie, the
65 simcall_* functions). Moreover, you must know that each blocking
66 simcall will result in an interruption of your execution flow. 
67
68 Let's take an example: If your code contains:
69    smx_action_t act = simcall_comm_isend(......);
70    simcall_comm_wait(act);
71    simcall_comm_destroy(act);
72    
73 The execution flow is interrupted brutally somewhere within
74 simcall_comm_isend(), the variable act will never be set (and any
75 code written after the first line is discarded).
76
77 Indeed each SIMIX simcall results in an interruption of the calling
78 process, but in state_machine there is only one system stack and the
79 whole state describing the process is in the structure describing it.
80 So, when we need to remove one process from the system, to pause it,
81 we do it the hard way: the stack [of maestro] is restored to the state
82 in which maestro put it, whatever what the user process put on it.
83
84 In short, each time simix wants to interrupt a process, state_machine
85 does a longjmp(2) to the point just before calling the user code. As a
86 result, each time you do a simcall, your stack is destroyed to restore
87 it in the state where maestro put it before calling your code.
88
89 This means that you cannot do anything after a simcall, and that the
90 stack is not a safe storing area for your data.
91
92 So, you have to write your code as a state machine, with a big ugly
93 switch. The previous code must be written something like:
94
95 run_fun(globals, res) {
96
97   switch (globals->state) {
98   case l1: /* default value st. we take that branch the first time */
99     globals->state = l2;
100     simcall_comm_isend(....); /* syscall=>hard interrupt on our code */
101   case l2: /* we'll take that branch the second time we're scheduled */
102     globals->comm = res;
103     globals->state = l3;
104     simcall_comm_wait(globals->comm); /* syscall=>interrupt */
105   case l3: 
106     globals->state = where_you_want_to_go_today;
107     simcall_comm_destroy(globals->comm);
108   }  
109 }
110
111 As you can see, the result of the /previous/ syscall is passed as second
112 argument to the run_fun().
113
114
115 Isn't all this beautifully awful?? A few gotos in your code are just
116 what you need to go 20 years back to the good old time of gwbasic...