Skip to content

Commit

Permalink
format all benchmarks except for the C benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
cmnrd committed Mar 8, 2024
1 parent e11e02d commit 0279203
Show file tree
Hide file tree
Showing 57 changed files with 9,201 additions and 9,350 deletions.
406 changes: 198 additions & 208 deletions Cpp/Savina/src/BenchmarkRunner.lf

Large diffs are not rendered by default.

339 changes: 168 additions & 171 deletions Cpp/Savina/src/concurrency/Banking.lf
Original file line number Diff line number Diff line change
@@ -1,190 +1,187 @@
/**
* Copyright (C) 2020 TU Dresden
*
* This benchmark consists of a so-called 'Teller' and a number of Account reactors.
* The Accounts keep an internal balance. The Teller generates a series of random
* credit messages which it sends to the various accounts. Each credit message
* specifies an amount and a destination account. The account that receives a credit
* message from the teller will reduce its balance by the given amount and sends a
* debit message of the same amount to the destination account. The destination
*
* This benchmark consists of a so-called 'Teller' and a number of Account reactors. The Accounts
* keep an internal balance. The Teller generates a series of random credit messages which it sends
* to the various accounts. Each credit message specifies an amount and a destination account. The
* account that receives a credit message from the teller will reduce its balance by the given
* amount and sends a debit message of the same amount to the destination account. The destination
* account will then increase its balance accordingly.
*
* Note that the original Akka implementation performs an explicit synchronisation, to
* ensure that the debit message has been processed by the destination account before
* replying to the teller that the credit message was processed. This synchronization
* and reply mechanism is not required in LF, as all messages are processed logically
* synchronous. The teller can be sure that any credit messages it sends will be
* processed before moving to the next tag, and similarly each account can be sure that
* any debit messages it sends will be processed by the receiving accounts before
* receiving any new messages.
*
* The initial generation of all the credit messages is an interesting problem in LF.
* In the Akka implementation, the teller actor will just send `numBankings` randomly
* generated messages to the account actors, which then process one message after the
* other. However, this is not easily doable in LF, as a port can only hold a single
* value. In order to send multiple messages to the same account, the teller needs to
* use an action and advance in logical time before sending the next message. This is
* currently implemented such that the Teller creates an initial queue per account
* which contains all credit messages that should be sent to this account. Then it
* schedules it `next` action and the corresponding reaction will send the first
* round of messages to the accounts. Then it schedules next again. This continues
* until all messages are sent.
*
* A potential optimization to the problem described above, would be not to send
* individual credit messages to the accounts, but instead a list of messages (for
* instance a std::vector). This could be done in a single logical step and should
* thus be much faster. However, this is likely an unfair advantage over the Akka
* implementation, which also sends all messages individually.
*
*
* Note that the original Akka implementation performs an explicit synchronisation, to ensure that
* the debit message has been processed by the destination account before replying to the teller
* that the credit message was processed. This synchronization and reply mechanism is not required
* in LF, as all messages are processed logically synchronous. The teller can be sure that any
* credit messages it sends will be processed before moving to the next tag, and similarly each
* account can be sure that any debit messages it sends will be processed by the receiving accounts
* before receiving any new messages.
*
* The initial generation of all the credit messages is an interesting problem in LF. In the Akka
* implementation, the teller actor will just send `numBankings` randomly generated messages to the
* account actors, which then process one message after the other. However, this is not easily
* doable in LF, as a port can only hold a single value. In order to send multiple messages to the
* same account, the teller needs to use an action and advance in logical time before sending the
* next message. This is currently implemented such that the Teller creates an initial queue per
* account which contains all credit messages that should be sent to this account. Then it schedules
* it `next` action and the corresponding reaction will send the first round of messages to the
* accounts. Then it schedules next again. This continues until all messages are sent.
*
* A potential optimization to the problem described above, would be not to send individual credit
* messages to the accounts, but instead a list of messages (for instance a std::vector). This could
* be done in a single logical step and should thus be much faster. However, this is likely an
* unfair advantage over the Akka implementation, which also sends all messages individually.
*
* @author Hannes Klein
* @author Christian Menard
*/

target Cpp {
cmake-include: "../IncludeHeaders.cmake",
build-type : Release,
no-runtime-validation: true,
logging : WARN
};
cmake-include: "../IncludeHeaders.cmake",
build-type: Release,
no-runtime-validation: true,
logging: WARN
}

import BenchmarkRunner from "../BenchmarkRunner.lf";
import BenchmarkRunner from "../BenchmarkRunner.lf"

public preamble {=
#include <deque>
#include <reactor-cpp/logging.hh>
struct CreditMessage {
size_t recipient;
double amount;
};
using MessageQueue = std::deque<CreditMessage>;
#include <deque>
#include <reactor-cpp/logging.hh>

struct CreditMessage {
size_t recipient;
double amount;
};

using MessageQueue = std::deque<CreditMessage>;
=}

reactor Teller(numAccounts:size_t=1000, numBankings:size_t=50000) {

public preamble {=
#include "PseudoRandom.hh"
=}

state randomGen: PseudoRandom;
state messageQueues: std::vector<MessageQueue>(numAccounts);

input start:void;
output finished:void;

output[numAccounts] reset_state: void;
output[numAccounts] credit: CreditMessage;

logical action next: void;

method generateWork() {=
for (size_t i{0}; i < numBankings; i++) {
// src is lower than dest id to ensure there is never a deadlock
// Note: this comment stems from the original Akka implementation and
// is actually not needed in LF, since cycle free programs cannot deadlock
size_t src_account = randomGen.nextInt((numAccounts / 10) * 8);
size_t loop_id = randomGen.nextInt(numAccounts - src_account);
if(loop_id == 0) {
loop_id += 1;
}
size_t dest_account = src_account + loop_id;

double amount = randomGen.nextDouble() * 1000;

reactor::log::Info() << "Send " << amount << " from " << src_account << " to " << dest_account;
messageQueues[src_account].push_back(CreditMessage{dest_account, amount});
}
=}

reaction(start) -> reset_state, next {=
reactor::log::Info() << "Teller: Start a new iteration";

// reset local state
randomGen = PseudoRandom(123456);

// reset all accounts
for (auto& r : reset_state)
r.set();

generateWork();

// start execution
next.schedule();
=}

reaction(next) -> next, credit, finished {=
reactor::log::Info() << "Teller: Sending a new round of credit messages";
bool work_found{false};
for (size_t i{0}; i < numAccounts; i++) {
auto& queue = messageQueues[i];
if (!queue.empty()) {
work_found = true;
const auto& message = queue.front();
credit[i].set(message);
queue.pop_front();
}
reactor Teller(numAccounts: size_t = 1000, numBankings: size_t = 50000) {
public preamble {=
#include "PseudoRandom.hh"
=}

state randomGen: PseudoRandom
state messageQueues: std::vector<MessageQueue>(numAccounts)

input start: void
output finished: void

output[numAccounts] reset_state: void
output[numAccounts] credit: CreditMessage

logical action next: void

method generateWork() {=
for (size_t i{0}; i < numBankings; i++) {
// src is lower than dest id to ensure there is never a deadlock
// Note: this comment stems from the original Akka implementation and
// is actually not needed in LF, since cycle free programs cannot deadlock
size_t src_account = randomGen.nextInt((numAccounts / 10) * 8);
size_t loop_id = randomGen.nextInt(numAccounts - src_account);
if(loop_id == 0) {
loop_id += 1;
}

if (work_found) {
next.schedule();
} else {
reactor::log::Info() << "Teller: Finished iteration";
finished.set();
size_t dest_account = src_account + loop_id;

double amount = randomGen.nextDouble() * 1000;

reactor::log::Info() << "Send " << amount << " from " << src_account << " to " << dest_account;
messageQueues[src_account].push_back(CreditMessage{dest_account, amount});
}
=}

reaction(start) -> reset_state, next {=
reactor::log::Info() << "Teller: Start a new iteration";

// reset local state
randomGen = PseudoRandom(123456);

// reset all accounts
for (auto& r : reset_state)
r.set();

generateWork();

// start execution
next.schedule();
=}

reaction(next) -> next, credit, finished {=
reactor::log::Info() << "Teller: Sending a new round of credit messages";
bool work_found{false};
for (size_t i{0}; i < numAccounts; i++) {
auto& queue = messageQueues[i];
if (!queue.empty()) {
work_found = true;
const auto& message = queue.front();
credit[i].set(message);
queue.pop_front();
}
=}
}

if (work_found) {
next.schedule();
} else {
reactor::log::Info() << "Teller: Finished iteration";
finished.set();
}
=}
}

reactor Account(bank_index:size_t=0, numAccounts:size_t=1000, initialBalance:double=0.0) {
state balance: double{0.0};
input reset_state: void;
input inCredit: CreditMessage;
input[numAccounts] inDebit: double;
output[numAccounts] outDebit: double;
reaction (reset_state) {=
balance = initialBalance;
=}
reaction (inCredit) -> outDebit {=
auto message = inCredit.get();
// reduce the balance
balance -= message->amount;
// and sent the recipient a debit message
outDebit[message->recipient].set(message->amount);
reactor::log::Info() << "Account " << bank_index << " credits " << message->amount
<< " to " << message->recipient;
=}
reaction (inDebit) {=
for (auto i: inDebit.present_indices_unsorted()) {
double amount = *(inDebit[i].get());
// increase the balance
balance += amount;
reactor::log::Info() << "Account " << bank_index << " received " << amount;
}
=}
reactor Account(bank_index: size_t = 0, numAccounts: size_t = 1000, initialBalance: double = 0.0) {
state balance: double{0.0}

input reset_state: void
input inCredit: CreditMessage

input[numAccounts] inDebit: double
output[numAccounts] outDebit: double

reaction(reset_state) {=
balance = initialBalance;
=}

reaction(inCredit) -> outDebit {=
auto message = inCredit.get();
// reduce the balance
balance -= message->amount;
// and sent the recipient a debit message
outDebit[message->recipient].set(message->amount);
reactor::log::Info() << "Account " << bank_index << " credits " << message->amount
<< " to " << message->recipient;
=}

reaction(inDebit) {=
for (auto i: inDebit.present_indices_unsorted()) {
double amount = *(inDebit[i].get());
// increase the balance
balance += amount;
reactor::log::Info() << "Account " << bank_index << " received " << amount;
}
=}
}

main reactor (numIterations:size_t=12, numTransactions:size_t=50000, numAccounts:size_t=1000) {

teller = new Teller(numAccounts=numAccounts, numBankings=numTransactions);
runner = new BenchmarkRunner(numIterations=numIterations);

runner.start -> teller.start;
teller.finished -> runner.finished;

accounts = new[numAccounts] Account(numAccounts=numAccounts, initialBalance={=std::numeric_limits<double>::max() / (numAccounts * numTransactions)=});

reaction(startup) {=
printBenchmarkInfo("BankingReactorLFCppBenchmark");
printArgs("numIterations", numIterations, "numTransactions", numTransactions, "numAccounts", numAccounts, "initialBalance", std::numeric_limits<double>::max() / (numAccounts * numTransactions));
printSystemInfo();
=}

teller.credit -> accounts.inCredit;
teller.reset_state -> accounts.reset_state;
accounts.outDebit -> interleaved(accounts.inDebit);
main reactor(
numIterations: size_t = 12,
numTransactions: size_t = 50000,
numAccounts: size_t = 1000) {
teller = new Teller(numAccounts=numAccounts, numBankings=numTransactions)
runner = new BenchmarkRunner(numIterations=numIterations)

runner.start -> teller.start
teller.finished -> runner.finished

accounts = new[numAccounts] Account(
numAccounts=numAccounts,
initialBalance = {= std::numeric_limits<double>::max() / (numAccounts * numTransactions) =})

teller.credit -> accounts.inCredit
teller.reset_state -> accounts.reset_state
accounts.outDebit -> interleaved(accounts.inDebit)

reaction(startup) {=
printBenchmarkInfo("BankingReactorLFCppBenchmark");
printArgs("numIterations", numIterations, "numTransactions", numTransactions, "numAccounts", numAccounts, "initialBalance", std::numeric_limits<double>::max() / (numAccounts * numTransactions));
printSystemInfo();
=}
}
Loading

0 comments on commit 0279203

Please sign in to comment.