From dd444c5d319b48b9a7d8e172313e2111b38379e9 Mon Sep 17 00:00:00 2001 From: Dorian Stoll <dorian.stoll@uni-potsdam.de> Date: Wed, 12 Jun 2024 12:32:32 +0200 Subject: [PATCH] zellularautomat: cpp: Initial port --- .gitignore | 4 + meson.build | 1 + src/benchmarks/zellularautomat/cpp/main.cpp | 164 ++++++++++++++++++ .../zellularautomat/cpp/meson.build | 31 ++++ src/benchmarks/zellularautomat/meson.build | 1 + subprojects/eigen.wrap | 13 ++ 6 files changed, 214 insertions(+) create mode 100644 src/benchmarks/zellularautomat/cpp/main.cpp create mode 100644 src/benchmarks/zellularautomat/cpp/meson.build create mode 100644 subprojects/eigen.wrap diff --git a/.gitignore b/.gitignore index 4a8fa099..aabcf599 100644 --- a/.gitignore +++ b/.gitignore @@ -290,3 +290,7 @@ pyrightconfig.json # Hidden files .* + +# Downloaded meson subprojects +subprojects/* +!subprojects/*.wrap diff --git a/meson.build b/meson.build index e6d4f65b..d68803dc 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,7 @@ project( 'praktikum', 'c', + 'cpp', 'fortran', default_options: [ 'warning_level=3', diff --git a/src/benchmarks/zellularautomat/cpp/main.cpp b/src/benchmarks/zellularautomat/cpp/main.cpp new file mode 100644 index 00000000..b7dc60c7 --- /dev/null +++ b/src/benchmarks/zellularautomat/cpp/main.cpp @@ -0,0 +1,164 @@ +/* + * simulate a cellular automaton with periodic boundaries (torus-like) + * OpenMP version + * + * (c) 2024 Dorian Stoll (C++ / Eigen port) + * (c) 2016 Steffen Christgau (C99 port, modularization, parallelization) + * (c) 1996,1997 Peter Sanders, Ingo Boesnach (original source) + * + * command line arguments: + * #1: Number of lines + * #2: Number of iterations to be simulated + * + */ + +/* + * Eigen defaults to using column-major order for its container types. + * + * However, using it like that would make the lines parameter correspond to Eigens .cols() function + * instead of the more fitting .rows(). To make the code more obvious, we change the default. + */ +#define EIGEN_DEFAULT_TO_ROW_MAJOR + +#include <Eigen/Eigen> + +#include <cassert> +#include <cstdint> +#include <cstdlib> +#include <iostream> +#include <stdexcept> + +class Automat { +private: + /* "ADT" State */ + using cell_state_t = uint8_t; + + using Field = Eigen::Matrix<cell_state_t, Eigen::Dynamic, Eigen::Dynamic>; + +private: + /* horizontal size of the configuration */ + static constexpr Eigen::Index XSIZE = 1024; + static constexpr Eigen::Index LINE_SIZE = XSIZE + 2; + + /* annealing rule from ChoDro96 page 34 + * the table is used to map the number of nonzero + * states in the neighborhood to the new state + */ + static constexpr std::array<cell_state_t, 10> ANNEAL {0, 0, 0, 0, 1, 0, 1, 1, 1, 1}; + +private: + int m_lines; + + Field m_from {}; + Field m_to {}; + +public: + Automat(const int lines) : m_lines {lines} + { + if (lines <= 0) + throw std::invalid_argument {"There must be at least one line"}; + + m_from.resize(lines + 2, LINE_SIZE); + m_to.resize(lines + 2, LINE_SIZE); + } + + void run(const int its) + { + this->init_config(); + + for (int i = 0; i < its; i++) { + this->boundary(); + this->simulate(); + + std::swap(m_from, m_to); + } + } + + void report() + { + auto block = m_from(Eigen::seqN(1, m_lines), Eigen::all); + + for (Eigen::Index y = 0; y < block.rows(); y++) { + block(y, 0) = 0; + block(y, block.cols() - 1) = 0; + } + + const auto start = block.data(); + const auto end = start + (block.size() * sizeof(*start)); + + std::copy(start, end, std::ostreambuf_iterator(std::cout)); + } + +private: + /* random starting configuration */ + void init_config() + { + srand(424243); + + for (Eigen::Index y = 1; y <= m_to.rows() - 2; y++) { + for (Eigen::Index x = 1; x <= m_from.cols() - 2; x++) + m_from(y, x) = rand() % 2; + } + } + + /* treat torus like boundary conditions */ + void boundary() + { +#pragma omp sections + { +#pragma omp section +#pragma omp parallel for + for (Eigen::Index y = 0; y < m_from.rows(); y++) { + /* copy rightmost column to the buffer column 0 */ + m_from(y, 0) = m_from(y, m_from.cols() - 2); + + /* copy leftmost column to the buffer column XSIZE + 1 */ + m_from(y, m_from.cols() - 1) = m_from(y, 1); + } + +#pragma omp section +#pragma omp parallel for + for (Eigen::Index x = 0; x < m_from.cols(); x++) { + /* copy bottommost row to buffer row 0 */ + m_from(0, x) = m_from(m_from.rows() - 2, x); + + /* copy topmost row to buffer row lines + 1 */ + m_from(m_from.rows() - 1, x) = m_from(1, x); + } + } + } + + /* make one simulation iteration with lines lines. + * old configuration is in from, new one is written to to. + */ + void simulate() + { +#pragma omp parallel for + for (Eigen::Index y = 1; y <= m_from.rows() - 2; y++) { + for (Eigen::Index x = 1; x <= m_from.cols() - 2; x++) + m_to(y, x) = transition(m_from, x, y); + } + } + + /* a: reference to matrix; x,y: coordinates; result: n-th element of anneal, + where n is the number of neighbors */ + cell_state_t transition(const Field &a, const Eigen::Index x, const Eigen::Index y) const + { + return ANNEAL[a(Eigen::seq(y - 1, y + 1), Eigen::seq(x - 1, x + 1)).sum()]; + } +}; + +int main(int argc, char **argv) +{ + assert(argc == 3); + + int lines = atoi(argv[1]); + int its = atoi(argv[2]); + + Automat ca {lines}; + + ca.run(its); + ca.report(); + + return EXIT_SUCCESS; +} diff --git a/src/benchmarks/zellularautomat/cpp/meson.build b/src/benchmarks/zellularautomat/cpp/meson.build new file mode 100644 index 00000000..772dffb9 --- /dev/null +++ b/src/benchmarks/zellularautomat/cpp/meson.build @@ -0,0 +1,31 @@ +sources = [ + 'main.cpp', +] + +# Build wrapped dependencies as static libraries and disable warnings +dependency_options = [ + 'default_library=static', + 'warning_level=0', + 'werror=false', +] + +dependencies = [ + dependency('openmp'), + dependency( + 'eigen3', + fallback: ['eigen', 'eigen_dep'], + default_options: dependency_options, + include_type: 'system', + ), +] + +options = [ + 'cpp_std=c++17', +] + +executable( + 'zellularautomat-cpp', + sources, + dependencies: dependencies, + override_options: options, +) diff --git a/src/benchmarks/zellularautomat/meson.build b/src/benchmarks/zellularautomat/meson.build index c957e89f..3a509a0d 100644 --- a/src/benchmarks/zellularautomat/meson.build +++ b/src/benchmarks/zellularautomat/meson.build @@ -1,2 +1,3 @@ subdir('c') +subdir('cpp') subdir('fortran') diff --git a/subprojects/eigen.wrap b/subprojects/eigen.wrap new file mode 100644 index 00000000..becc4767 --- /dev/null +++ b/subprojects/eigen.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = eigen-3.4.0 +source_url = https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.bz2 +source_filename = eigen-3.4.0.tar.bz2 +source_hash = b4c198460eba6f28d34894e3a5710998818515104d6e74e5cc331ce31e46e626 +patch_filename = eigen_3.4.0-2_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/eigen_3.4.0-2/get_patch +patch_hash = cb764fd9fec02d94aaa2ec673d473793c0d05da4f4154c142f76ef923ea68178 +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/eigen_3.4.0-2/eigen-3.4.0.tar.bz2 +wrapdb_version = 3.4.0-2 + +[provide] +eigen3 = eigen_dep -- GitLab