Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • flow/emper
  • aj46ezos/emper
  • i4/manycore/emper
3 results
Show changes
Commits on Source (125)
Showing with 441 additions and 27 deletions
......@@ -6,15 +6,18 @@ Checks: >
performance-*,
portability-*,
readability-*,
-bugprone-assignment-in-if-condition,
-bugprone-easily-swappable-parameters,
-cert-err58-cpp,
-clang-diagnostic-empty-translation-unit,
-performance-avoid-endl,
-readability-braces-around-statements,
-readability-function-cognitive-complexity,
-readability-identifier-length,
-readability-implicit-bool-conversion,
-readability-isolate-declaration,
-readability-magic-numbers,
-readability-simplify-boolean-expr,
WarningsAsErrors: >
bugprone-*,
......
*
!.gitignore
image: "flowdalic/debian-testing-dev:1.20"
image: "flowdalic/gentoo-dev:20241216"
before_script:
- ulimit -a
......@@ -19,6 +19,10 @@ before_script:
valgrind
EOF
for tool in ${TOOLS[@]}; do
if ! command -v $tool; then
echo "No $tool binary found"
continue
fi
echo -n "$tool version: "
$tool --version
done
......@@ -29,8 +33,10 @@ cache:
variables:
BUILDDTYPE: debugoptimized
CC: gcc
CXX: g++
GCC: gcc
GXX: g++
CC: $GCC
CXX: $GXX
EXTRA_NINJA_ARGS: -v
stages:
......@@ -87,8 +93,8 @@ clang-tidy:
.gcc:
variables:
CC: gcc
CXX: g++
CC: $GCC
CXX: $GXX
.clang:
variables:
......@@ -103,6 +109,10 @@ clang-tidy:
EMPER_CPP_ARGS: "-stdlib=libc++"
EMPER_CPP_LINK_ARGS: "-stdlib=libc++"
.lto:
variables:
EMPER_B_LTO: "true"
.emper-ws-scheduling:
variables:
EMPER_DEFAULT_SCHEDULING_STRATEGY: "work_stealing"
......@@ -293,6 +303,11 @@ smoke-test-libc++:
- .fast-variant-check
- .libc++
test-lto:
extends:
- .test
- .lto
test-libc++:
extends:
- .test
......
......@@ -50,6 +50,28 @@ clang:
CC=clang CXX=clang++ \
BUILDDIR="build-$@"
.PHONY: clang-release
clang-release:
rm -f build
$(MAKE) build \
CC=clang CXX=clang++ \
BUILDTYPE=release \
BUILDDIR="build-$@"
.PHONY: gcc-%
gcc-%: GCC_VER=$(subst gcc-,,$@)
gcc-%:
rm -f build
$(MAKE) build \
CC=gcc-$(GCC_VER) CXX=g++-$(GCC_VER) \
BUILDDIR="build-$@"
.PHONY: test-%
test-%: TEST_BUILD=$(subst test-,,$@)
test-%:
$(MAKE) $(TEST_BUILD)
meson test -C build-$(TEST_BUILD)
.PHONY: fibril-locked
fibril-locked:
rm -f build
......@@ -75,6 +97,35 @@ stats:
EMPER_STATS=true \
BUILDDIR="build-$@"
.PHONY: lto
lto:
rm -f build
$(MAKE) build \
BUILDTYPE=release \
EMPER_B_LTO=true \
BUILDDIR="build-$@"
.PHONY: asan
asan:
rm -f build
$(MAKE) build \
EMPER_LOG_LEVEL="Info" \
EMPER_B_SANITIZE=address \
BUILDDIR="build-$@"
.PHONY: asan-test
asan-test: asan
meson test -C build-asan
.PHONY: waitfd
waitfd:
rm -f build
$(MAKE) build \
BUILDTYPE=release \
BUILDDIR="build-$@" \
EMPER_WORKER_SLEEP_STRATEGY=waitfd
.PHONY: fast-static-analysis
fast-static-analysis: all check-format check-license doc
......@@ -140,7 +191,7 @@ stresstest: test
# e.g. test-gcc.
.PHONY: gitlab-runner
gitlab-runner:
gitlab-runner exec docker smoke-test
gitlab-ci-local smoke-test-suite
.PHONY: subprojects
subprojects:
......
......@@ -24,11 +24,11 @@ emper_fibril auto fib(int n) -> int {
Fibril fibril;
int a, b;
fibril.fork(&a, fib, n - 1);
fibril.spawn(&a, fib, n - 1);
b = fib(n - 2);
fibril.join();
fibril.sync();
return a + b;
}
......
......@@ -5,6 +5,7 @@
#include <array>
#include <atomic>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <mutex>
#include <string>
......
......@@ -7,6 +7,7 @@
#include <netinet/tcp.h>
#include <sys/socket.h> // for shutdown, socket, AF_INET
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <algorithm> // for find
......@@ -20,7 +21,8 @@
#include <cstring> // for memcmp
#include <iomanip>
#include <iostream> // for operator<<, basic_ostream, endl
#include <string> // for allocator, string, char_traits
#include <sstream>
#include <string> // for allocator, string, char_traits
#include <thread>
#include <utility>
......@@ -102,7 +104,7 @@ struct TimeStamps {
class Client {
using IterationResult = std::pair<int, std::string>;
enum class ClientState {
enum class ClientState : std::uint8_t {
unknown,
connecting,
sending,
......@@ -229,9 +231,9 @@ class Client {
}
}
enum class CollectTimeStamps { yes, no };
enum class CollectTimeStamps : std::uint8_t { yes, no };
enum class LinkedFutures { yes, no };
enum class LinkedFutures : std::uint8_t { yes, no };
template <CollectTimeStamps collectTimeStampsSwitch, LinkedFutures linkedFuturesSwitch>
void _run() {
......@@ -251,7 +253,8 @@ class Client {
RecvFuture recvFuture(sock, inBuf, size, MSG_WAITALL);
// prepare output buf
sprintf(outBuf, "%lu:%lu", id, iteration);
int res = sprintf(outBuf, "%lu:%lu", id, iteration);
if (res < 0) DIE_MSG("sprintf failed");
echoStart = high_resolution_clock::now();
if constexpr (collectTimeStamps) {
......@@ -402,6 +405,7 @@ static auto getOption(int argc, char** argv, const std::string& option) -> char*
char** end = argv + argc;
char** itr = std::find(argv, end, option);
// NOLINTNEXTLINE(bugprone-inc-dec-in-conditions)
if (itr != end && ++itr != end) {
return *itr;
}
......
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2020-2021 Florian Fischer
#include <sys/socket.h>
#include <sys/types.h>
#include <atomic>
#include <cerrno>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <iostream>
#include <random>
#include <string>
#include <vector>
#include "Common.hpp"
#include "Debug.hpp"
......@@ -35,6 +38,7 @@ static float max_computations_probability = -1;
static std::atomic<bool> quit = false;
// NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp)
static thread_local std::mt19937 randGenerator;
static auto getComputation() -> unsigned {
// fixed computation is computations_us
......
......@@ -7,8 +7,10 @@
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
#include "Common.hpp"
#include "Debug.hpp"
......
......@@ -3,7 +3,7 @@
#include <boost/program_options.hpp>
#include <cstdint>
#include <iostream> // for basic_ostream::operator<<
#include <memory>
#include <string>
#include <thread>
#include "BinaryPrivateSemaphore.hpp" // for BPS
......
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2024 Florian Schmaus
#include <array>
#include <boost/numeric/ublas/io.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/program_options.hpp>
#include <chrono>
#include <cstdint>
#include <cstdlib>
#include <exception>
#include <iostream>
#include <random>
#include <string>
#include <thread>
#include <type_traits>
#include <vector>
#include "Common.hpp"
#include "CountingPrivateSemaphore.hpp"
#include "Fiber.hpp"
#include "Runtime.hpp"
#include "Semaphore.hpp"
#include "emper.hpp"
namespace po = boost::program_options;
template <class T>
using matrix = boost::numeric::ublas::matrix<T>;
static auto sequentialMatMul(matrix<double> &a, matrix<double> &b) -> matrix<double> {
const auto m = a.size1(); // number of rows of a
const auto n = a.size2(); // number of columns of a
if (n != b.size1()) DIE_MSG("Invalid argument");
const auto l = b.size2(); // number of rows of b
matrix<double> res(m, l);
for (unsigned int i = 0; i < m; ++i) {
for (unsigned int j = 0; j < l; ++j) {
res(i, j) = 0;
for (unsigned int k = 0; k < n; ++k) {
res(i, j) = res(i, j) + a(i, k) * b(k, j);
}
}
}
return res;
}
static auto naiveParallelMatMul(matrix<double> &a, matrix<double> &b) -> matrix<double> {
const auto m = a.size1(); // number of rows of a
const auto n = a.size2(); // number of columns of a
if (n != b.size1()) DIE_MSG("Invalid argument");
const auto l = b.size2(); // number of rows of b
matrix<double> res(m, l);
CPS cps;
for (unsigned int i = 0; i < m; ++i) {
for (unsigned int j = 0; j < l; ++j) {
spawn(
[i, j, n, &a, &b, &res] {
res(i, j) = 0;
for (unsigned int k = 0; k < n; ++k) {
res(i, j) = res(i, j) + a(i, k) * b(k, j);
}
},
cps);
}
}
cps.wait();
return res;
}
static auto stupidParallelMatMul(matrix<double> &a, matrix<double> &b) -> matrix<double> {
const auto m = a.size1(); // number of rows of a
const auto n = a.size2(); // number of columns of a
if (n != b.size1()) DIE_MSG("Invalid argument");
const auto l = b.size2(); // number of rows of b
matrix<double> res(m, l);
CPS outerCps;
for (unsigned int i = 0; i < m; ++i) {
for (unsigned int j = 0; j < l; ++j) {
outerCps.incrementCounterByOne();
auto *innerCps = new CPS();
auto *sem = new emper::Semaphore(1);
auto *scalprod = new double;
for (unsigned int k = 0; k < n; ++k) {
spawn(
[i, j, k, sem, scalprod, &a, &b] {
double product = a(i, k) * b(k, j);
sem->acquire();
*scalprod += product;
sem->release();
},
*innerCps);
}
async([i, j, innerCps, sem, scalprod, &res, &outerCps] {
innerCps->wait();
res(i, j) = *scalprod;
outerCps.signal();
delete innerCps;
delete sem;
delete scalprod;
});
}
}
outerCps.wait();
return res;
}
template <class G>
static auto createRandomMatrix(unsigned int m_dim, unsigned int n_dim, float zero_probability, G &g)
-> matrix<double> {
double min = 0;
double max = 1'000'000'000'000'000;
std::uniform_real_distribution<> value_distribution(min, max);
std::bernoulli_distribution zero_distribution(zero_probability);
matrix<double> res(m_dim, n_dim);
for (unsigned int m = 0; m < m_dim; ++m) {
for (unsigned int n = 0; n < n_dim; ++n) {
double value;
if (zero_distribution(g))
value = 0;
else
value = value_distribution(g);
res(m, n) = value;
}
}
return res;
}
using ParMatMul = struct {
std::string name;
matrix<double> (*mul)(matrix<double> &a, matrix<double> &b);
};
namespace chrn = std::chrono;
using Clock = std::chrono::high_resolution_clock;
static auto matmul(const po::variables_map &vm) -> int {
const auto nthreads = vm["nthreads"].as<unsigned int>();
const auto seed = vm["seed"].as<std::uint_fast64_t>();
const auto m = vm["m"].as<unsigned int>();
const auto n = vm["n"].as<unsigned int>();
const auto l = vm["l"].as<unsigned int>();
const auto par_mat_muls = vm["parMatMuls"].as<unsigned int>();
const auto iters = vm["iters"].as<unsigned int>();
const auto zero_probability = vm["zeroProb"].as<float>();
const auto verify = vm["verify"].as<bool>();
Runtime runtime(nthreads);
std::mt19937_64 rng(seed);
std::vector<matrix<double>> a_mtxs, b_mtxs, seq_results;
for (unsigned int i = 0; i < par_mat_muls; ++i) {
auto a = createRandomMatrix(m, n, zero_probability, rng);
a_mtxs.push_back(a);
auto b = createRandomMatrix(n, l, zero_probability, rng);
b_mtxs.push_back(b);
auto seq_res = sequentialMatMul(a_mtxs[i], b_mtxs[i]);
seq_results.push_back(seq_res);
}
auto par_mat_mul_impls = std::to_array<ParMatMul>({
{"naive", naiveParallelMatMul},
{"stupid", stupidParallelMatMul},
});
int exitStatus = EXIT_SUCCESS;
Fiber *alphaFiber = Fiber::from([&] {
for (auto impl : par_mat_mul_impls) {
std::vector<chrn::milliseconds> durations(iters);
std::cout << "Start " << impl.name << " with " << iters << " iterations: " << std::flush;
for (unsigned int iter = 0; iter < iters; ++iter) {
std::vector<matrix<double>> results(par_mat_muls);
CPS cps(par_mat_muls);
chrn::time_point<Clock> start = Clock::now();
for (unsigned int i = 0; i < par_mat_muls; ++i) {
async([i, &cps, &impl, &results, &a_mtxs, &b_mtxs] {
auto a = a_mtxs[i];
auto b = b_mtxs[i];
auto res = impl.mul(a, b);
results[i] = res;
cps.signal();
});
}
cps.wait();
chrn::time_point<Clock> end = Clock::now();
std::cout << "." << std::flush;
auto duration = chrn::duration_cast<chrn::milliseconds>(end - start);
durations[iter] = duration;
if (!verify) continue;
for (unsigned int i = 0; i < par_mat_muls; ++i) {
if (boost::numeric::ublas::detail::equals(seq_results[i], results[i], 1.e-6, 0.))
continue;
// clang-format off
std::cerr
<< std::endl
<< "ERROR: Matrix " << i << " produced by " << impl.name << " incorrect" << std::endl
<< "Expected matrix: " << std::endl << seq_results[i] << std::endl
<< "Provided matrix: " << std::endl << results[i] << std::endl
;
// clang-format on
exitStatus = EXIT_FAILURE;
runtime.initiateTermination();
return;
}
}
std::cout << std::endl << "Implementation '" << impl.name << "' took ";
for (unsigned int i = 0; i < iters; ++i) {
std::cout << durations[i];
if (i + 1 != iters) std::cout << ", ";
}
std::cout << ";" << std::endl;
}
runtime.initiateTermination();
});
runtime.scheduleFromAnywhere(*alphaFiber);
runtime.waitUntilFinished();
return exitStatus;
}
auto main(int argc, char *argv[]) -> int {
po::options_description desc("Allowed options");
// clang-format off
desc.add_options()
("help", "Show help")
("nthreads", po::value<unsigned int>()->default_value(std::thread::hardware_concurrency()), "Number of worker threads used by EMPER's runtime system")
("seed", po::value<std::uint_fast64_t>()->default_value(20180922), "Initial seed of the random number generator")
("m", po::value<unsigned int>()->default_value(10), "Number of rows of the first matrix and of the result matrix")
("n", po::value<unsigned int>()->default_value(10), "Number of columns of the first matrix and of rows of the second matrix")
("l", po::value<unsigned int>()->default_value(10), "Number of columns of the second matrix and of the result matrix")
("parMatMuls", po::value<unsigned int>()->default_value(5), "Number of matrix multiplications to perform in parallel")
("iters", po::value<unsigned int>()->default_value(3), "Number of benchmark iterations")
("zeroProb", po::value<float>()->default_value(0.01), "Probaility of generating a zero when initializing the matrices")
("verify", po::value<bool>()->default_value(true), "Verify the result of the matrix multiplication")
;
// clang-format on
auto parse_result = po::command_line_parser(argc, argv).options(desc).run();
po::variables_map vm;
po::store(parse_result, vm);
po::notify(vm);
if (vm.count("help")) {
std::cout << desc << std::endl;
return EXIT_SUCCESS;
}
try {
return matmul(vm);
} catch (std::exception &e) {
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}
}
......@@ -3,7 +3,6 @@
#include <chrono> // for milliseconds, operator+, hig...
#include <cstdlib> // for exit, EXIT_SUCCESS
#include <iostream> // for operator<<, basic_ostream, endl
#include <ratio> // for ratio
#include <string>
#include "CountingPrivateSemaphore.hpp" // for CPS
......@@ -33,8 +32,7 @@ static void letsGetBusy(std::chrono::duration<Rep, Period> duration) {
// TODO: The suppressed linter error below may be a false positive
// reported by clang-tidy.
// NOLINTNEXTLINE(modernize-use-nullptr)
while (std::chrono::high_resolution_clock::now() < deadline)
;
while (std::chrono::high_resolution_clock::now() < deadline);
}
static void alphaFiber() {
......
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2021-2022 Florian Fischer, Florian Schmaus
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <array>
......@@ -9,6 +10,7 @@
#include <cstring>
#include <exception>
#include <filesystem>
#include <functional>
#include <iostream>
#include <sstream>
#include <string>
......@@ -24,7 +26,7 @@
namespace fs = std::filesystem;
namespace po = boost::program_options;
#define EMPER_RIPGREP_BUFSIZE 4096
static constexpr size_t EMPER_RIPGREP_BUFSIZE = 4096;
static const char* needle;
static size_t needle_len;
......@@ -50,7 +52,7 @@ void search(const std::string& path) {
ssize_t bytes_read = emper::io::readFileAndWait(fd, buf.data(), buf.size(), 0);
while (bytes_read > 0) {
if (memmem(&buf[0], bytes_read, needle, needle_len)) {
if (memmem(buf.data(), bytes_read, needle, needle_len)) {
outBuf->getStream() << path << std::endl;
goto out;
}
......
......@@ -76,7 +76,7 @@ class FileSearcher {
DIE_MSG_ERRNO("read failed");
}
if (memmem(&buf[0], bytes_read, needle, needle_len)) {
if (memmem(buf.data(), bytes_read, needle, needle_len)) {
printf("%s\n", path.c_str());
delete this;
return;
......
......@@ -54,12 +54,38 @@ cpp_can_link_with_boost_program_options = cpp_compiler.links(
dependencies: boost_program_options_dep,
)
if cpp_can_link_with_boost_program_options
boost_ublas_code = '''
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>
int main (int argc, char* argv[]) {
using namespace boost::numeric::ublas;
matrix m (3, 3);
for (unsigned i = 0; i < m.size1 (); ++ i)
for (unsigned j = 0; j < m.size2 (); ++ j)
m (i, j) = 3 * i + j;
std::cout << m << std::endl;
}
'''
cpp_can_link_with_boost_ublas = cpp_compiler.links(
boost_ublas_code,
name: 'boost_ublas',
dependencies: boost_dep,
)
if (not automagic) or cpp_can_link_with_boost_program_options
fib_child_stealing_exe = executable(
'fib-child-stealing',
'FibChildStealing.cpp',
dependencies: [emper_dep, boost_program_options_dep],
)
if (not automagic) or cpp_can_link_with_boost_ublas
mat_mul_exe = executable(
'mat-mul',
'MatMul.cpp',
dependencies: [emper_dep, boost_dep, boost_program_options_dep],
)
endif
endif
subdir('fsearch')
......@@ -12,5 +12,6 @@ docker run \
--env USER_ID="${UID}" \
--env GROUP_ID="$(id -g ${USER})" \
--security-opt=seccomp:unconfined \
--entrypoint "${EMPER_ROOT}/tools/docker-prepare" \
"${IMAGE}" \
"${EMPER_ROOT}/tools/docker-prepare" "${EMPER_ROOT}" $@
"${EMPER_ROOT}" "$@"
......@@ -3,6 +3,7 @@
#include "BinaryPrivateSemaphore.hpp"
#include <cassert> // for assert
#include <ostream>
#include "Common.hpp" // for unlikely
#include "Context.hpp" // for Context
......
......@@ -71,7 +71,8 @@ class Blockable : public Logger<logSubsystem> {
// Only contexts managed by EMPER's runtime can block.
emper::assertInRuntime();
LOGD("block() blockedContext=" << Context::getCurrentContext());
LOGD("block() context " << Context::getCurrentContext() << " and fiber "
<< Context::getCurrentFiber());
maybeSetAffinity();
......@@ -120,6 +121,8 @@ class Blockable : public Logger<logSubsystem> {
void unblock(Context* context) {
Fiber* unblockFiber = unblockAndGetContinuation(context);
LOGD("unblock() context " << context << " via unblock fiber " << unblockFiber);
if constexpr (callerEnvironment == CallerEnvironment::EMPER) {
runtime.schedule(*unblockFiber);
} else {
......
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2020-2021 Florian Schmaus
// Copyright © 2020-2024 Florian Schmaus
#include "Common.hpp"
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <string>
#include "log/log.hpp"
void die(const char* message, bool usePerror) {
if (usePerror) {
// TODO: Include errno value and short description (e.g., ENOSYS).
std::perror(message);
} else {
std::cerr << message << std::endl;
emper::log::log("!!!!! PANIC", message);
}
abort();
......
......@@ -2,6 +2,8 @@
// Copyright © 2020-2021 Florian Schmaus, Florian Fischer
#pragma once
#include <errno.h>
#include <functional>
#include <sstream> // IWYU pragma: keep
#include <type_traits> // IWYU pragma: keep
......@@ -21,11 +23,12 @@ using func_t = std::function<void()>;
#define DIE_MSG(x) do { std::stringstream sst; sst << __FILE__ ":" TOSTRING(__LINE__) " " << x; die(sst.str().c_str(), false); } while (false)
// NOLINTNEXTLINE(bugprone-macro-parentheses)
#define DIE_MSG_ERRNO(x) do { std::stringstream sst; sst << __FILE__ ":" TOSTRING(__LINE__) " " << x; die(sst.str().c_str(), true); } while (false)
#define DIE_MSG_ERRNO_IS(e, x) do { errno = -e; DIE_MSG_ERRNO(x); } while (false)
// clang-format on
// We compile with -fno-exceptions for the moment.
//#define THROW(x) do { throw std::runtime_error(x); } while (false)
// #define THROW(x) do { throw std::runtime_error(x); } while (false)
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
......@@ -42,6 +45,7 @@ using WORD = unsigned int;
#ifdef EMPER_STATS
#include <chrono> // IWYU pragma: keep
#define TIME_NS(code, record_func) \
auto _time_ns_start = std::chrono::high_resolution_clock::now(); \
code; \
......