Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add double buffering for data printing
Browse files Browse the repository at this point in the history
mscuttari committed Oct 28, 2024
1 parent bb5cd5f commit 488f9b2
Showing 9 changed files with 234 additions and 72 deletions.
5 changes: 5 additions & 0 deletions include/marco/Runtime/Printers/CSV/Options.h
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
#ifndef MARCO_RUNTIME_PRINTING_CONFIG_H
#define MARCO_RUNTIME_PRINTING_CONFIG_H

#include <cstddef>

namespace marco::runtime::printing
{
struct PrintOptions
{
bool scientificNotation = false;
unsigned int precision = 9;

// Buffer size in bytes.
size_t bufferSize = 10 * 1024 * 1024;
};

PrintOptions& printOptions();
12 changes: 12 additions & 0 deletions include/marco/Runtime/Printers/CSV/Printer.h
Original file line number Diff line number Diff line change
@@ -2,6 +2,8 @@
#define MARCO_RUNTIME_PRINTING_PRINTERS_CSV_H

#include "marco/Runtime/Printers/Printer.h"
#include "marco/Runtime/Printers/DoubleBuffer.h"
#include "marco/Runtime/Multithreading/ThreadPool.h"

namespace marco::runtime::printing
{
@@ -19,6 +21,16 @@ namespace marco::runtime::printing
void printValues() override;

void simulationEnd() override;

private:
void initialize();

void printBufferedValues(const double* values, uint64_t count);

private:
DoubleBuffer buffer;
std::vector<size_t> bufferPositions;
ThreadPool threadPool;
};
}

40 changes: 40 additions & 0 deletions include/marco/Runtime/Printers/DoubleBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#ifndef MARCO_RUNTIME_PRINTERS_DOUBLEBUFFER_H
#define MARCO_RUNTIME_PRINTERS_DOUBLEBUFFER_H

#include "marco/Runtime/Printers/Printer.h"
#include <functional>

namespace marco::runtime::printing
{
class DoubleBuffer
{
public:
DoubleBuffer(uint64_t lineElementsCount, size_t bufferSizeBytes, std::function<void(const double*, uint64_t)> callback);

DoubleBuffer(const DoubleBuffer& other) = delete;

DoubleBuffer(DoubleBuffer&& other) = default;

DoubleBuffer& operator=(const DoubleBuffer& other) = delete;

DoubleBuffer& operator=(DoubleBuffer&& other) = default;

double* getActiveBuffer();

void endLine();

void flush();

private:
uint64_t lineElementsCount{1};
uint64_t lines{1};
std::function<void(const double*, uint64_t)> callback;

std::vector<double> inputBuffer;
std::vector<double> outputBuffer;

uint64_t currentLine{0};
};
}

#endif // MARCO_RUNTIME_PRINTERS_DOUBLEBUFFER_H
4 changes: 4 additions & 0 deletions include/marco/Runtime/Simulation/Runtime.h
Original file line number Diff line number Diff line change
@@ -12,6 +12,10 @@ namespace marco::runtime
class Simulation
{
public:
int64_t getNumOfPrintableScalarVariables() const;

int64_t getNumOfPrintableScalarVariables(int64_t variable) const;

/// Get the data printer.
Printer* getPrinter();

2 changes: 2 additions & 0 deletions lib/Printers/CSV/CLI.cpp
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@ void CommandLineOptions::printCommandLineOptions(std::ostream &os) const {
// clang-format off
os << " --scientific-notation Print the values using the scientific notation." << std::endl;
os << " --precision=<value> Set the number of decimals to be printed. Defaults to " << printOptions().precision << "." << std::endl;
os << " --buffer-size=<value> Set the size (in bytes) of the output buffer. Defaults to " << (printOptions().bufferSize / 1024 / 1024) << " MB." << std::endl;
// clang-format on
}

@@ -18,6 +19,7 @@ void CommandLineOptions::parseCommandLineOptions(
// clang-format off
printOptions().scientificNotation = options["scientific-notation"];
options("precision") >> printOptions().precision;
options("buffer-size") >> printOptions().bufferSize;
// clang-format on
}
} // namespace marco::runtime::printing
1 change: 1 addition & 0 deletions lib/Printers/CSV/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
marco_add_runtime_static_library(PrinterCSV
STATIC
../DoubleBuffer.cpp
../Printer.cpp
CLI.cpp
Options.cpp
163 changes: 91 additions & 72 deletions lib/Printers/CSV/Printer.cpp
Original file line number Diff line number Diff line change
@@ -130,78 +130,15 @@ static void printHeader(const Simulation& simulation)
PRINT_PROFILER_STRING_STOP;
}

static void printValues(const Simulation& simulation)
{
auto& options = printOptions();
std::cout.precision(options.precision);

if (options.scientificNotation) {
std::cout << std::scientific;
} else {
std::cout << std::fixed;
}

double time = getTime();

PRINT_PROFILER_FLOAT_START;
std::cout << time;
PRINT_PROFILER_FLOAT_STOP;

for (int64_t var : simulation.variablesPrintOrder) {
if (!simulation.printableVariables[var]) {
// The variable must not be printed.
continue;
}

int64_t rank = simulation.variablesRanks[var];

if (rank != 0 && simulation.variablesPrintableIndices[var].empty()) {
// The array variable has no printable indices.
continue;
}

if (rank == 0) {
// Print the scalar variable.
double value = getVariableValue(var, nullptr);

PRINT_PROFILER_STRING_START;
std::cout << ',';
PRINT_PROFILER_STRING_STOP;

PRINT_PROFILER_FLOAT_START;
std::cout << value;
PRINT_PROFILER_FLOAT_STOP;
} else {
// Print the components of the array variable.
for (const auto& range : simulation.variablesPrintableIndices[var]) {
auto beginIt = MultidimensionalRangeIterator::begin(range);
auto endIt = MultidimensionalRangeIterator::end(range);

for (auto it = beginIt; it != endIt; ++it) {
double value = getVariableValue(var, *it);

PRINT_PROFILER_STRING_START;
std::cout << ',';
PRINT_PROFILER_STRING_STOP;

PRINT_PROFILER_FLOAT_START;
std::cout << value;
PRINT_PROFILER_FLOAT_STOP;
}
}
}
}

PRINT_PROFILER_STRING_START;
std::cout << std::endl;
PRINT_PROFILER_STRING_STOP;
}

namespace marco::runtime::printing
{
CSVPrinter::CSVPrinter(Simulation* simulation)
: Printer(simulation)
: Printer(simulation),
buffer(1 + simulation->getNumOfPrintableScalarVariables(), printOptions().bufferSize, [&](const double* values, uint64_t count) {
printBufferedValues(values, count);
})
{
initialize();
}

#ifdef CLI_ENABLE
@@ -220,14 +157,96 @@ namespace marco::runtime::printing

void CSVPrinter::printValues()
{
SIMULATION_PROFILER_PRINTING_START;
::printValues(*getSimulation());
SIMULATION_PROFILER_PRINTING_STOP;
buffer.getActiveBuffer()[0] = getTime();

for (int64_t var : getSimulation()->variablesPrintOrder) {
if (!getSimulation()->printableVariables[var]) {
// The variable must not be printed.
continue;
}

int64_t rank = getSimulation()->variablesRanks[var];

if (rank != 0 && getSimulation()->variablesPrintableIndices[var].empty()) {
// The array variable has no printable indices.
continue;
}

if (rank == 0) {
// Print the scalar variable.
double value = getVariableValue(var, nullptr);
buffer.getActiveBuffer()[bufferPositions[var]] = value;
} else {
// Print the components of the array variable.
size_t relativePos = 0;

for (const auto& range : getSimulation()->variablesPrintableIndices[var]) {
auto beginIt = MultidimensionalRangeIterator::begin(range);
auto endIt = MultidimensionalRangeIterator::end(range);

for (auto it = beginIt; it != endIt; ++it) {
double value = getVariableValue(var, *it);
buffer.getActiveBuffer()[bufferPositions[var] + relativePos] = value;
++relativePos;
}
}
}
}

buffer.endLine();
}

void CSVPrinter::simulationEnd()
{
// Do nothing.
buffer.flush();
}

void CSVPrinter::initialize()
{
bufferPositions.resize(getSimulation()->printableVariables.size(), 0);

// Time variable.
bufferPositions.push_back(0);

// Model variables.
int64_t position = 1;

for (int64_t variable : getSimulation()->variablesPrintOrder) {
bufferPositions[variable] = position;
position += getSimulation()->getNumOfPrintableScalarVariables(variable);
}
}

void CSVPrinter::printBufferedValues(const double* values, uint64_t count)
{
SIMULATION_PROFILER_PRINTING_START;

auto& options = printOptions();
std::cout.precision(options.precision);

if (options.scientificNotation) {
std::cout << std::scientific;
} else {
std::cout << std::fixed;
}

for (uint64_t i = 0; i < count; ++i) {
PRINT_PROFILER_FLOAT_START;
std::cout << values[i];
PRINT_PROFILER_FLOAT_STOP;

if (i + 1 != count) {
PRINT_PROFILER_STRING_START;
std::cout << ',';
PRINT_PROFILER_STRING_STOP;
}
}

PRINT_PROFILER_STRING_START;
std::cout << std::endl;
PRINT_PROFILER_STRING_STOP;

SIMULATION_PROFILER_PRINTING_STOP;
}
}

40 changes: 40 additions & 0 deletions lib/Printers/DoubleBuffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#include "marco/Runtime/Printers/DoubleBuffer.h"

using namespace ::marco::runtime::printing;

namespace marco::runtime::printing {
DoubleBuffer::DoubleBuffer(
uint64_t lineElementsCount, size_t bufferSizeBytes,
std::function<void(const double *, uint64_t)> callback)
: lineElementsCount(lineElementsCount), callback(callback) {
uint64_t requestedLines =
bufferSizeBytes / (sizeof(double) * lineElementsCount);

lines = std::max(static_cast<uint64_t>(1), requestedLines);
inputBuffer.resize(lines * lineElementsCount, 0xDEADBEEF);
outputBuffer.resize(lines * lineElementsCount, 0xDEADBEEF);
}

double *DoubleBuffer::getActiveBuffer() {
return inputBuffer.data() + currentLine * lineElementsCount;
}

void DoubleBuffer::endLine() {
if (++currentLine >= lines) {
flush();
}
}

void DoubleBuffer::flush() {
if (currentLine > 0) {
std::swap(inputBuffer, outputBuffer);

for (uint64_t line = 0; line < lines; ++line) {
callback(outputBuffer.data() + line * lineElementsCount,
lineElementsCount);
}

currentLine = 0;
}
}
} // namespace marco::runtime::printing
39 changes: 39 additions & 0 deletions lib/Simulation/Runtime.cpp
Original file line number Diff line number Diff line change
@@ -55,6 +55,45 @@ static void printHelp()

namespace marco::runtime
{
int64_t Simulation::getNumOfPrintableScalarVariables() const
{
int64_t result = 0;

for (int64_t variable : variablesPrintOrder) {
result += getNumOfPrintableScalarVariables(variable);
}

return result;
}

int64_t Simulation::getNumOfPrintableScalarVariables(int64_t variable) const
{
if (!printableVariables[variable]) {
// The variable must not be printed.
return 0;
}

int64_t rank = variablesRanks[variable];

if (rank == 0) {
return 1;
}

int64_t result = 0;

for (const auto& range : variablesPrintableIndices[variable]) {
size_t rangeSize = 1;

for (int64_t dim = 0; dim < rank; ++dim) {
rangeSize *= range[dim].end - range[dim].begin;
}

result += rangeSize;
}

return result;
}

Printer* Simulation::getPrinter()
{
assert(printer != nullptr);

0 comments on commit 488f9b2

Please sign in to comment.