Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Sleeping Barber #46

Merged
merged 2 commits into from
Sep 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 64 additions & 74 deletions Rust/Savina/src/concurrency/SleepingBarber.lf
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@
* barber and the factory operate in parallel. Since the complexity of
* computations involved in this benchmark is low, this isn't a problem for
* performance though.
*
*
* @author Christian Menard
* @author Hannes Klein
* @author Johannes Hayeß
*/

target Rust {
build-type: Release,
cargo-features: ["cli"],
Expand All @@ -56,79 +56,70 @@ import BenchmarkRunner from "../lib/BenchmarkRunner.lf";
reactor CustomerFactory(numCustomers:usize(2000), averageProductionRate:usize(1000)) {
state num_customers(numCustomers);
state average_production_rate(averageProductionRate);

input start: unit;
output finished: unit;

output sendCustomer: usize;
input[numCustomers] customerDone: unit;
input[numCustomers] customerReturned: unit;

physical action createNextCustomer: unit;
physical action sendCustomerAgain: usize;

state doneCustomers: usize(0);
state attempts: usize(0);
state next_customer_id: usize(0);
state random: PseudoRandomGenerator;

preamble {=
use crate::pseudo_random::PseudoRandomGenerator;
use crate::reactors::barber::busy_wait;
=}

reaction(start) -> createNextCustomer {=
// reset state
self.doneCustomers = 0;
self.attempts = 0;
self.next_customer_id = 0;
self.random = PseudoRandomGenerator::default();
// start "creating" customers
let create_next_customer = createNextCustomer.clone();
ctx.spawn_physical_thread(move |link| {
link.schedule_physical(&create_next_customer, Asap).unwrap();
});
ctx.schedule(createNextCustomer, Asap);
=}

reaction(createNextCustomer) -> sendCustomer, createNextCustomer {=
// get random production delay
let delay = self.random.gen_range(0..self.average_production_rate as u32);
busy_wait(delay);
let delay = self.random.next_in_range(0..self.average_production_rate as i64);
busy_wait(delay.into());

// send the new customer to the waiting room
info!("Factory: Send customer {} to the waiting room", self.next_customer_id);
self.attempts += 1;
ctx.set(sendCustomer, self.next_customer_id);

self.next_customer_id += 1;
if self.next_customer_id < self.num_customers {
// schedule again
let create_next_customer = createNextCustomer.clone();
ctx.spawn_physical_thread(move |link| {
link.schedule_physical(&create_next_customer, Asap).unwrap();
});
ctx.schedule(createNextCustomer, Asap);
}
=}

reaction(sendCustomerAgain) -> sendCustomer {=
let customer_id = ctx.get(sendCustomerAgain).unwrap();
info!("Factory: Send customer {} again to the waiting room", customer_id);
self.attempts += 1;

ctx.set(sendCustomer, customer_id);
=}

reaction(customerReturned) -> sendCustomerAgain {=
for (i, customer) in customerReturned.into_iter().enumerate() {
if ctx.is_present(&customer) {
let send_customer_again = sendCustomerAgain.clone();
ctx.spawn_physical_thread(move |link| {
link.schedule_physical_with_v(&send_customer_again, Some(i), Asap).unwrap();
});
ctx.schedule_with_v(sendCustomerAgain, Some(i), Asap);
}
}
=}

reaction(customerDone) -> finished {=
for customer in customerDone {
if ctx.is_present(&customer) {
Expand All @@ -145,33 +136,33 @@ reactor CustomerFactory(numCustomers:usize(2000), averageProductionRate:usize(10

reactor WaitingRoom(capacity:usize(1000), numCustomers:usize(2000)) {
state capacity(capacity);

input reset_state: unit;

input receiveCustomer: usize;

output[numCustomers] full: unit;
output[numCustomers] wait: unit;

input barberNext: unit;
output barberEnter: usize;
output barberWait: unit;

state queue: VecDeque<usize>;
state barberAsleep: bool(true);

preamble {=
use std::collections::VecDeque;
=}

reaction(reset_state) {=
self.barberAsleep = true;
=}
=}

reaction(receiveCustomer) -> full, wait, barberEnter {=
let customer_id = ctx.get(receiveCustomer).unwrap();
info!("Room: Customer {} tries to enter", customer_id);

if self.queue.len() == self.capacity {
ctx.set(full.get(customer_id), ());
} else {
Expand All @@ -184,7 +175,7 @@ reactor WaitingRoom(capacity:usize(1000), numCustomers:usize(2000)) {
}
}
=}

reaction(barberNext) -> barberEnter, barberWait {=
if self.queue.is_empty() {
self.barberAsleep = true;
Expand All @@ -197,28 +188,28 @@ reactor WaitingRoom(capacity:usize(1000), numCustomers:usize(2000)) {

reactor Customer(bank_index:usize(0)) {
state bank_index(bank_index);

input roomFull: unit;
input wait: unit;
input startCutting: unit;
input doneCutting: unit;
input doneCutting: unit;

output returned: unit;
output done: unit;

reaction(roomFull) -> returned {=
info!("Customer {}: The wating room is full. I am leaving.", self.bank_index);
ctx.set(returned, ());
=}
=}

reaction(wait) {=
info!("Customer {}: I will wait.", self.bank_index);
=}

reaction(startCutting) {=
info!("Customer {}: I am now being served.", self.bank_index);
=}

reaction(doneCutting) -> done {=
info!("Customer {}: I have been served.", self.bank_index);
ctx.set(done, ());
Expand All @@ -227,64 +218,63 @@ reactor Customer(bank_index:usize(0)) {

reactor Barber(averageHaircutRate:usize(1000), numCustomers:usize(2000)) {
state average_haircut_rate(averageHaircutRate);

input reset_state: unit;
input enter: usize;
input wait: unit;

output[numCustomers] startCutting: unit;
output[numCustomers] doneCutting: unit;
output next: unit;

physical action done: usize;

state random: PseudoRandomGenerator;

reaction(reset_state) {=
self.random = PseudoRandomGenerator::default();
=}
=}

reaction(done) -> doneCutting, next {=
let customer_id = ctx.get(done).unwrap();
ctx.set(doneCutting.get(customer_id), ());
ctx.set(next, ());
=}

reaction(enter) -> startCutting, done {=
let customer_id = ctx.get(enter).unwrap();
ctx.set(startCutting.get(customer_id), ());

// calculate a random delay
let delay = self.random.gen_range(0..self.average_haircut_rate as u32) + 10;

let delay: u32 =
<RandomValue as Into<u32>>::into(self.random.next_in_range(0..self.average_haircut_rate as i64))
+ 10;

// do the actual cutting and apply a physical delay
info!("Barber: Processing customer {}", customer_id);
busy_wait(delay);

// notify the customer
let d = done.clone();
ctx.spawn_physical_thread(move |link| {
link.schedule_physical_with_v(&d, Some(customer_id), Asap).unwrap();
});
ctx.schedule_with_v(done, Some(customer_id), Asap);
=}

reaction(wait) {=
info!("Barber: No customers. Going to sleep.");
=}

preamble {=
use crate::pseudo_random::PseudoRandomGenerator;
use crate::pseudo_random::{PseudoRandomGenerator, RandomValue};
use rand::prelude::*;

pub fn busy_wait(limit: u32) -> u32 {
let mut test = 0;
let mut rng = rand::thread_rng();

for _ in 0..limit {
let _: usize = rng.gen();
test += 1;
}

test
}
=}
Expand All @@ -296,13 +286,13 @@ main reactor (numIterations:usize(12), waitingRoomSize:usize(1000), averageProdu
state average_production_rate(averageProductionRate);
state average_haircut_rate(averageHaircutRate);
state num_haircuts(numHaircuts);

runner = new BenchmarkRunner(num_iterations=numIterations);
factory = new CustomerFactory(numCustomers=numHaircuts, averageProductionRate=averageProductionRate);
room = new WaitingRoom(capacity=waitingRoomSize, numCustomers=numHaircuts);
barber = new Barber(averageHaircutRate=averageHaircutRate, numCustomers=numHaircuts)
customers = new[numHaircuts] Customer();

reaction(startup) {=
print_benchmark_info("SleepingBarberReactorLFRustBenchmark");
print_args!(
Expand All @@ -319,10 +309,10 @@ main reactor (numIterations:usize(12), waitingRoomSize:usize(1000), averageProdu
);
print_system_info();
=}

(runner.start)+ -> factory.start, barber.reset_state, room.reset_state;
factory.finished -> runner.finished;

factory.sendCustomer -> room.receiveCustomer;
room.full -> customers.roomFull;
room.wait -> customers.wait;
Expand All @@ -332,9 +322,9 @@ main reactor (numIterations:usize(12), waitingRoomSize:usize(1000), averageProdu
barber.startCutting -> customers.startCutting;
barber.doneCutting -> customers.doneCutting;
customers.done -> factory.customerDone;

customers.returned -> factory.customerReturned;

preamble {=
use crate::{print_args,reactors::benchmark_runner::{print_system_info, print_benchmark_info}};
=}
Expand Down
6 changes: 6 additions & 0 deletions Rust/Savina/src/lib/pseudo_random.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ impl Into<i32> for RandomValue {
}
}

impl Into<u32> for RandomValue {
fn into(self) -> u32 {
*self as u32
}
}

impl Into<u64> for RandomValue {
fn into(self) -> u64 {
*self as u64
Expand Down
14 changes: 11 additions & 3 deletions runner/conf/benchmark/savina_concurrency_barber.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ targets:
waiting_room_size: ["-D", "waitingRoomSize=<value>"]
production_rate: ["-D", "averageProductionRate=<value>"]
haircut_rate: ["-D", "averageHaircutRate=<value>"]



lf-rust:
copy_sources:
- "${bench_path}/Rust/Savina/src/lib"
- "${bench_path}/Rust/Savina/src/concurrency"
lf_file: "concurrency/SleepingBarber.lf"
binary: "sleeping_barber"
run_args:
haircuts: ["--main-num-haircuts", "<value>"]
waiting_room_size: ["--main-waiting-room-size", "<value>"]
production_rate: ["--main-average-production-rate", "<value>"]
haircut_rate: ["--main-average-haircut-rate", "<value>"]