-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
format all benchmarks except for the C benchmarks
- Loading branch information
Showing
57 changed files
with
9,201 additions
and
9,350 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
=} | ||
} |
Oops, something went wrong.