Skip to content
Snippets Groups Projects
Commit 11e245f4 authored by Dorian Weber's avatar Dorian Weber
Browse files

Updated the README and renamed some files.

parent 9999e61d
Branches dissertation
No related merge requests found
...@@ -2,7 +2,9 @@ ...@@ -2,7 +2,9 @@
## Introduction ## Introduction
This Rust project provides a framework for creating and running simulations using asynchronous processes. By leveraging Rust's concurrency features, it allows you to simulate various scenarios, making it a valuable tool for studying the mechanics of simulation libraries in projects involving complex, process-driven models. This Rust project provides a minimally-viable library for creating and running simulations using asynchronous processes. By leveraging Rust's concurrency features, it allows you to simulate various scenarios, making it a valuable tool for studying the mechanics of simulation libraries in projects involving complex, process-based models.
This simulation core is not meant to be used in production code, see [odem-rs] for an earnest attempt.
## Features ## Features
...@@ -14,22 +16,15 @@ The project includes: ...@@ -14,22 +16,15 @@ The project includes:
- `barbershop` - `barbershop`
- `ferry` - `ferry`
- `philosophers` - `philosophers`
- **SLX Code (`slx`)**: Contains SLX code for the same examples, including benchmarking provisions with real-time measurements. - **Decompressor-Tokenizer (`bin/coroutines.rs`)**: An educational example with an executor, a corresponding spawner, and a channel that shows the mechanism without using unsafe code.
- **C++ Code (`cpp`)**: Contains three subprojects for the examples using ODEMx. - **Decompressor-Tokenizer (`c/coroutines.c`)**: The same example implemented manually in C to illustrate the transformation performed by the Rust compiler on `async`/`await`.
- **C Code (`c`)**: Demonstrates the transformation of an ordinary routine into a coroutine for a decompressor-tokenizer example.
- **ODEMx (`odemx-lite`)**: A clean version without dependencies, compiled using a C++14-conforming compiler to run benchmarks.
## Getting Started ## Getting Started
### Prerequisites ### Prerequisites
- **Rust Compiler**: Latest stable version is recommended. [Install Rust][Rust-compiler] - **Rust Compiler**: Latest stable version is recommended. [Install Rust][Rust-compiler]
- **C++14-Conforming Compiler**: Required for the ODEMx benchmarks. Popular compilers like MSVC, Clang, MinGW, and GCC should work out of the box. - no other dependencies
- **SLX Compiler**: Required for the SLX benchmarks (both student and full versions work).
#### Note on SLX Compiler
Currently, obtaining the SLX compiler may be challenging because the creator, James O. Henriksen, passed away in 2020, and the company's original website is no longer available. If you wish to obtain the free version of SLX for benchmarking, please contact me directly.
### Building the Project ### Building the Project
...@@ -72,3 +67,4 @@ possible. Extra-special thanks to Lukas Markeffsky for helping me to refine this ...@@ -72,3 +67,4 @@ possible. Extra-special thanks to Lukas Markeffsky for helping me to refine this
discussions and by resolving its soundness problems through a refactoring of the code. discussions and by resolving its soundness problems through a refactoring of the code.
[Rust-compiler]: https://rust.sh [Rust-compiler]: https://rust.sh
[odem-rs]: https://crates.io/crates/odem-rs
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.SUFFIXES: .SUFFIXES:
.PHONY: all run clean .PHONY: all run clean
TAR = decompress-tokenize TAR = coroutines
SRC = $(wildcard *.c) SRC = $(wildcard *.c)
OBJ = $(SRC:%.c=%.o) OBJ = $(SRC:%.c=%.o)
DEP = $(OBJ:%.o=%.d) DEP = $(OBJ:%.o=%.d)
......
...@@ -26,7 +26,7 @@ enum Tag { WORD, PUNCT }; ...@@ -26,7 +26,7 @@ enum Tag { WORD, PUNCT };
/* Names of the token tags. */ /* Names of the token tags. */
static const char *TOKEN_TAG[] = { static const char *TOKEN_TAG[] = {
[WORD] = "WORD", [PUNCT] = "PUNCT" [WORD] = "Word", [PUNCT] = "Punct"
}; };
/* Token type with tag and value. */ /* Token type with tag and value. */
......
import <h7>
// enable this macro for the benchmark
// #define BENCH
module barbershop {
// globally shared data
facility joe;
random_variable wait_time;
// helper constants
constant int BENCH_SAMPLES = 100;
constant double SIM_DURATION = 60.0*24.0*7.0*3.0; // simulation runs over 3 weeks
constant int SEED_A = 100000;
constant int SEED_S = 200000;
// Customer process with access to the barber and a random processing delay.
class Customer(double _delay) {
double delay = _delay;
actions {
// access the barber and record the time for the report
double arrival_time = time;
seize joe;
tabulate wait_time = time - arrival_time;
// spend time
advance delay;
// release the barber
release joe;
}
}
procedure barbershop(double duration) {
// pseudo random number generators referenced from within the main process
rn_stream rng_a seed = SEED_A;
rn_stream rng_s seed = SEED_S;
fork {
forever {
advance rv_uniform(rng_a, 12.0, 24.0);
if (time >= duration) terminate;
activate new Customer(
rv_uniform(rng_s, 12.0, 18.0)
);
}
}
// wait until the store closes
advance duration;
// finish processing the queue (no more customers arrive)
wait until FNU(joe);
}
procedure main(int argc, string(*) argv[*]) {
// interpret the optional command line arguments for simulation
// duration and number of timed runs in this batch
double sim_duration = SIM_DURATION;
int bench_samples = BENCH_SAMPLES;
if (argc > 1) {
read string = argv[1] (sim_duration);
if (argc > 2) {
read string = argv[2] (bench_samples);
}
}
#ifdef BENCH
random_variable samples;
double real_start, real_end;
int i;
// remove the random variable from the system as to prevent resets on 'clear system'
remove &samples from random_variable_set;
for (i = 1; i <= bench_samples; ++i) {
real_start = real_time();
barbershop(sim_duration);
real_end = real_time();
tabulate samples = real_end - real_start;
empty rn_stream_set;
clear system;
}
print(sample_sum(samples)) "_.________";
#else
barbershop(sim_duration);
report wait_time;
#endif
}
}
\ No newline at end of file
import <h7>
// enable this macro for the benchmark
// #define BENCH
module ferry {
constant int BENCH_SAMPLES = 100;
constant double SIM_DURATION = 24.0*60.0*7.0;
constant double HARBOR_DISTANCE = 10.0;
constant double FERRY_TIMEOUT = 5.0;
constant int FERRY_CAPACITY = 5;
constant int FERRY_COUNT = 2;
constant int HARBOR_COUNT = 4;
random_variable ferry_cargo_len;
random_variable ferry_load_time;
random_variable car_wait_time;
passive class Car(double duration) {
double arrival_time = time;
double load_duration = duration;
}
class Pier {
rn_stream rng;
set(Car) ranked FIFO landing_site;
actions {
double load_duration;
forever {
advance rv_expo(rng, 10.0);
do load_duration = rv_normal(rng, 0.5, 0.2);
while (load_duration < 0.0);
place new Car(load_duration) into landing_site;
}
}
}
class Ferry(int _capacity, double _timeout, double _travel_time) {
pointer(Car) cargo[_capacity];
double timeout = _timeout;
double travel_time = _travel_time;
set(Pier) ranked FIFO piers;
pointer(Pier) pier;
pointer(Car) car;
actions {
double begin_loading, begin_waiting;
int len = 0;
int i;
forever {
for (pier = each Pier in piers) {
// unload the cars
for (i = 1; i <= len; ++i) {
advance cargo[i]->load_duration;
}
len = 0;
begin_loading = time;
// wait until new cars arrive or a timeout occurs
while (len < array_upper_bound(cargo, 1)) {
begin_waiting = time;
wait until pier->landing_site.size > 0
|| time >= begin_waiting + timeout;
car = first Car in pier->landing_site;
if (car != NULL) {
// a car arrived in time
remove car from pier->landing_site;
tabulate car_wait_time = time - car->arrival_time;
advance car->load_duration;
cargo[++len] = car;
} else {
// the timeout has been triggered
break;
}
}
tabulate ferry_load_time = time - begin_loading;
tabulate ferry_cargo_len = len;
// travel to the next harbor
advance travel_time;
}
}
}
}
procedure ferry(double duration, int ferries, int harbors) {
pointer(Pier) ports[harbors];
pointer(Ferry) ferry;
pointer(Car) car;
int i, j;
// create all of the harbors
for (i = 1; i <= harbors; ++i) {
ports[i] = new Pier;
activate ports[i];
}
// create all of the ferries
for (i = 1; i <= ferries; ++i) {
ferry = new Ferry(FERRY_CAPACITY, FERRY_TIMEOUT, HARBOR_DISTANCE);
for (j = 1; j <= harbors; ++j)
place ports[(i + j - 2) % harbors + 1] into ferry->piers;
activate ferry;
}
// await the end of the simulation
advance duration;
// take cars into account that weren't picked up by a ferry
for (i = 1; i <= harbors; ++i) {
for (car = each Car in ports[i]->landing_site) {
tabulate car_wait_time = time - car->arrival_time;
}
}
}
procedure main(int argc, string(*) argv[*]) {
// interpret the optional command line arguments for simulation
// duration and number of timed runs in this batch
double sim_duration = SIM_DURATION;
int bench_samples = BENCH_SAMPLES;
if (argc > 1) {
read string = argv[1] (sim_duration);
if (argc > 2) {
read string = argv[2] (bench_samples);
}
}
#ifdef BENCH
random_variable samples;
double real_start, real_end;
int i;
// remove the random variable from the system as to prevent resets on 'clear system'
remove &samples from random_variable_set;
for (i = 1; i <= bench_samples; ++i) {
real_start = real_time();
ferry(sim_duration, FERRY_COUNT, HARBOR_COUNT);
real_end = real_time();
tabulate samples = real_end - real_start;
empty rn_stream_set;
clear system;
}
print(sample_sum(samples)) "_.________";
#else
ferry(sim_duration, FERRY_COUNT, HARBOR_COUNT);
report system;
#endif
}
}
\ No newline at end of file
import <h7>
#define SLX2
// enable this macro for the benchmark
// #define BENCH
module philosophers {
constant int BENCH_SAMPLES = 100;
constant int PHILOSOPHER_COUNT = 5;
constant int EXPERIMENT_COUNT = 500;
random_variable sim_duration;
passive class Table(int count) {
control boolean forks[count];
control int forks_held = 0;
control int forks_awaited = 0;
clear {
int i;
for (i = 1; i <= array_upper_bound(forks, 1); ++i) {
forks[i] = FALSE;
}
forks_held = 0;
forks_awaited = 0;
}
// blocks until the requested fork is available and acquires it
method acquire_fork(int i) {
int num = ((i - 1) % array_upper_bound(forks, 1)) + 1;
forks_awaited++;
wait until forks[num] == FALSE;
forks[num] = TRUE;
forks_awaited--;
forks_held++;
}
// returns the requested fork to the table
method release_fork(int i) {
forks[((i - 1) % array_upper_bound(forks, 1)) + 1] = FALSE;
forks_held--;
}
}
class Philosopher(pointer(Table) my_table, int my_seat) {
pointer(Table) table = my_table;
int seat = my_seat;
rn_stream rng;
actions {
forever {
double eating_duration;
// spend some time pondering the nature of things
advance rv_expo(rng, 1.0);
// acquire the first fork
table->acquire_fork(seat);
// introduce an artificial delay to leave room for deadlocks
advance rv_uniform(rng, 0.1, 0.2);
// acquire the second fork
table->acquire_fork(seat + 1);
// spend some time eating
do eating_duration = rv_normal(rng, 0.5, 0.2);
while (eating_duration < 0.0);
advance eating_duration;
// release the forks
table->release_fork(seat + 1);
table->release_fork(seat);
}
}
}
procedure philosophers(int count, int reruns) {
pointer(Table) table = new Table(count);
pointer(Philosopher) philos[count];
int i, j;
// create the philosopher-processes and seat them
for (i = 1; i <= count; ++i) {
philos[i] = new Philosopher(table, i);
activate philos[i];
}
// since we want to collect statistics over several runs,
// exclude the random variable from the system
remove &sim_duration from random_variable_set;
// run the simulation a number of times
for (j = 1; j <= reruns; ++j) {
// wait for the precise configuration indicating a deadlock
wait until table->forks_held == count
&& table->forks_awaited == count;
// tabulate the current system time
tabulate sim_duration = time;
// reset the table as well as the simulation system
clear *table;
clear system;
// activate another set of philosophers
for (i = 1; i <= count; ++i) {
activate philos[i];
}
}
// re-insert the random variable into the system again
place &sim_duration into random_variable_set;
}
procedure main(int argc, string(*) argv[*]) {
// interpret the optional command line arguments for number
// of experiments per run and number of timed runs in this batch
int experiment_count = EXPERIMENT_COUNT;
int bench_samples = BENCH_SAMPLES;
if (argc > 1) {
read string = argv[1] (experiment_count);
if (argc > 2) {
read string = argv[2] (bench_samples);
}
}
#ifdef BENCH
random_variable samples;
double real_start, real_end;
int i;
// remove the random variable from the system as to prevent resets on 'clear system'
remove &samples from random_variable_set;
for (i = 1; i <= bench_samples; ++i) {
real_start = real_time();
philosophers(PHILOSOPHER_COUNT, experiment_count);
real_end = real_time();
tabulate samples = real_end - real_start;
empty rn_stream_set;
clear system;
}
print(sample_sum(samples)) "_.________";
#else
philosophers(PHILOSOPHER_COUNT, experiment_count);
report sim_duration;
#endif
}
}
\ No newline at end of file
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment