Diferenças
Aqui você vê as diferenças entre duas revisões dessa página.
Ambos lados da revisão anterior Revisão anterior Próxima revisão | Revisão anterior | ||
software:simulation [2009/08/29 16:43] – maziero | software:simulation [2020/08/18 22:22] (atual) – edição externa 127.0.0.1 | ||
---|---|---|---|
Linha 1: | Linha 1: | ||
+ | ====== Simpatica ====== | ||
+ | |||
+ | [ [[software: | ||
+ | |||
+ | SIMPATICA is a small library for building discrete event simulations. Its main features are: | ||
+ | |||
+ | * Written in ANSI C. | ||
+ | * Its internal structure is simple and easy to understand, allowing its use in the classroom. | ||
+ | * Extensively tested in Linux environments (32 and 64 bits). | ||
+ | * Based on the actors/ | ||
+ | * Conceived aiming a low memory footprint; this allows to build large simulations, | ||
+ | * Excellent performance, | ||
+ | * The scheduler queue is implemented using a binary heap, allowing it to achieve a good performance in large-scale simulations. | ||
+ | * Open source software (GNU GPL License) | ||
+ | |||
+ | The SIMPATICA library allows to build discrete event simulations based on the [[http:// | ||
+ | |||
+ | * **Task**: is an active entity, whose behavior is defined by a C function (exactly like a thread body). Each task has an unique identifier in the simulation. Tasks can produce and consume messages, which are transferred between tasks through queues. | ||
+ | * **Queue**: is a message repository, in which messages are stored according the arrival date (FIFO ordering). Tasks can put messages on the queues and get messages from them. Tasks and queues are independent: | ||
+ | * **Message**: | ||
+ | |||
+ | {{ software: | ||
+ | |||
+ | ===== Files ===== | ||
+ | |||
+ | * {{software: | ||
+ | |||
+ | ===== Interface ===== | ||
+ | |||
+ | This library provides a set of ANSI C functions to build simulation models. By convention, most functions abort the simulation execution if an error occurs, printing an error message in '' | ||
+ | |||
+ | <code c> | ||
+ | void init_simulation (int maxTasks, int maxQueues) | ||
+ | </ | ||
+ | |||
+ | Initializes the internal structures needed for each simulation. This function should be called only once, at the beginning of C program containing the model. | ||
+ | |||
+ | The '' | ||
+ | |||
+ | <code c> | ||
+ | void run_simulation (double maxTime) | ||
+ | </ | ||
+ | |||
+ | Runs the simulation until the simulation clock achieves the '' | ||
+ | |||
+ | This function can be called several times, to make a simulation to progress in time steps. The simulation clock will then advance from its last value to the '' | ||
+ | |||
+ | <code c> | ||
+ | ... // initialize and create tasks/ | ||
+ | run_simulation (1000) ; // runs the simulation from t=0 to t=1000 | ||
+ | ... // process partial results | ||
+ | run_simulation (2000) ; // continues the simulation until t=2000 | ||
+ | ... // process partial results | ||
+ | </ | ||
+ | |||
+ | <code c> | ||
+ | void kill_simulation () | ||
+ | </ | ||
+ | |||
+ | Finishes a simulation, cleaning all data structures allocated to it (tasks, queues, and messages). This call is used to restart the library, allowing to run several simulations in the same C program, sequentially. | ||
+ | |||
+ | <code c> | ||
+ | void trace_interval (double startTime, double stopTime) | ||
+ | </ | ||
+ | |||
+ | Enables trace messages when the simulation time is in the interval ['' | ||
+ | |||
+ | <code c> | ||
+ | uint task_create (void (*taskBody)(void *), void* startArg, int stackPages) | ||
+ | </ | ||
+ | |||
+ | Creates a new task. The '' | ||
+ | |||
+ | The '' | ||
+ | |||
+ | It is important to say that stacks too big consume more memory, and this lowers the maximum number of entities the simulation can handle. On the other hand, stacks too small can lead to memory access errors, sometimes undetected. The stack size depends on the task behavior: the total size of local variables, functions called by the task, etc. Simple models generally work well with stacks having 1-3 pages, but this should be evaluated in each case. | ||
+ | |||
+ | PS: the '' | ||
+ | |||
+ | Return value: the ID of the task just created. IDs are positive integers allocated in sequence (1, 2, 3, ...). The scheduler itself has ID 0. | ||
+ | |||
+ | <code c> | ||
+ | void task_exit () | ||
+ | </ | ||
+ | |||
+ | Exits the current/ | ||
+ | |||
+ | <code c> | ||
+ | void task_destroy (int task_id) | ||
+ | </ | ||
+ | |||
+ | Destroys the task indicated as a parameter, freeing its resources. | ||
+ | |||
+ | <code c> | ||
+ | void task_sleep (double t) | ||
+ | </ | ||
+ | |||
+ | Makes the current task to sleep during '' | ||
+ | |||
+ | <code c> | ||
+ | void task_passivate () | ||
+ | </ | ||
+ | |||
+ | Makes the current task to sleep indefinitely, | ||
+ | |||
+ | <code c> | ||
+ | void task_activate (int task_id, double waitTime) | ||
+ | </ | ||
+ | |||
+ | Awakes the indicated task after '' | ||
+ | |||
+ | <code c> | ||
+ | int task_id () | ||
+ | </ | ||
+ | |||
+ | Retrieves the unique ID of the current task, which is a positive integer (1, 2, 3, ...). If called within the scheduler (outside of a task) returns 0. | ||
+ | |||
+ | <code c> | ||
+ | double time_now () | ||
+ | </ | ||
+ | |||
+ | Retrieves the current value of the simulation time. | ||
+ | |||
+ | <code c> | ||
+ | int queue_create (int capacity, int policy) | ||
+ | </ | ||
+ | |||
+ | Creates a new message queue with the given capacity (maximum number of massages the queue can hold) and ordering policy. In the current version, both parameters are ignored: messages are ordered by they arrival date and queue maximum size is only limited by the available memory. | ||
+ | |||
+ | <code c> | ||
+ | int queue_destroy (int queue_id) | ||
+ | </ | ||
+ | |||
+ | Destroys the queue indicated, deleting all the messages it contains and also the data structures that implement it. | ||
+ | |||
+ | <code c> | ||
+ | void queue_stats (uint queue_id, uint *size, uint *max, | ||
+ | double *mean, double *var, ulong *put, ulong *got) | ||
+ | </ | ||
+ | |||
+ | Retrieves the following information about the indicated queue, calculate from t=0: | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | For each field, it should be given the address (pointer) of a variable that will receive the corresponding value, or NULL (0) to ignore it. For instance, the following call retrieves the current number of messages in the queue '' | ||
+ | |||
+ | <code c> | ||
+ | queue_stats (q1, &size, 0, 0, 0, 0, 0) ; | ||
+ | </ | ||
+ | |||
+ | <code c> | ||
+ | void* msg_create (short size) | ||
+ | </ | ||
+ | |||
+ | Creates a new message with the given '' | ||
+ | |||
+ | Return value: a void pointer to the new message. | ||
+ | |||
+ | <code c> | ||
+ | void msg_destroy (void *msg) | ||
+ | </ | ||
+ | |||
+ | Destroys the message indicated as parameter. All messages should be destroyed after their usage, for freeing memory to other needs. This is particularly important for large and/or long simulations. | ||
+ | |||
+ | <code c> | ||
+ | void msg_put (int queue_id, void* msg) | ||
+ | </ | ||
+ | |||
+ | Puts the indicated message at the end of the indicated queue (appends it). | ||
+ | |||
+ | <code c> | ||
+ | void* msg_get (void *msg) | ||
+ | </ | ||
+ | |||
+ | Removes the given message from the queue where it currently is, returning a pointer to it (the same pointer informed as parameter, its value does not change). | ||
+ | |||
+ | <code c> | ||
+ | void* msg_wait (int queue_id, double timeOut) | ||
+ | </ | ||
+ | |||
+ | Waits for a message in the queue indicated as parameter. The current task is suspended until receiving a message or a time-out. The '' | ||
+ | |||
+ | <code c> | ||
+ | void* msg_first (int queue_id) | ||
+ | void* msg_last (int queue_id) | ||
+ | void* msg_prev (void* msg) | ||
+ | void* msg_next (void* msg) | ||
+ | </ | ||
+ | |||
+ | Allows to " | ||
+ | |||
+ | <code c> | ||
+ | void msg_attr (void *msg, long *id, double *birth, double *sent, | ||
+ | long *creator, long *sender, int *queue) | ||
+ | </ | ||
+ | |||
+ | Retrieve the following information about the given message: | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | For each field, it should be given the address (pointer) of a variable that will receive the corresponding value, or NULL (0) to ignore it (see '' | ||
+ | |||
+ | ===== Usage ===== | ||
+ | |||
+ | Using this library is quite simple: one needs to write a C program using the library functions to define the simulation model and the simulation parameters. Then one should compile and link it with the library to get an executable file that implements the simulation: | ||
+ | |||
+ | <code bash> | ||
+ | $ cc simpatica.c model.c | ||
+ | $ a.out | ||
+ | </ | ||
+ | |||
+ | The file '' | ||
+ | |||
+ | ===== Simulation example ===== | ||
+ | |||
+ | Hereafter is presented a model in which 1,000 " | ||
+ | |||
+ | The source code for the model is (to be saved as file '' | ||
+ | |||
+ | <code c model.c> | ||
+ | #include < | ||
+ | #include < | ||
+ | #include " | ||
+ | |||
+ | // queue descriptor | ||
+ | int q1 ; | ||
+ | |||
+ | // variables to calculate mean of messages' | ||
+ | long num_msgs = 0 ; | ||
+ | double sum_times = 0.0 ; | ||
+ | |||
+ | // messages are structs with user-defined fields | ||
+ | typedef struct msg_t | ||
+ | { | ||
+ | int value ; // a random value, just to give an example | ||
+ | } msg_t ; | ||
+ | |||
+ | // source tasks' | ||
+ | void sourceBody (void *arg) | ||
+ | { | ||
+ | msg_t *msg ; | ||
+ | |||
+ | for (;;) | ||
+ | { | ||
+ | // creates a new message | ||
+ | msg = (msg_t*) msg_create (sizeof (msg_t)) ; | ||
+ | |||
+ | // fills the message with a random value | ||
+ | msg-> | ||
+ | |||
+ | // puts the message in q1 queue | ||
+ | msg_put (q1, msg) ; | ||
+ | |||
+ | // sleeps for a random amount of time | ||
+ | task_sleep (15 + random() % 5) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // sink task's body | ||
+ | void sinkBody (void *arg) | ||
+ | { | ||
+ | msg_t *msg ; | ||
+ | | ||
+ | |||
+ | for (;;) | ||
+ | { | ||
+ | // waits for a message and removes it from q1 queue | ||
+ | msg = (msg_t*) msg_get (msg_wait (q1, INFINITY)) ; | ||
+ | |||
+ | // gets the message creation date | ||
+ | msg_attr (msg, 0, & | ||
+ | |||
+ | // simulation time elapsed for processing the message | ||
+ | task_sleep (1) ; | ||
+ | |||
+ | // accumulate times | ||
+ | sum_times += (time_now() - creation_time) ; | ||
+ | num_msgs ++ ; | ||
+ | |||
+ | // destroys the message to free its resources | ||
+ | msg_destroy (msg) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | int main () | ||
+ | { | ||
+ | int i ; | ||
+ | | ||
+ | |||
+ | // prepares a simulation for 1001 tasks and one queue | ||
+ | | ||
+ | |||
+ | // creates 1000 " | ||
+ | for (i=0; i< 1000; i++) | ||
+ | task_create (sourceBody, | ||
+ | |||
+ | // creates one " | ||
+ | | ||
+ | |||
+ | // creates the q1 queue | ||
+ | q1 = queue_create (0, 0) ; | ||
+ | |||
+ | // executes the simulation until t=50000 time units | ||
+ | | ||
+ | |||
+ | // print results | ||
+ | | ||
+ | | ||
+ | |||
+ | // prints mean queue size and its variance | ||
+ | | ||
+ | | ||
+ | |||
+ | // frees simulation resources | ||
+ | | ||
+ | |||
+ | | ||
+ | } ; | ||
+ | </ | ||
+ | |||
+ | Model compilation is done through the following command line: | ||
+ | |||
+ | <code bash> | ||
+ | $ cc simpatica.c model.c | ||
+ | </ | ||
+ | |||
+ | The model execution generates the following output: | ||
+ | |||
+ | < | ||
+ | $ ./a.out | ||
+ | -- Simulation initialized, | ||
+ | -- Simulation in interval t=[0.000, 50000.000), 1001 tasks | ||
+ | -- Simulation time: 5000.000, | ||
+ | -- Simulation time: 10000.000, | ||
+ | -- Simulation time: 15000.000, | ||
+ | -- Simulation time: 20000.000, | ||
+ | -- Simulation time: 25000.000, | ||
+ | -- Simulation time: 30000.000, | ||
+ | -- Simulation time: 35000.000, | ||
+ | -- Simulation time: 40000.000, | ||
+ | -- Simulation time: 45000.000, | ||
+ | -- Simulation time: 50000.000, | ||
+ | -- Simulation completed in 6 seconds (mem: 143967Kb) | ||
+ | Mean time between msg production/ | ||
+ | Queue size: mean 1445994.474, | ||
+ | -- Simulation killed (mem: 0Kb) | ||
+ | </ | ||
+ | |||
+ | In the example above, lines beginning by "'' | ||
+ | |||
+ | ===== Compilation options ===== | ||
+ | |||
+ | There are some compilation options that allow to activate/ | ||
+ | |||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | These options can be used as follows: | ||
+ | |||
+ | <code bash> | ||
+ | $ cc -DDEBUGHEAP simpatica.c model.c | ||
+ | </ | ||
+ | |||
+ | ===== Concurrency ===== | ||
+ | |||
+ | In systems containing several tasks executing simultaneously, | ||
+ | |||
+ | In this library, a thread execution is suspended only when it requests an operation that can have an impact on the simulation time: '' | ||