diff --git a/Lab04/HPC-Lab-04.pdf b/Lab04/HPC-Lab-04.pdf new file mode 100644 index 0000000..0dc215f Binary files /dev/null and b/Lab04/HPC-Lab-04.pdf differ diff --git a/Lab04/find_max b/Lab04/find_max new file mode 100644 index 0000000..4599f63 Binary files /dev/null and b/Lab04/find_max differ diff --git a/Lab04/find_max.cpp b/Lab04/find_max.cpp new file mode 100644 index 0000000..8d7b109 --- /dev/null +++ b/Lab04/find_max.cpp @@ -0,0 +1,39 @@ +#include +#include +#include +#include + +/** + * Function to find the maximum element in a vector using multiple threads. + * + * @param maxElement Reference to the variable where the maximum element will be stored. + * @param v The vector of integers to search. + */ +int findMaxElement(const std::vector& v) { + int maxElement = std::numeric_limits::min(); + // Find max element in the range + for (int i = 0; i < v.size(); ++i) { + if (v[i] > maxElement) { + maxElement = v[i]; + } + } + return maxElement; +} + +int main() { + // Create a vector with some random values + const int n = 10240; + std::vector v(n); + v[0] = 1; + for (int i = 1; i < n; ++i) { + v[i] = (75 * v[i - 1]) % 65537; + } + + // Find the maximum element in the vector + // This should be done in parallel using multiple threads + int maxElement = findMaxElement(v); + + std::cout << "Maximum element: " << maxElement << std::endl; + + return 0; +} \ No newline at end of file diff --git a/Lab04/find_max_threads b/Lab04/find_max_threads new file mode 100644 index 0000000..b571186 Binary files /dev/null and b/Lab04/find_max_threads differ diff --git a/Lab04/find_max_threads.cpp b/Lab04/find_max_threads.cpp new file mode 100644 index 0000000..d05aa8f --- /dev/null +++ b/Lab04/find_max_threads.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include + +/** + * Function to find the maximum element in a vector using multiple threads. + * + * @param maxElement Reference to the variable where the maximum element will be stored. + * @param v The vector of integers to search. + */ +void findMaxElement(const std::vector& v, const int start, const int end, int& result) { + int maxElement = std::numeric_limits::min(); + // Find max element in the range + for (int i = start; i < end; ++i) { + if (v[i] > maxElement) { + maxElement = v[i]; + } + } + result = maxElement; +} + +int main() { + // Create a vector with some random values + const int n = 10240; + const int threadCounter = 4; + std::vector threads; + std::vector res(4, 0); + std::vector v(n); + v[0] = 1; + for (int i = 1; i < n; ++i) { + v[i] = (75 * v[i - 1]) % 65537; + } + + for(int i = 0; i < threadCounter; i++) { + int start = n / threadCounter * i; + int end = n / threadCounter * (i + 1); + + threads.emplace_back(findMaxElement, std::ref(v), start, end, std::ref(res[i])); + } + + for(auto&t : threads) { + t.join(); + } + + int max = res[0]; + for(int i = 1; i < res.size(); i++) { + if (max < res[i]){ + max = res[i]; + } + } + + // Find the maximum element in the vector + // This should be done in parallel using multiple threads + //int maxElement = findMaxElement(v); + + std::cout << "Maximum element: " << max << std::endl; + + return 0; +} diff --git a/Lab04/helgrind.out b/Lab04/helgrind.out new file mode 100644 index 0000000..7402de1 --- /dev/null +++ b/Lab04/helgrind.out @@ -0,0 +1,6 @@ +==5173== Helgrind, a thread error detector +==5173== Copyright (C) 2007-2017, and GNU GPL'd, by OpenWorks LLP et al. +==5173== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info +==5173== Command: ./find_max_threads +==5173== Parent PID: 5132 +==5173== diff --git a/Lab04/llvm.sh b/Lab04/llvm.sh new file mode 100644 index 0000000..c2e26ec --- /dev/null +++ b/Lab04/llvm.sh @@ -0,0 +1,184 @@ +#!/bin/bash +################################################################################ +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +################################################################################ +# +# This script will install the llvm toolchain on the different +# Debian and Ubuntu versions + +set -eux + +usage() { + set +x + echo "Usage: $0 [llvm_major_version] [all] [OPTIONS]" 1>&2 + echo -e "all\t\t\tInstall all packages." 1>&2 + echo -e "-n=code_name\t\tSpecifies the distro codename, for example bionic" 1>&2 + echo -e "-h\t\t\tPrints this help." 1>&2 + echo -e "-m=repo_base_url\tSpecifies the base URL from which to download." 1>&2 + exit 1; +} + +CURRENT_LLVM_STABLE=18 +BASE_URL="http://apt.llvm.org" + +# Check for required tools +needed_binaries=(lsb_release wget add-apt-repository gpg) +missing_binaries=() +for binary in "${needed_binaries[@]}"; do + if ! which $binary &>/dev/null ; then + missing_binaries+=($binary) + fi +done +if [[ ${#missing_binaries[@]} -gt 0 ]] ; then + echo "You are missing some tools this script requires: ${missing_binaries[@]}" + echo "(hint: apt install lsb-release wget software-properties-common gnupg)" + exit 4 +fi + +# Set default values for commandline arguments +# We default to the current stable branch of LLVM +LLVM_VERSION=$CURRENT_LLVM_STABLE +ALL=0 +DISTRO=$(lsb_release -is) +VERSION=$(lsb_release -sr) +UBUNTU_CODENAME="" +CODENAME_FROM_ARGUMENTS="" +# Obtain VERSION_CODENAME and UBUNTU_CODENAME (for Ubuntu and its derivatives) +source /etc/os-release +DISTRO=${DISTRO,,} +case ${DISTRO} in + debian) + # Debian Trixie has a workaround because of + # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1038383 + if [[ "${VERSION}" == "unstable" ]] || [[ "${VERSION}" == "testing" ]] || [[ "${VERSION_CODENAME}" == "trixie" ]]; then + CODENAME=unstable + LINKNAME= + else + # "stable" Debian release + CODENAME=${VERSION_CODENAME} + LINKNAME=-${CODENAME} + fi + ;; + *) + # ubuntu and its derivatives + if [[ -n "${UBUNTU_CODENAME}" ]]; then + CODENAME=${UBUNTU_CODENAME} + if [[ -n "${CODENAME}" ]]; then + LINKNAME=-${CODENAME} + fi + fi + ;; +esac + +# read optional command line arguments +if [ "$#" -ge 1 ] && [ "${1::1}" != "-" ]; then + if [ "$1" != "all" ]; then + LLVM_VERSION=$1 + else + # special case for ./llvm.sh all + ALL=1 + fi + OPTIND=2 + if [ "$#" -ge 2 ]; then + if [ "$2" == "all" ]; then + # Install all packages + ALL=1 + OPTIND=3 + fi + fi +fi + +while getopts ":hm:n:" arg; do + case $arg in + h) + usage + ;; + m) + BASE_URL=${OPTARG} + ;; + n) + CODENAME=${OPTARG} + if [[ "${CODENAME}" == "unstable" ]]; then + # link name does not apply to unstable repository + LINKNAME= + else + LINKNAME=-${CODENAME} + fi + CODENAME_FROM_ARGUMENTS="true" + ;; + esac +done + +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root!" + exit 1 +fi + +declare -A LLVM_VERSION_PATTERNS +LLVM_VERSION_PATTERNS[9]="-9" +LLVM_VERSION_PATTERNS[10]="-10" +LLVM_VERSION_PATTERNS[11]="-11" +LLVM_VERSION_PATTERNS[12]="-12" +LLVM_VERSION_PATTERNS[13]="-13" +LLVM_VERSION_PATTERNS[14]="-14" +LLVM_VERSION_PATTERNS[15]="-15" +LLVM_VERSION_PATTERNS[16]="-16" +LLVM_VERSION_PATTERNS[17]="-17" +LLVM_VERSION_PATTERNS[18]="-18" +LLVM_VERSION_PATTERNS[19]="-19" +LLVM_VERSION_PATTERNS[20]="" + +if [ ! ${LLVM_VERSION_PATTERNS[$LLVM_VERSION]+_} ]; then + echo "This script does not support LLVM version $LLVM_VERSION" + exit 3 +fi + +LLVM_VERSION_STRING=${LLVM_VERSION_PATTERNS[$LLVM_VERSION]} + +# join the repository name +if [[ -n "${CODENAME}" ]]; then + REPO_NAME="deb ${BASE_URL}/${CODENAME}/ llvm-toolchain${LINKNAME}${LLVM_VERSION_STRING} main" + + # check if the repository exists for the distro and version + if ! wget -q --method=HEAD ${BASE_URL}/${CODENAME} &> /dev/null; then + if [[ -n "${CODENAME_FROM_ARGUMENTS}" ]]; then + echo "Specified codename '${CODENAME}' is not supported by this script." + else + echo "Distribution '${DISTRO}' in version '${VERSION}' is not supported by this script." + fi + exit 2 + fi +fi + + +# install everything + +if [[ ! -f /etc/apt/trusted.gpg.d/apt.llvm.org.asc ]]; then + # download GPG key once + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc +fi + +if [[ -z "`apt-key list 2> /dev/null | grep -i llvm`" ]]; then + # Delete the key in the old format + apt-key del AF4F7421 +fi +if [[ "${VERSION_CODENAME}" == "bookworm" ]]; then + # add it twice to workaround: + # https://github.com/llvm/llvm-project/issues/62475 + add-apt-repository -y "${REPO_NAME}" +fi + +add-apt-repository -y "${REPO_NAME}" +apt-get update +PKG="clang-$LLVM_VERSION lldb-$LLVM_VERSION lld-$LLVM_VERSION clangd-$LLVM_VERSION" +if [[ $ALL -eq 1 ]]; then + # same as in test-install.sh + # No worries if we have dups + PKG="$PKG clang-tidy-$LLVM_VERSION clang-format-$LLVM_VERSION clang-tools-$LLVM_VERSION llvm-$LLVM_VERSION-dev lld-$LLVM_VERSION lldb-$LLVM_VERSION llvm-$LLVM_VERSION-tools libomp-$LLVM_VERSION-dev libc++-$LLVM_VERSION-dev libc++abi-$LLVM_VERSION-dev libclang-common-$LLVM_VERSION-dev libclang-$LLVM_VERSION-dev libclang-cpp$LLVM_VERSION-dev libunwind-$LLVM_VERSION-dev" + if test $LLVM_VERSION -gt 14; then + PKG="$PKG libclang-rt-$LLVM_VERSION-dev libpolly-$LLVM_VERSION-dev" + fi +fi +apt-get install -y $PKG diff --git a/Lab04/start_triad_bench.sh b/Lab04/start_triad_bench.sh new file mode 100644 index 0000000..57e82ec --- /dev/null +++ b/Lab04/start_triad_bench.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +#SBATCH --job-name=G5_triad_bench +#SBATCH --nodes=4 +#SBATCH --ntasks=1 + +# Your script goes here +./triad_bench \ No newline at end of file diff --git a/Lab04/triad_bench b/Lab04/triad_bench new file mode 100644 index 0000000..0a118af Binary files /dev/null and b/Lab04/triad_bench differ diff --git a/Lab04/triad_bench.cpp b/Lab04/triad_bench.cpp new file mode 100644 index 0000000..6ea002a --- /dev/null +++ b/Lab04/triad_bench.cpp @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include + +std::vector B(1000000, 1); +std::vector C(1000000, 2); +std::vector D(1000000, 3); + +static double ownMethod(int n) { + std::vector A(n, 0); + std::chrono::duration sumTime; + for (int i = 0; i < 20; ++i) { + auto startTime = std::chrono::system_clock::now(); + for (int j = 0; j < n; ++j) { + A[j] = B[j] + C[j] * D[j]; + } + // prevent the compiler from optimizing everything away + volatile double dummy = A[0]; + auto endTime = std::chrono::system_clock::now(); + sumTime += endTime - startTime; + } + double averageTime = sumTime.count() / 20; + return averageTime; +} + +static std::vector ownMethod2(int n) { + std::vector A(n, 0); + std::vector results(2); + std::chrono::duration sumTime; + for (int i = 0; i < 20; ++i) { + auto startTime = std::chrono::system_clock::now(); + for (int j = 0; j < n; ++j) { + A[j] = B[j] + C[j] * D[j]; + } + // prevent the compiler from optimizing everything away + volatile double dummy = A[0]; + auto endTime = std::chrono::system_clock::now(); + sumTime += endTime - startTime; + } + double averageTime = sumTime.count() / 20; + double flops = (2.0 * n) / (averageTime / 1000); + results[0] = std::to_string(averageTime); + results[1] = std::to_string(flops); + return results; +} + +static void workerThread(int start, int end, int threadCount) { + std::vector> outcomes; + for (int i = start; i < end; i += 10) { + outcomes.push_back(ownMethod2(i)); + } + std::stringstream fileName; + fileName << "outcomes-" << start << "-" << end << ".csv"; + std::ofstream MyFile(fileName.str()); + for (int i = 0; i < outcomes.size(); i++) { + MyFile << "(" << outcomes[i][0] << "," << outcomes[i][1] << "," << threadCount << ")"; + } + MyFile.close(); +} + +int main() { + int range_start = 100; + int range_end = 1'000'000; + int countThreads[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64}; + // int num_threads = std::thread::hardware_concurrency(); // use system's core + // count + for (int j = 0; j < (sizeof(countThreads) / sizeof(int)); j++) { + int total = range_end - range_start; + int chunk_size = total / countThreads[j]; + + std::vector threads; + + for (int i = 1; i < countThreads[j]; ++i) { + int start = range_start + i * chunk_size; + int end = (i == countThreads[j] - 1) ? range_end : start + chunk_size; + + threads.emplace_back(workerThread, start, end, countThreads[j]); + } + workerThread(range_start, range_end, countThreads[j]); + + for (auto& t : threads) + t.join(); + + std::cout << "All threads finished.\n"; + } + + return 0; +} \ No newline at end of file