From 15920fa8dad4c190f02f8ebb1ca3be08146c12ae Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sat, 6 Nov 2021 17:24:09 +0100 Subject: [PATCH 1/6] simplify C++ Philosophers benchmark --- .../Cpp/Savina/src/concurrency/Philosopher.lf | 217 ------------------ .../Savina/src/concurrency/Philosophers.lf | 182 +++++++++++++++ 2 files changed, 182 insertions(+), 217 deletions(-) delete mode 100644 benchmark/Cpp/Savina/src/concurrency/Philosopher.lf create mode 100644 benchmark/Cpp/Savina/src/concurrency/Philosophers.lf diff --git a/benchmark/Cpp/Savina/src/concurrency/Philosopher.lf b/benchmark/Cpp/Savina/src/concurrency/Philosopher.lf deleted file mode 100644 index 99b5c4436c..0000000000 --- a/benchmark/Cpp/Savina/src/concurrency/Philosopher.lf +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Copyright (C) 2020 TU Dresden - * - * This implementation stays close to the Savina implementation - * with Akka. There is no notion of time and there is no delay - * or work done when a philosopher is thinking or eating. - * - * @author Hannes Klein - * @author Christian Menard - */ - -target Cpp { - build-type : RelWithDebInfo -}; - -import BenchmarkRunner from "../BenchmarkRunner.lf"; - -public preamble {= - enum MsgType { - StartMsg, - HungryMsg, - DoneMsg, - ExitMsg, - DeniedMsg, - EatMsg - }; - - struct Message { - - MsgType type; - }; -=} - -reactor ArbitratorReactor(numPhilosophers:size_t(20)) { - - state messagesToSend: std::vector>; - state forks: std::vector; - state numExitedPhilosophers: size_t{0}; - - input inStart: void; - output outFinished: void; - - output[numPhilosophers] outPhilosophers: Message; - input[numPhilosophers] inPhilosophers: Message; - output outCounter: void; - - logical action sendMessages: void; - - reaction(startup) {= - forks.resize(numPhilosophers, false); - messagesToSend.reserve(numPhilosophers); - =} - - reaction(inStart) -> outPhilosophers, outCounter {= - - // reset state - messagesToSend.clear(); - numExitedPhilosophers = 0; - std::fill(forks.begin(), forks.end(), false); - - // reset other reactors and start execution - for(auto& p: outPhilosophers) { - p.set(Message{StartMsg}); - } - outCounter.set(); - =} - - reaction(sendMessages) -> outPhilosophers {= - for(size_t i = 0; i < messagesToSend.size(); i++) { - outPhilosophers[messagesToSend[i].first].set(messagesToSend[i].second); - } - messagesToSend.clear(); - =} - - reaction(inPhilosophers) -> outFinished, sendMessages {= - - // the order of checking the inputs gives the philosophers a priority - for(size_t i = 0; i < inPhilosophers.size(); i++) { - if(inPhilosophers[i].is_present()) { - if(inPhilosophers[i].get()->type == HungryMsg) { - - bool leftFork = forks[i]; - bool rightFork = forks[(i + 1) % numPhilosophers]; - - if(leftFork || rightFork) { - // someone else has access to the fork - //outPhilosophers[i].set(Message{DeniedMsg}); - messagesToSend.push_back(std::pair{i, Message{DeniedMsg}}); - } else { - forks[i] = true; - forks[(i + 1) % numPhilosophers] = true; - //outPhilosophers[i].set(Message{EatMsg}); - messagesToSend.push_back(std::pair{i, Message{EatMsg}}); - } - - } else if(inPhilosophers[i].get()->type == DoneMsg) { - - forks[i] = false; - forks[(i + 1) % numPhilosophers] = false; - - } else if(inPhilosophers[i].get()->type == ExitMsg) { - - numExitedPhilosophers += 1; - if(numPhilosophers == numExitedPhilosophers) { - outFinished.set(); - } - } - } - } - - // schedule sending messages here for efficiency - sendMessages.schedule(); - =} -} - -reactor PhilosopherReactor(rounds:size_t(10000)) { - - state localCounter: size_t(0); // count failed tries - state roundsSoFar: size_t(0); // count successful tries - - input inArbitrator: Message; - output outArbitrator: Message; - output outCounterDenied: size_t; - - logical action requestToEat: void; - logical action finish: void; - - reaction(requestToEat) -> outArbitrator {= - outArbitrator.set(Message{HungryMsg}); - =} - - reaction(finish) -> outArbitrator, outCounterDenied {= - outArbitrator.set(Message{ExitMsg}); - outCounterDenied.set(localCounter); - =} - - reaction(inArbitrator) -> outArbitrator, requestToEat, finish {= - - if(inArbitrator.get()->type == DeniedMsg) { - localCounter += 1; - outArbitrator.set(Message{HungryMsg}); - - } else if(inArbitrator.get()->type == EatMsg) { - roundsSoFar += 1; - outArbitrator.set(Message{DoneMsg}); - - if(roundsSoFar < rounds) { - requestToEat.schedule(); - } else { - finish.schedule(); - } - } else if(inArbitrator.get()->type == StartMsg) { - //reset state - localCounter = 0; - roundsSoFar = 0; - - //start eating - requestToEat.schedule(); - } - =} -} - -reactor CounterReactor(numLocalCounters:size_t(20)) { - - private preamble {= - #include "reactor-cpp/logging.hh" - =} - - state counter: size_t(0); - state receivedLocalCounts: size_t(0); - - input[numLocalCounters] inLocalCounters: size_t; - input inController: void; - - reaction(inController) {= - //reset local state - counter = 0L; - receivedLocalCounts = 0; - =} - - reaction(inLocalCounters) {= - for(size_t i = 0; i < inLocalCounters.size(); i++) { - if(inLocalCounters[i].is_present()) { - counter += *(inLocalCounters[i].get()); - receivedLocalCounts += 1; - } - } - - if(receivedLocalCounts >= numLocalCounters) { - reactor::log::Info() << "Counted: " << counter; - } - =} -} - -main reactor (numIterations:size_t(12), numEatingRounds:size_t(10000), numPhilosophers:size_t(20)) { - - arbitrator = new ArbitratorReactor(numPhilosophers=numPhilosophers); - runner = new BenchmarkRunner(numIterations=numIterations); - - runner.start -> arbitrator.inStart; - arbitrator.outFinished -> runner.finished; - - reaction(startup) {= - printBenchmarkInfo("PhilosopherReactorLFCppBenchmark"); - printArgs("numIterations", numIterations, "numEatingRounds", numEatingRounds, "numPhilosophers", numPhilosophers); - printSystemInfo(); - =} - - philosophers = new[numPhilosophers] PhilosopherReactor(rounds=numEatingRounds); - counter = new CounterReactor(numLocalCounters=numPhilosophers); - - arbitrator.outPhilosophers -> philosophers.inArbitrator; - philosophers.outArbitrator -> arbitrator.inPhilosophers; - arbitrator.outCounter -> counter.inController; - philosophers.outCounterDenied -> counter.inLocalCounters; - -} diff --git a/benchmark/Cpp/Savina/src/concurrency/Philosophers.lf b/benchmark/Cpp/Savina/src/concurrency/Philosophers.lf new file mode 100644 index 0000000000..9bc206e77c --- /dev/null +++ b/benchmark/Cpp/Savina/src/concurrency/Philosophers.lf @@ -0,0 +1,182 @@ +/** + * Copyright (C) 2020 TU Dresden + * + * This implementation stays close to the Savina implementation + * with Akka. There is no notion of time and there is no delay + * or work done when a philosopher is thinking or eating. + * + * @author Hannes Klein + * @author Christian Menard + */ + +target Cpp { + build-type : RelWithDebInfo, + logging: warn +}; + +import BenchmarkRunner from "../BenchmarkRunner.lf"; + +reactor Arbitrator(numPhilosophers:size_t(20)) { + + public preamble {= + enum class Reply { + INVALID = 0, + EAT = 1, + DENIED = 2, + }; + =} + + state replies: std::vector; + state forks: std::vector; + state numFinishedPhilosophers: size_t{0}; + state arbitration_id: size_t{0}; + state numRetries: size_t{0}; + + input start: void; + output allFinished: void; + + input[numPhilosophers] hungry: void; + input[numPhilosophers] done: void; + input[numPhilosophers] finished: size_t; + output[numPhilosophers] eat: void; + output[numPhilosophers] denied: void; + + logical action sendReplies; + + reaction(startup) {= + forks.resize(numPhilosophers, false); + replies.resize(numPhilosophers, Reply::INVALID); + =} + + reaction(start) {= + // reset state + numFinishedPhilosophers = 0; + numRetries = 0; + arbitration_id = 0; + std::fill(forks.begin(), forks.end(), false); + std::fill(replies.begin(), replies.end(), Reply::INVALID); + =} + + reaction(sendReplies) -> eat, denied {= + for(size_t i = 0; i < numPhilosophers; i++) { + if (replies[i] == Reply::EAT) { + eat[i].set(); + } else if (replies[i] == Reply::DENIED) { + denied[i].set(); + } + } + std::fill(replies.begin(), replies.end(), Reply::INVALID); + =} + + reaction (done) {= + for(size_t i{0}; i < numPhilosophers; i++) { + if (done[i].is_present()) { + forks[i] = false; + forks[(i + 1) % numPhilosophers] = false; + } + } + =} + + reaction(hungry) -> sendReplies {= + for(size_t i{0}; i < numPhilosophers; i++) { + size_t j = (i + arbitration_id) % numPhilosophers; + + if(hungry[j].is_present()) { + bool leftFork = forks[j]; + bool rightFork = forks[(j + 1) % numPhilosophers]; + + if(leftFork || rightFork) { + // someone else has access to the fork + replies[j] = Reply::DENIED; + } else { + forks[j] = true; + forks[(j + 1) % numPhilosophers] = true; + replies[j] = Reply::EAT; + } + } + } + arbitration_id = (arbitration_id + 1) % numPhilosophers; + sendReplies.schedule(); + =} + + reaction (finished) -> allFinished {= + for(const auto& f : finished) { + if (f.is_present()) { + numRetries += *f.get(); + numFinishedPhilosophers++; + if(numPhilosophers == numFinishedPhilosophers) { + std::cout << "Total retires: " << numRetries << '\n'; + allFinished.set(); + } + } + } + =} +} + +reactor Philosopher(bank_index: size_t{0}, rounds:size_t(10000)) { + + state numDenied: size_t(0); // count failed tries + state numEaten: size_t(0); // count successful tries + + input start: void + input eat: void; + input denied: void; + output hungry: void; + output done: void; + output finished: size_t; + + logical action requestToEat: void; + logical action finish: void; + + reaction (start) -> hungry {= + numEaten = 0; + numDenied = 0; + reactor::log::Info() << "Hello! I am Philosopher " << bank_index << " and I am hungry!"; + hungry.set(); + =} + + reaction (eat) -> hungry, done, finished {= + // do eating + reactor::log::Info() << "Philosopher " << bank_index << " is eating :)"; + numEaten++; + done.set(); + + if (numEaten == rounds) { + // now I am really finished + finished.set(numDenied); + } else { + // I am hungry again! + hungry.set(); + } + =} + + reaction (denied) -> hungry {= + // do thinking + reactor::log::Info() << "Philosopher " << bank_index << " got denied :( and is now thinking"; + numDenied++; + // I am hungry again! + hungry.set(); + =} +} + +main reactor (numIterations:size_t(12), numEatingRounds:size_t(10000), numPhilosophers:size_t(20)) { + + arbitrator = new Arbitrator(numPhilosophers=numPhilosophers); + philosophers = new[numPhilosophers] Philosopher(rounds=numEatingRounds); + runner = new BenchmarkRunner(numIterations=numIterations); + + reaction(startup) {= + printBenchmarkInfo("PhilosopherReactorLFCppBenchmark"); + printArgs("numIterations", numIterations, "numEatingRounds", numEatingRounds, "numPhilosophers", numPhilosophers); + printSystemInfo(); + =} + + (runner.start)+ -> arbitrator.start, philosophers.start; + arbitrator.allFinished -> runner.finished; + + arbitrator.eat -> philosophers.eat; + arbitrator.denied -> philosophers.denied; + philosophers.hungry -> arbitrator.hungry; + philosophers.done -> arbitrator.done; + philosophers.finished -> arbitrator.finished; +} From 76e368d46c7ae380fbf278f6dce20f390edc5901 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sat, 6 Nov 2021 17:25:15 +0100 Subject: [PATCH 2/6] fix config --- .../runner/conf/benchmark/savina_concurrency_philosopher.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmark/runner/conf/benchmark/savina_concurrency_philosopher.yaml b/benchmark/runner/conf/benchmark/savina_concurrency_philosopher.yaml index dc3c36f446..ec50f855df 100644 --- a/benchmark/runner/conf/benchmark/savina_concurrency_philosopher.yaml +++ b/benchmark/runner/conf/benchmark/savina_concurrency_philosopher.yaml @@ -21,8 +21,8 @@ targets: copy_sources: - "${lf_path}/benchmark/Cpp/Savina/src/BenchmarkRunner.lf" - "${lf_path}/benchmark/Cpp/Savina/src/concurrency" - lf_file: "concurrency/Philosopher.lf" - binary: "Philosopher" + lf_file: "concurrency/Philosophers.lf" + binary: "Philosophers" gen_args: null run_args: philosophers: ["--numPhilosophers", ""] From d219b0796d459ce47b833bc4dc384cd8eb245ebe Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sat, 6 Nov 2021 17:52:56 +0100 Subject: [PATCH 3/6] adjust C version of Philosophers to new C++ version --- .../C/Savina/src/concurrency/Philosophers.lf | 187 +++++------------- 1 file changed, 54 insertions(+), 133 deletions(-) diff --git a/benchmark/C/Savina/src/concurrency/Philosophers.lf b/benchmark/C/Savina/src/concurrency/Philosophers.lf index 9ff0f48eee..3f9f74f3de 100644 --- a/benchmark/C/Savina/src/concurrency/Philosophers.lf +++ b/benchmark/C/Savina/src/concurrency/Philosophers.lf @@ -1,17 +1,4 @@ /** - * Philosopher benchmark from the Savina benchmark suite for actor languages and frameworks. - * The original benchmarks are available at https://github.com/shamsmahmood/savina. - * - * This program takes advantage of particular features of LF to model the timing of the - * philosophers. Specifically, it has top-level parameters that specify the amount of time - * that philosophers spend eating and thinking. With the "fast" target option set to true, - * this does not actually affect the execution time, but rather better models a physical - * scenario. - * - * See, Shams Imam and Vivek Sarkar, "Savina - An Actor Benchmark Suite: Enabling Empirical - * Evaluation of Actor Libraries," AGERE! '14: Proceedings of the 4th International Workshop - * on Programming based on Actors Agents & Decentralized Control, October 2014, Pages 67–80, - * https://doi.org/10.1145/2687357.2687368 * * @author Christian Menard * @author Edward A. Lee @@ -29,14 +16,9 @@ target C { }; reactor Philosopher( - eating_time:time(1 sec), // (Logical) time spent eating. - thinking_time:time(1 sec), // (Logical) time spent thinking. - instance:int(0), // Instance number in a bank of reactors. - count:int(10000), // Having eaten this many times, send finished. - starvation_threshold:int(1000), // Print a starvation message when denied this many times. - verbose:bool(false) // Print verbose messages. + bank_index:int(0), // Instance number in a bank of reactors. + count:int(10000) // Having eaten this many times, send finished. ) { - input start:bool; output finished:bool; input eat:bool; @@ -45,76 +27,36 @@ reactor Philosopher( output done:bool; state times_eaten:int; - state times_denied:int; - - logical action done_eating; - logical action done_thinking; - - reaction(start) -> hungry {= - if(self->verbose) { - printf("Hello! I am philosopher %d, and I am very hungry!\n", self->instance); - } + + reaction(startup) -> hungry {= + info_print("Hello! I am philosopher %d, and I am very hungry!", self->bank_index); self->times_eaten = 0; - self->times_denied = 0; - SET(hungry, true); - =} - - reaction(done_eating) -> done, finished, done_thinking {= - // ... put forks away - SET(done, true); // signal that I am done eating - if(self->verbose) { - printf("Philosopher %d is thinking.\n", self->instance); - } - if (self->times_eaten < self->count) { - schedule(done_thinking, self->thinking_time); - } else { - // Now I am really not hungry anymore! - SET(finished, true); - } - =} - - reaction(done_thinking) -> hungry {= - if(self->verbose) { - printf("*********** Current time is %lld\n", get_elapsed_logical_time()); - printf("Philosopher %d is hungry.\n", self->instance); - } SET(hungry, true); =} - reaction(eat) -> done_eating {= + reaction(eat) -> done, finished, hungry {= // ... take left and right fork - if (!eat->is_present) { - fprintf(stderr, "ERROR: reaction to eat triggered with no input.\n"); - } - if(self->verbose) { - printf("*********** Current time is %lld\n", get_elapsed_logical_time()); - printf("Philosopher %d is eating.\n", self->instance); - } + info_print("Philosopher %d is eating.", self->bank_index); self->times_eaten++; - self->times_denied = 0; - schedule(done_eating, self->eating_time); - =} - - reaction(denied) -> done_thinking {= - self->times_denied++; - if (self->times_denied == self->starvation_threshold) { - printf("Philosopher %d is starving!\n", self->instance); - } - if(self->verbose) { - printf("*********** Current time is %lld\n", get_elapsed_logical_time()); - printf("Philosopher %d was denied and is thinking.\n", self->instance); + SET(done, true); + + if (self->times_eaten == self->count) { + SET(finished, true); + } else { + SET(hungry, true); } + =} + + reaction(denied) -> hungry {= + info_print("Philosopher %d was denied and is thinking.", self->bank_index); // Well, I will just try again... - schedule(done_thinking, self->thinking_time); + SET(hungry, true); =} } -reactor Arbitrator( - num_philosophers:int(20), - count:int(10000), - verbose:bool(false) -) { +reactor Arbitrator(num_philosophers:int(20)) { + preamble {= /* * Try to acquire both forks for a philosopher. Returns true if @@ -142,12 +84,16 @@ reactor Arbitrator( forks[instance] = false; // left forks[(instance + 1) % num_philosophers] = false; // right } + + enum Reply { + INVALID = 0, + EAT = 1, + DENIED = 2, + }; =} - /** Signal to philosopher to start an iteration. */ - output[num_philosophers] philosopher_start:bool; /** Signal from philosopher that it has eaten enough times in the iteration. */ - input[num_philosophers] philosopher_finished:bool; + input[num_philosophers] finished:bool; /** Signal from philosopher that it is ready to eat. */ input[num_philosophers] hungry:bool; @@ -159,58 +105,47 @@ reactor Arbitrator( output[num_philosophers] denied:bool; state forks:bool[]; + state replies:int[]; state finished_philosophers:int(0); state arbitration_id:int(0); state retries:int(0); + logical action send_replies; + reaction(startup) {= self->forks = calloc(self->num_philosophers, sizeof(bool)); + self->replies = calloc(self->num_philosophers, sizeof(int)); =} - - reaction(startup) -> philosopher_start {= - if (self->verbose) { - printf("Starting the arbitrator\n"); - } - self->finished_philosophers = 0; - self->retries = 0; - // Broadcast a `start` signal to all philosophers. - for(int i = 0; i < philosopher_start_width; i++) { - SET(philosopher_start[i], true); + reaction(send_replies) -> eat, denied {= + for(size_t i = 0; i < self->num_philosophers; i++) { + if (self->replies[i] == EAT) { + SET(eat[i], true); + } else if (self->replies[i] == DENIED) { + SET(denied[i], true); + } } + memset(self->replies, INVALID, sizeof(int) * self->num_philosophers); =} reaction(done) {= for(int i = 0; i < done_width; i++) { if (done[i]->is_present) { - if (self->verbose) { - printf("Arbitrator: Philosopher %d signals that they are done eating.\n", i); - } free_forks(self->forks, i, self->num_philosophers); } } =} - reaction(hungry) -> eat, denied {= + reaction(hungry) -> send_replies {= // Iterate over all philosophers, each time starting from a different one. // This arbitration ensures that no philosopher has to starve. for(int i = self->arbitration_id; i < self->arbitration_id + self->num_philosophers; i++) { int j = i % self->num_philosophers; if (hungry[j]->is_present) { - if (self->verbose) { - printf("*********** Current time is %lld\n", get_elapsed_logical_time()); - printf("Arbitrator: Philosopher %d signals that they are hungry.\n", j); - } if (acquire_forks(self->forks, j, self->num_philosophers)) { - if (self->verbose) { - printf("Arbitrator tells philosopher %d to eat.\n", j); - } - SET(eat[j], true); + self->replies[j] = EAT; } else { - if (self->verbose) { - printf("Arbitrator denies philosopher %d to eat.\n", j); - } - SET(denied[j], true); + self->replies[j] = DENIED; self->retries++; } } @@ -220,47 +155,33 @@ reactor Arbitrator( if (self->arbitration_id == self->num_philosophers) { self->arbitration_id = 0; } + schedule(send_replies, 0); =} - reaction (philosopher_finished) {= - for(int i = 0; i < philosopher_finished_width; i++) { - if (philosopher_finished[i]->is_present) { + reaction (finished) {= + for(int i = 0; i < finished_width; i++) { + if (finished[i]->is_present) { self->finished_philosophers++; } } - if (self->verbose) { - printf("Arbitrator: Number of finished philosophers: %d\n", self->finished_philosophers); - } - if (self->finished_philosophers == self->num_philosophers) { - printf("Arbitrator: All philosophers are sated. Number of denials to philosophers: %d\n", self->retries); + printf("Arbitrator: All philosophers are sated. Number of denials to philosophers: %d", self->retries); request_stop(); } =} } main reactor Philosophers( - num_philosophers:int(20), - iterations:int(12), - count:int(10), - eating_time:time(1 sec), // (Logical) time spent eating. - thinking_time:time(600 msec), // (Logical) time spent thinking. - verbose:bool(true) + num_philosophers:int(5), + count:int(2) ) { - arbitrator = new Arbitrator(count=count, verbose=verbose, num_philosophers = num_philosophers); - philosophers = new[num_philosophers] Philosopher( - count=count, - verbose=verbose, - eating_time=eating_time, - thinking_time = thinking_time - ); - - arbitrator.philosopher_start -> philosophers.start; - philosophers.finished -> arbitrator.philosopher_finished; + arbitrator = new Arbitrator(num_philosophers = num_philosophers); + philosophers = new[num_philosophers] Philosopher(count=count); - philosophers.hungry -> arbitrator.hungry; - philosophers.done -> arbitrator.done; arbitrator.eat -> philosophers.eat; arbitrator.denied -> philosophers.denied; + philosophers.hungry -> arbitrator.hungry; + philosophers.done -> arbitrator.done; + philosophers.finished -> arbitrator.finished; } From 08d50c57b039074bf2a420088ed2e71df7c62ef9 Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sat, 6 Nov 2021 17:58:39 +0100 Subject: [PATCH 4/6] configure Philosophers for benchmark runner --- benchmark/C/Savina/src/concurrency/Philosophers.lf | 13 +++++++------ .../benchmark/savina_concurrency_philosopher.yaml | 8 ++++++++ 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/benchmark/C/Savina/src/concurrency/Philosophers.lf b/benchmark/C/Savina/src/concurrency/Philosophers.lf index 3f9f74f3de..b7be3d2cc2 100644 --- a/benchmark/C/Savina/src/concurrency/Philosophers.lf +++ b/benchmark/C/Savina/src/concurrency/Philosophers.lf @@ -12,7 +12,7 @@ target C { ]]] */ threads: 0, /// [[[end]]] - fast: true, + logging: warn }; reactor Philosopher( @@ -172,10 +172,11 @@ reactor Arbitrator(num_philosophers:int(20)) { =} } -main reactor Philosophers( - num_philosophers:int(5), - count:int(2) -) { +/* [[[cog + cog.outl(f"main reactor Philosophers(num_philosophers:int({numPhilosophers}), count:int({numEatingRounds})) {{") +]]] */ +main reactor Philosophers(num_philosophers:int(20), count:int(10000)) { +/// [[[end]]] arbitrator = new Arbitrator(num_philosophers = num_philosophers); philosophers = new[num_philosophers] Philosopher(count=count); @@ -184,4 +185,4 @@ main reactor Philosophers( philosophers.hungry -> arbitrator.hungry; philosophers.done -> arbitrator.done; philosophers.finished -> arbitrator.finished; -} +} \ No newline at end of file diff --git a/benchmark/runner/conf/benchmark/savina_concurrency_philosopher.yaml b/benchmark/runner/conf/benchmark/savina_concurrency_philosopher.yaml index ec50f855df..e8c5f8c8ba 100644 --- a/benchmark/runner/conf/benchmark/savina_concurrency_philosopher.yaml +++ b/benchmark/runner/conf/benchmark/savina_concurrency_philosopher.yaml @@ -27,3 +27,11 @@ targets: run_args: philosophers: ["--numPhilosophers", ""] eating_rounds: ["--numEatingRounds", ""] + lf-c: + copy_sources: + - "${lf_path}/benchmark/C/Savina/src/concurrency/Philosophers.lf" + lf_file: "Philosophers.lf" + binary: "Philosophers" + gen_args: + philosophers: ["-D", "numPhilosophers="] + eating_rounds: ["-D", "numEatingRounds="] From cb729088714457d69cd88f14b5f5866133459e6d Mon Sep 17 00:00:00 2001 From: Christian Menard Date: Sat, 6 Nov 2021 18:11:20 +0100 Subject: [PATCH 5/6] update comments --- .../C/Savina/src/concurrency/Philosophers.lf | 4 +++ .../Savina/src/concurrency/Philosophers.lf | 25 ++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/benchmark/C/Savina/src/concurrency/Philosophers.lf b/benchmark/C/Savina/src/concurrency/Philosophers.lf index b7be3d2cc2..814c6ae293 100644 --- a/benchmark/C/Savina/src/concurrency/Philosophers.lf +++ b/benchmark/C/Savina/src/concurrency/Philosophers.lf @@ -1,4 +1,8 @@ /** + * Copyright (C) 2020 TU Dresden and UC Berkeley + * + * For more details on this benchmark, see the Cpp version from which it was derived: + * https://github.com/lf-lang/lingua-franca/blob/master/benchmark/Cpp/Savina/src/concurrency/Philosophers.lf. * * @author Christian Menard * @author Edward A. Lee diff --git a/benchmark/Cpp/Savina/src/concurrency/Philosophers.lf b/benchmark/Cpp/Savina/src/concurrency/Philosophers.lf index 9bc206e77c..24803bc7b2 100644 --- a/benchmark/Cpp/Savina/src/concurrency/Philosophers.lf +++ b/benchmark/Cpp/Savina/src/concurrency/Philosophers.lf @@ -1,9 +1,28 @@ /** * Copyright (C) 2020 TU Dresden * - * This implementation stays close to the Savina implementation - * with Akka. There is no notion of time and there is no delay - * or work done when a philosopher is thinking or eating. + * This benchmark implements a "solution" to the philosophers problem. + * This LF implementation stays close to the original Akka implementation + * in the Savina suite. However, it is not a particular good solution as + * it relies on busy waiting. The Philosophers bombard the Arbitrator + * with "hungry" messages until they are allowed to eat. In an actor + * implementation, this leads to millions of messages being sent. + * Due to the synchronous semantics of LF, this effect is mitigated. + * Since we know all hungry philosophers at each tag, we can compute a + * schedule and let as many philosophers eat as possible. Philosophers + * will try again if they are denied, but they will only send a request + * once per tag and hence the total number of denied messages is much lower + * than in Akka. + * + * Due to the bidrectional interaction between the Arbitrator and the + * Philosophers, there is a cycle in the dependency graph. Normally we + * would break the cycle by using actions in the Philosophers as this would + * model the time a philosopher needs to eat and think. But for this simple + * benchmark, it is more efficient to have a single action in the arbitrator + * only to break the loop. The idea is to process all hungry requests within + * one tag and to store the replies in a state variable. Thena logical action + * is scheduled and the corresponding reaction will send the eat and denied + * messages to each Philosopher. * * @author Hannes Klein * @author Christian Menard From 103bb0433460bc685fc2803de705d18e930e9c57 Mon Sep 17 00:00:00 2001 From: Marten Lohstroh Date: Sat, 6 Nov 2021 15:08:28 -0700 Subject: [PATCH 6/6] Apply suggestions from code review --- benchmark/Cpp/Savina/src/concurrency/Philosophers.lf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmark/Cpp/Savina/src/concurrency/Philosophers.lf b/benchmark/Cpp/Savina/src/concurrency/Philosophers.lf index 24803bc7b2..10778ad202 100644 --- a/benchmark/Cpp/Savina/src/concurrency/Philosophers.lf +++ b/benchmark/Cpp/Savina/src/concurrency/Philosophers.lf @@ -3,7 +3,7 @@ * * This benchmark implements a "solution" to the philosophers problem. * This LF implementation stays close to the original Akka implementation - * in the Savina suite. However, it is not a particular good solution as + * in the Savina suite. However, it is not a particularly good solution as * it relies on busy waiting. The Philosophers bombard the Arbitrator * with "hungry" messages until they are allowed to eat. In an actor * implementation, this leads to millions of messages being sent. @@ -20,7 +20,7 @@ * model the time a philosopher needs to eat and think. But for this simple * benchmark, it is more efficient to have a single action in the arbitrator * only to break the loop. The idea is to process all hungry requests within - * one tag and to store the replies in a state variable. Thena logical action + * one tag and to store the replies in a state variable. Then, a logical action * is scheduled and the corresponding reaction will send the eat and denied * messages to each Philosopher. *