diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 59e1306c..a70258da 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -75,35 +75,37 @@ jobs: os: ubuntu-24.04-arm arch: arm64 - build-acl-docker-amd64: - needs: [ push-buildenv-manifest ] - if: | - always() && - (contains(needs.push-buildenv-manifest.result, 'success') || contains(needs.push-buildenv-manifest.result, 'skipped')) - uses: Algebraic-Programming/HiCR/.github/workflows/master-build-acl-workflow.yml@master - with: - os: ubuntu-24.04 - arch: amd64 + # disabled because of broken docker image creation + + # build-acl-docker-amd64: + # needs: [ push-buildenv-manifest ] + # if: | + # always() && + # (contains(needs.push-buildenv-manifest.result, 'success') || contains(needs.push-buildenv-manifest.result, 'skipped')) + # uses: Algebraic-Programming/HiCR/.github/workflows/master-build-acl-workflow.yml@master + # with: + # os: ubuntu-24.04 + # arch: amd64 - push-buildenv-acl-manifest: - runs-on: ubuntu-latest - needs: [ build-acl-docker-amd64, build-acl-docker-arm64 ] - if: | - always() && - contains(needs.build-acl-docker-amd64.result, 'success') && - contains(needs.build-acl-docker-arm64.result, 'success') - steps: - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + # push-buildenv-acl-manifest: + # runs-on: ubuntu-latest + # needs: [ build-acl-docker-amd64, build-acl-docker-arm64 ] + # if: | + # always() && + # contains(needs.build-acl-docker-amd64.result, 'success') && + # contains(needs.build-acl-docker-arm64.result, 'success') + # steps: + # - name: Set up Docker Buildx + # uses: docker/setup-buildx-action@v3 - - name: Log in to the Container registry - uses: docker/login-action@v3 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} + # - name: Log in to the Container registry + # uses: docker/login-action@v3 + # with: + # registry: ${{ env.REGISTRY }} + # username: ${{ github.repository_owner }} + # password: ${{ secrets.GITHUB_TOKEN }} - - name: Create and push manifest images - run: - docker buildx imagetools create --tag ${{ env.DOCKERIMAGE }}-acl:latest ${{ env.DOCKERIMAGE }}-acl:amd64-latest ${{ env.DOCKERIMAGE }}-acl:arm64-latest + # - name: Create and push manifest images + # run: + # docker buildx imagetools create --tag ${{ env.DOCKERIMAGE }}-acl:latest ${{ env.DOCKERIMAGE }}-acl:amd64-latest ${{ env.DOCKERIMAGE }}-acl:arm64-latest diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 4c3c88a2..5d67dd1d 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -20,10 +20,52 @@ jobs: run: sudo apt update - name: Installing clang-tormat run: | - python3 -m pip install 'clang-format==18.1.0' + python3 -m pip install --upgrade pip + python3 -m pip install 'clang-format==18.1.0' + python3 -m pip install sphinx + python3 -m pip install sphinx_rtd_theme + python3 -m pip install sphinxcontrib-bibtex + python3 -m pip install doxysphinx + python3 -m pip install sphinxcontrib.needs + python3 -m pip install sphinxcontrib.plantuml + python3 -m pip install autoapi + python3 -m pip install sphinx-autoapi + python3 -m pip install myst_parser + python3 -m pip install sphinx_copybutton + python3 -m pip install sphinxcontrib.doxylink + python3 -m pip install sphinx_design + python3 -m pip install Sphinx-Substitution-Extensions + python3 -m pip install sphinx_toolbox + python3 -m pip install sphinx-theme + python3 -m pip install sphinx-book-theme + sudo apt update --fix-missing + sudo apt install -y build-essential \ + doxygen \ + graphviz \ + fonts-freefont-ttf \ + texlive \ + texlive-latex-extra \ + texlive-fonts-extra \ + libffi-dev \ + ghostscript \ + texlive-extra-utils \ + texlive-font-utils + + python3 -m pip install 'sphinx==7.2.6' + sphinx-build --version - name: Checking style run: | echo "Checking HiCR source and test formatting..." .build-tools/style/check-style.sh check include .build-tools/style/check-style.sh check tests .build-tools/style/check-style.sh check examples + - name: Build documentation + run: | + echo "Building code documentation..." + make -j1 -C docs + mkdir public + cp -r docs/build/html/* public + - uses: actions/upload-artifact@v4 + with: + name: docs + path: docs/build/html diff --git a/examples/channels/fixedSize/mpsc/locking/include/consumer.hpp b/examples/channels/fixedSize/mpsc/locking/include/consumer.hpp index ecd62f79..7efb5680 100644 --- a/examples/channels/fixedSize/mpsc/locking/include/consumer.hpp +++ b/examples/channels/fixedSize/mpsc/locking/include/consumer.hpp @@ -22,9 +22,12 @@ #include #include "common.hpp" -void consumerFc(HiCR::MemoryManager &memoryManager, - HiCR::CommunicationManager &communicationManager, - std::shared_ptr bufferMemorySpace, +void consumerFc(HiCR::MemoryManager &coordinationMemoryManager, + HiCR::MemoryManager &payloadMemoryManager, + HiCR::CommunicationManager &coordinationCommunicationManager, + HiCR::CommunicationManager &payloadCommunicationManager, + std::shared_ptr coordinationMemorySpace, + std::shared_ptr payloadMemorySpace, const size_t channelCapacity, const size_t producerCount) { @@ -32,31 +35,32 @@ void consumerFc(HiCR::MemoryManager &memoryManager, auto tokenBufferSize = HiCR::channel::fixedSize::Base::getTokenBufferSize(sizeof(ELEMENT_TYPE), channelCapacity); // Registering token buffer as a local memory slot - auto tokenBufferSlot = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, tokenBufferSize); + auto tokenBufferSlot = payloadMemoryManager.allocateLocalMemorySlot(payloadMemorySpace, tokenBufferSize); // Getting required buffer size auto coordinationBufferSize = HiCR::channel::fixedSize::Base::getCoordinationBufferSize(); // Registering token buffer as a local memory slot - auto coordinationBuffer = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBuffer = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); // Initializing coordination buffer (sets to zero the counters) HiCR::channel::fixedSize::Base::initializeCoordinationBuffer(coordinationBuffer); // Exchanging local memory slots to become global for them to be used by the remote end - communicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, /* global tag */ - {{TOKEN_BUFFER_KEY, tokenBufferSlot}, /* key-slot pairs */ - {CONSUMER_COORDINATION_BUFFER_KEY, coordinationBuffer}}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {{CONSUMER_COORDINATION_BUFFER_KEY, coordinationBuffer}}); + payloadCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {{TOKEN_BUFFER_KEY, tokenBufferSlot}}); // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Obtaining the globally exchanged memory slots - auto globalTokenBufferSlot = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, TOKEN_BUFFER_KEY); - auto consumerCoordinationBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); + auto consumerCoordinationBuffer = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); + auto globalTokenBufferSlot = payloadCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, TOKEN_BUFFER_KEY); // Creating producer and consumer channels - auto consumer = HiCR::channel::fixedSize::MPSC::locking::Consumer(communicationManager, + auto consumer = HiCR::channel::fixedSize::MPSC::locking::Consumer(coordinationCommunicationManager, + payloadCommunicationManager, globalTokenBufferSlot, /* tokenBuffer */ coordinationBuffer, /* internalCoordinationBuffer */ consumerCoordinationBuffer, @@ -90,18 +94,20 @@ void consumerFc(HiCR::MemoryManager &memoryManager, } // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // De-registering global slots - communicationManager.deregisterGlobalMemorySlot(globalTokenBufferSlot); - communicationManager.deregisterGlobalMemorySlot(consumerCoordinationBuffer); + payloadCommunicationManager.deregisterGlobalMemorySlot(globalTokenBufferSlot); + coordinationCommunicationManager.deregisterGlobalMemorySlot(consumerCoordinationBuffer); - communicationManager.destroyGlobalMemorySlot(globalTokenBufferSlot); - communicationManager.destroyGlobalMemorySlot(consumerCoordinationBuffer); + payloadCommunicationManager.destroyGlobalMemorySlot(globalTokenBufferSlot); + coordinationCommunicationManager.destroyGlobalMemorySlot(consumerCoordinationBuffer); - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Freeing up local memory - memoryManager.freeLocalMemorySlot(tokenBufferSlot); - memoryManager.freeLocalMemorySlot(coordinationBuffer); + payloadMemoryManager.freeLocalMemorySlot(tokenBufferSlot); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBuffer); } diff --git a/examples/channels/fixedSize/mpsc/locking/include/producer.hpp b/examples/channels/fixedSize/mpsc/locking/include/producer.hpp index 26a39681..ef9b1756 100644 --- a/examples/channels/fixedSize/mpsc/locking/include/producer.hpp +++ b/examples/channels/fixedSize/mpsc/locking/include/producer.hpp @@ -22,9 +22,12 @@ #include #include "common.hpp" -void producerFc(HiCR::MemoryManager &memoryManager, - HiCR::CommunicationManager &communicationManager, - std::shared_ptr bufferMemorySpace, +void producerFc(HiCR::MemoryManager &coordinationMemoryManager, + HiCR::MemoryManager &payloadMemoryManager, + HiCR::CommunicationManager &coordinationCommunicationManager, + HiCR::CommunicationManager &payloadCommunicationManager, + std::shared_ptr coordinationMemorySpace, + std::shared_ptr payloadMemorySpace, const size_t channelCapacity, const size_t producerId) { @@ -32,23 +35,26 @@ void producerFc(HiCR::MemoryManager &memoryManager, auto coordinationBufferSize = HiCR::channel::fixedSize::Base::getCoordinationBufferSize(); // Registering token buffer as a local memory slot - auto coordinationBuffer = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBuffer = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); // Initializing coordination buffer (sets to zero the counters) HiCR::channel::fixedSize::Base::initializeCoordinationBuffer(coordinationBuffer); // Exchanging local memory slots to become global for them to be used by the remote end - communicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {}); + payloadCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {}); // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Obtaining the globally exchanged memory slots - auto globalTokenBufferSlot = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, TOKEN_BUFFER_KEY); - auto consumerCoordinationBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); + auto globalTokenBufferSlot = payloadCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, TOKEN_BUFFER_KEY); + auto consumerCoordinationBuffer = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); // Creating producer and consumer channels - auto producer = HiCR::channel::fixedSize::MPSC::locking::Producer(communicationManager, + auto producer = HiCR::channel::fixedSize::MPSC::locking::Producer(coordinationCommunicationManager, + payloadCommunicationManager, globalTokenBufferSlot, /* tokenBuffer */ coordinationBuffer, /* internalCoordinationBuffer */ consumerCoordinationBuffer, @@ -58,7 +64,7 @@ void producerFc(HiCR::MemoryManager &memoryManager, // Allocating a send slot to put the values we want to communicate ELEMENT_TYPE sendBuffer = 0; auto sendBufferPtr = &sendBuffer; - auto sendSlot = memoryManager.registerLocalMemorySlot(bufferMemorySpace, sendBufferPtr, sizeof(ELEMENT_TYPE)); + auto sendSlot = payloadMemoryManager.registerLocalMemorySlot(payloadMemorySpace, sendBufferPtr, sizeof(ELEMENT_TYPE)); // Pushing values to the channel, one by one, suspending when/if the channel is full for (size_t i = 0; i < MESSAGES_PER_PRODUCER; i++) @@ -74,15 +80,17 @@ void producerFc(HiCR::MemoryManager &memoryManager, } // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // De-registering global slots - communicationManager.deregisterGlobalMemorySlot(globalTokenBufferSlot); - communicationManager.deregisterGlobalMemorySlot(consumerCoordinationBuffer); + payloadCommunicationManager.deregisterGlobalMemorySlot(globalTokenBufferSlot); + coordinationCommunicationManager.deregisterGlobalMemorySlot(consumerCoordinationBuffer); // Destroying global slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Freeing up local memory - memoryManager.freeLocalMemorySlot(coordinationBuffer); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBuffer); } diff --git a/examples/channels/fixedSize/mpsc/locking/meson.build b/examples/channels/fixedSize/mpsc/locking/meson.build index 5500f02c..9b6d51fe 100644 --- a/examples/channels/fixedSize/mpsc/locking/meson.build +++ b/examples/channels/fixedSize/mpsc/locking/meson.build @@ -1,21 +1,6 @@ -testSuite = [ 'examples', 'channels', 'fixedSize', 'mpsc', 'locking'] -test_timeout = 60 - exampleBuildIncludes = include_directories([ 'include' ]) - -if 'mpi' in enabledBackends and 'hwloc' in enabledBackends - mpi = executable('mpi', [ 'source/mpi.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) - - if get_option('buildTests') - test('mpi', mpirunExecutable, args : [ '-n', '4', '--oversubscribe', '--host', 'localhost:16', mpi.full_path(), '3' ], timeout: test_timeout, suite: testSuite ) - endif -endif -if 'lpf' in enabledBackends and 'hwloc' in enabledBackends - lpf = executable('lpf', [ 'source/lpf.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) - if get_option('buildTests') - test('lpf', lpfrunExecutable, args : [ '-np', '4', '-engine', 'zero', lpf.full_path(), '3' ], timeout: test_timeout, suite: testSuite ) - endif -endif +subdir('source/distributed') +subdir('source/local') \ No newline at end of file diff --git a/examples/channels/fixedSize/mpsc/locking/source/lpf.cpp b/examples/channels/fixedSize/mpsc/locking/source/distributed/lpf.cpp similarity index 93% rename from examples/channels/fixedSize/mpsc/locking/source/lpf.cpp rename to examples/channels/fixedSize/mpsc/locking/source/distributed/lpf.cpp index 507b2087..d1c6895e 100644 --- a/examples/channels/fixedSize/mpsc/locking/source/lpf.cpp +++ b/examples/channels/fixedSize/mpsc/locking/source/distributed/lpf.cpp @@ -20,8 +20,8 @@ #include #include #include -#include "include/consumer.hpp" -#include "include/producer.hpp" +#include "../include/consumer.hpp" +#include "../include/producer.hpp" // flag needed when using MPI to launch const int LPF_MPI_AUTO_INITIALIZE = 0; @@ -81,8 +81,8 @@ void spmd(lpf_t lpf, lpf_pid_t pid, lpf_pid_t nprocs, lpf_args_t args) size_t rankId = pid; // Rank 0 is consumer, the rest are producers - if (rankId == 0) consumerFc(m, c, firstMemorySpace, channelCapacity, producerCount); - if (rankId >= 1) producerFc(m, c, firstMemorySpace, channelCapacity, rankId); + if (rankId == 0) consumerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, producerCount); + if (rankId >= 1) producerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, rankId); } int main(int argc, char **argv) diff --git a/examples/channels/fixedSize/mpsc/locking/source/distributed/meson.build b/examples/channels/fixedSize/mpsc/locking/source/distributed/meson.build new file mode 100644 index 00000000..05f1c879 --- /dev/null +++ b/examples/channels/fixedSize/mpsc/locking/source/distributed/meson.build @@ -0,0 +1,45 @@ +testSuite = ['examples', 'channels', 'fixedSize', 'mpsc', 'locking', 'distributed'] +test_timeout = 60 + +if 'mpi' in enabledBackends and 'hwloc' in enabledBackends + mpi = executable( + 'mpi', + ['mpi.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test( + 'mpi', + mpirunExecutable, + args: [ + '-n', '4', + '--oversubscribe', + '--host', 'localhost:16', + mpi.full_path(), + '3', + ], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif + +if 'lpf' in enabledBackends and 'hwloc' in enabledBackends + lpf = executable( + 'lpf', + ['lpf.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + if get_option('buildTests') + test( + 'lpf', + lpfrunExecutable, + args: ['-np', '4', '-engine', 'zero', lpf.full_path(), '3'], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif \ No newline at end of file diff --git a/examples/channels/fixedSize/mpsc/locking/source/mpi.cpp b/examples/channels/fixedSize/mpsc/locking/source/distributed/mpi.cpp similarity index 89% rename from examples/channels/fixedSize/mpsc/locking/source/mpi.cpp rename to examples/channels/fixedSize/mpsc/locking/source/distributed/mpi.cpp index e7ab0081..04191f46 100644 --- a/examples/channels/fixedSize/mpsc/locking/source/mpi.cpp +++ b/examples/channels/fixedSize/mpsc/locking/source/distributed/mpi.cpp @@ -18,8 +18,8 @@ #include #include #include -#include "include/consumer.hpp" -#include "include/producer.hpp" +#include "../include/consumer.hpp" +#include "../include/producer.hpp" int main(int argc, char **argv) { @@ -85,8 +85,8 @@ int main(int argc, char **argv) size_t producerCount = rankCount - 1; // Rank 0 is consumer, the rest are producers - if (rankId == 0) consumerFc(m, c, firstMemorySpace, channelCapacity, producerCount); - if (rankId >= 1) producerFc(m, c, firstMemorySpace, channelCapacity, rankId); + if (rankId == 0) consumerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, producerCount); + if (rankId >= 1) producerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, rankId); // Finalizing MPI MPI_Finalize(); diff --git a/examples/channels/fixedSize/mpsc/locking/source/local/meson.build b/examples/channels/fixedSize/mpsc/locking/source/local/meson.build new file mode 100644 index 00000000..ddc0d0d7 --- /dev/null +++ b/examples/channels/fixedSize/mpsc/locking/source/local/meson.build @@ -0,0 +1,21 @@ +testSuite = ['examples', 'channels', 'fixedSize', 'mpsc', 'locking', 'local'] +test_timeout = 60 + +if 'pthreads' in enabledBackends and 'hwloc' in enabledBackends + pthreads = executable( + 'pthreads', + ['pthreads.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test( + 'pthreads', + pthreads, + args: ['3', '4'], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif \ No newline at end of file diff --git a/examples/channels/fixedSize/mpsc/locking/source/local/pthreads.cpp b/examples/channels/fixedSize/mpsc/locking/source/local/pthreads.cpp new file mode 100644 index 00000000..f3d4fd73 --- /dev/null +++ b/examples/channels/fixedSize/mpsc/locking/source/local/pthreads.cpp @@ -0,0 +1,131 @@ +/* + * Copyright 2025 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include + +#include "../include/consumer.hpp" +#include "../include/producer.hpp" + +int main(int argc, char **argv) +{ + // Checking arguments + if (argc != 3) + { + fprintf(stderr, "Error: Must provide the channel capacity and the thread pool size as argument.\n"); + return -1; + } + + // Reading argument + auto channelCapacity = std::atoi(argv[1]); + + // Capacity must be larger than zero + if (channelCapacity == 0) + { + fprintf(stderr, "Error: Cannot create channel with zero capacity.\n"); + return -1; + } + + // Reading argument + size_t threadPoolSize = std::atoi(argv[2]); + + // Capacity must be larger than zero + if (threadPoolSize == 0) + { + fprintf(stderr, "Error: Cannot create a thread pool with zero capacity.\n"); + return -1; + } + + // Creating HWloc topology object + hwloc_topology_t topology; + + // Reserving memory for hwloc + hwloc_topology_init(&topology); + + // Initializing host (CPU) topology manager + HiCR::backend::hwloc::TopologyManager dm(&topology); + + // Instantiating backend + HiCR::backend::hwloc::MemoryManager m(&topology); + + // Create shared memory + auto sharedMemoryFactory = HiCR::backend::pthreads::SharedMemoryFactory(); + auto &coordinationSharedMemory = sharedMemoryFactory.get(0, threadPoolSize); + auto &payloadSharedMemory = sharedMemoryFactory.get(1, threadPoolSize); + + // Create communication managers + std::vector coordinationCommunicationManagers; + std::vector payloadCommunicationManagers; + for (size_t i = 0; i < threadPoolSize; ++i) + { + coordinationCommunicationManagers.emplace_back(coordinationSharedMemory); + payloadCommunicationManagers.emplace_back(payloadSharedMemory); + } + + // Asking backend to check the available devices + const auto t = dm.queryTopology(); + + // Getting first device found + auto d = *t.getDevices().begin(); + + // Obtaining memory spaces + auto memSpaces = d->getMemorySpaceList(); + + // Getting a reference to the first memory space + auto firstMemorySpace = *memSpaces.begin(); + + // Create thread pool + std::vector threadPool; + for (size_t threadId = 0; threadId < threadPoolSize; ++threadId) + { + // The first thread is a consumer + if (threadId == 0) + { + threadPool.emplace_back(consumerFc, + std::ref(m), + std::ref(m), + std::ref(coordinationCommunicationManagers[threadId]), + std::ref(payloadCommunicationManagers[threadId]), + firstMemorySpace, + firstMemorySpace, + channelCapacity, + threadPoolSize - 1); + } + // All the others are producers + else + { + threadPool.emplace_back(producerFc, + std::ref(m), + std::ref(m), + std::ref(coordinationCommunicationManagers[threadId]), + std::ref(payloadCommunicationManagers[threadId]), + firstMemorySpace, + firstMemorySpace, + channelCapacity, + threadId - 1); + } + } + + // Wait for the execution to terminate + for (auto &thread : threadPool) { thread.join(); } + + return 0; +} diff --git a/examples/channels/fixedSize/mpsc/nonlocking/include/consumer.hpp b/examples/channels/fixedSize/mpsc/nonlocking/include/consumer.hpp index 1a8c56e7..e4788fd8 100644 --- a/examples/channels/fixedSize/mpsc/nonlocking/include/consumer.hpp +++ b/examples/channels/fixedSize/mpsc/nonlocking/include/consumer.hpp @@ -23,9 +23,12 @@ #include #include "common.hpp" -void consumerFc(HiCR::MemoryManager &memoryManager, - HiCR::CommunicationManager &communicationManager, - std::shared_ptr bufferMemorySpace, +void consumerFc(HiCR::MemoryManager &coordinationMemoryManager, + HiCR::MemoryManager &payloadMemoryManager, + HiCR::CommunicationManager &coordinationCommunicationManager, + HiCR::CommunicationManager &payloadCommunicationManager, + std::shared_ptr coordinationMemorySpace, + std::shared_ptr payloadMemorySpace, const size_t channelCapacity, const size_t producerCount) { @@ -41,37 +44,37 @@ void consumerFc(HiCR::MemoryManager &memoryManager, for (size_t i = 0; i < producerCount; i++) { // consumer needs to allocate #producers token buffers for #producers SPSCs - auto tokenBufferSlot = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, tokenBufferSize); + auto tokenBufferSlot = payloadMemoryManager.allocateLocalMemorySlot(payloadMemorySpace, tokenBufferSize); tokenBuffers.push_back(std::make_pair(i, tokenBufferSlot)); // consumer needs to allocate #producers consumer side coordination buffers for #producers SPSCs auto coordinationBufferSize = HiCR::channel::fixedSize::Base::getCoordinationBufferSize(); - auto coordinationBuffer = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBuffer = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); HiCR::channel::fixedSize::Base::initializeCoordinationBuffer(coordinationBuffer); localCoordinationBuffers.push_back(coordinationBuffer); consumerCoordinationBuffers.push_back(std::make_pair(i, coordinationBuffer)); } // communicate to producers the token buffer references - communicationManager.exchangeGlobalMemorySlots(TOKEN_TAG, tokenBuffers); - communicationManager.fence(TOKEN_TAG); + payloadCommunicationManager.exchangeGlobalMemorySlots(TOKEN_TAG, tokenBuffers); + payloadCommunicationManager.fence(TOKEN_TAG); // get from producers their coordination buffer references - communicationManager.exchangeGlobalMemorySlots(PRODUCER_COORDINATION_TAG, {}); - communicationManager.fence(PRODUCER_COORDINATION_TAG); + coordinationCommunicationManager.exchangeGlobalMemorySlots(PRODUCER_COORDINATION_TAG, {}); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_TAG); // communicate to producers the consumer buffers - communicationManager.exchangeGlobalMemorySlots(CONSUMER_COORDINATION_TAG, consumerCoordinationBuffers); - communicationManager.fence(CONSUMER_COORDINATION_TAG); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CONSUMER_COORDINATION_TAG, consumerCoordinationBuffers); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_TAG); for (size_t i = 0; i < producerCount; i++) { - auto globalTokenBufferSlot = communicationManager.getGlobalMemorySlot(TOKEN_TAG, i); + auto globalTokenBufferSlot = payloadCommunicationManager.getGlobalMemorySlot(TOKEN_TAG, i); globalTokenBuffers.push_back(globalTokenBufferSlot); - auto producerCoordinationBuffer = communicationManager.getGlobalMemorySlot(PRODUCER_COORDINATION_TAG, i); + auto producerCoordinationBuffer = coordinationCommunicationManager.getGlobalMemorySlot(PRODUCER_COORDINATION_TAG, i); producerCoordinationBuffers.push_back(producerCoordinationBuffer); } // Creating producer and consumer channels auto consumer = HiCR::channel::fixedSize::MPSC::nonlocking::Consumer( - communicationManager, globalTokenBuffers, localCoordinationBuffers, producerCoordinationBuffers, sizeof(ELEMENT_TYPE), channelCapacity); + coordinationCommunicationManager, payloadCommunicationManager, globalTokenBuffers, localCoordinationBuffers, producerCoordinationBuffers, sizeof(ELEMENT_TYPE), channelCapacity); // Calculating the expected message count size_t expectedMessageCount = MESSAGES_PER_PRODUCER * producerCount; @@ -102,21 +105,21 @@ void consumerFc(HiCR::MemoryManager &memoryManager, consumer.pop(); } - communicationManager.fence(TOKEN_TAG); - communicationManager.fence(PRODUCER_COORDINATION_TAG); - communicationManager.fence(CONSUMER_COORDINATION_TAG); + payloadCommunicationManager.fence(TOKEN_TAG); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_TAG); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_TAG); for (size_t i = 0; i < producerCount; i++) { - communicationManager.deregisterGlobalMemorySlot(globalTokenBuffers[i]); - communicationManager.destroyGlobalMemorySlot(globalTokenBuffers[i]); - memoryManager.freeLocalMemorySlot(globalTokenBuffers[i]->getSourceLocalMemorySlot()); - communicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffers[i]); - communicationManager.destroyGlobalMemorySlot(producerCoordinationBuffers[i]); - memoryManager.freeLocalMemorySlot(localCoordinationBuffers[i]); + payloadCommunicationManager.deregisterGlobalMemorySlot(globalTokenBuffers[i]); + payloadMemoryManager.freeLocalMemorySlot(globalTokenBuffers[i]->getSourceLocalMemorySlot()); + coordinationCommunicationManager.destroyGlobalMemorySlot(globalTokenBuffers[i]); + coordinationCommunicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffers[i]); + coordinationCommunicationManager.destroyGlobalMemorySlot(producerCoordinationBuffers[i]); + coordinationMemoryManager.freeLocalMemorySlot(localCoordinationBuffers[i]); } - communicationManager.fence(TOKEN_TAG); - communicationManager.fence(PRODUCER_COORDINATION_TAG); - communicationManager.fence(CONSUMER_COORDINATION_TAG); + payloadCommunicationManager.fence(TOKEN_TAG); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_TAG); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_TAG); } diff --git a/examples/channels/fixedSize/mpsc/nonlocking/include/producer.hpp b/examples/channels/fixedSize/mpsc/nonlocking/include/producer.hpp index c527c62f..54682f90 100644 --- a/examples/channels/fixedSize/mpsc/nonlocking/include/producer.hpp +++ b/examples/channels/fixedSize/mpsc/nonlocking/include/producer.hpp @@ -23,47 +23,55 @@ #include "common.hpp" #include -void producerFc(HiCR::MemoryManager &memoryManager, - HiCR::CommunicationManager &communicationManager, - std::shared_ptr bufferMemorySpace, +void producerFc(HiCR::MemoryManager &coordinationMemoryManager, + HiCR::MemoryManager &payloadMemoryManager, + HiCR::CommunicationManager &coordinationCommunicationManager, + HiCR::CommunicationManager &payloadCommunicationManager, + std::shared_ptr coordinationMemorySpace, + std::shared_ptr payloadMemorySpace, const size_t channelCapacity, const size_t producerId, const size_t producerCount) { // initialize producer coordination buffer auto coordinationBufferSize = HiCR::channel::fixedSize::Base::getCoordinationBufferSize(); - auto coordinationBuffer = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBuffer = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); HiCR::channel::fixedSize::Base::initializeCoordinationBuffer(coordinationBuffer); // these vectors hold all producer buffers and are only used for later deregistration std::vector> globalTokenBuffers; // get from consumer all the token buffer information - communicationManager.exchangeGlobalMemorySlots(TOKEN_TAG, {}); - communicationManager.fence(TOKEN_TAG); + payloadCommunicationManager.exchangeGlobalMemorySlots(TOKEN_TAG, {}); + payloadCommunicationManager.fence(TOKEN_TAG); // communicate to consumer all producer coordination buffers - communicationManager.exchangeGlobalMemorySlots(PRODUCER_COORDINATION_TAG, {{producerId, coordinationBuffer}}); - communicationManager.fence(PRODUCER_COORDINATION_TAG); + coordinationCommunicationManager.exchangeGlobalMemorySlots(PRODUCER_COORDINATION_TAG, {{producerId, coordinationBuffer}}); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_TAG); - communicationManager.exchangeGlobalMemorySlots(CONSUMER_COORDINATION_TAG, {}); - communicationManager.fence(CONSUMER_COORDINATION_TAG); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CONSUMER_COORDINATION_TAG, {}); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_TAG); for (size_t i = 0; i < producerCount; i++) { - auto globalTokenBufferSlot = communicationManager.getGlobalMemorySlot(TOKEN_TAG, i); + auto globalTokenBufferSlot = payloadCommunicationManager.getGlobalMemorySlot(TOKEN_TAG, i); globalTokenBuffers.push_back(globalTokenBufferSlot); } - auto consumerCoordinationBuffer = communicationManager.getGlobalMemorySlot(CONSUMER_COORDINATION_TAG, producerId); + auto consumerCoordinationBuffer = coordinationCommunicationManager.getGlobalMemorySlot(CONSUMER_COORDINATION_TAG, producerId); // Creating producer channel // This call does the same as the SPSC Producer constructor, as // the producer of MPSC::nonlocking has the same view - auto producer = HiCR::channel::fixedSize::MPSC::nonlocking::Producer( - communicationManager, globalTokenBuffers[producerId], coordinationBuffer, consumerCoordinationBuffer, sizeof(ELEMENT_TYPE), channelCapacity); + auto producer = HiCR::channel::fixedSize::MPSC::nonlocking::Producer(coordinationCommunicationManager, + payloadCommunicationManager, + globalTokenBuffers[producerId], + coordinationBuffer, + consumerCoordinationBuffer, + sizeof(ELEMENT_TYPE), + channelCapacity); // Allocating a send slot to put the values we want to communicate ELEMENT_TYPE sendBuffer = 0; auto sendBufferPtr = &sendBuffer; - auto sendSlot = memoryManager.registerLocalMemorySlot(bufferMemorySpace, sendBufferPtr, sizeof(ELEMENT_TYPE)); + auto sendSlot = payloadMemoryManager.registerLocalMemorySlot(payloadMemorySpace, sendBufferPtr, sizeof(ELEMENT_TYPE)); // Pushing values to the channel, one by one, suspending when/if the channel is full for (size_t i = 0; i < MESSAGES_PER_PRODUCER; i++) @@ -80,14 +88,14 @@ void producerFc(HiCR::MemoryManager &memoryManager, printf("[Producer %03lu] Sent Value: %u\n", producerId, *sendBufferPtr); } - communicationManager.fence(TOKEN_TAG); - communicationManager.fence(PRODUCER_COORDINATION_TAG); - communicationManager.fence(CONSUMER_COORDINATION_TAG); + payloadCommunicationManager.fence(TOKEN_TAG); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_TAG); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_TAG); // Clean-up on the consumer side, fence for slot destruction - communicationManager.fence(TOKEN_TAG); - communicationManager.fence(PRODUCER_COORDINATION_TAG); - communicationManager.fence(CONSUMER_COORDINATION_TAG); + payloadCommunicationManager.fence(TOKEN_TAG); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_TAG); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_TAG); - memoryManager.freeLocalMemorySlot(coordinationBuffer); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBuffer); } diff --git a/examples/channels/fixedSize/mpsc/nonlocking/meson.build b/examples/channels/fixedSize/mpsc/nonlocking/meson.build index 0eae9768..291668fb 100644 --- a/examples/channels/fixedSize/mpsc/nonlocking/meson.build +++ b/examples/channels/fixedSize/mpsc/nonlocking/meson.build @@ -1,19 +1,6 @@ -testSuite = [ 'examples', 'channels', 'fixedSize', 'mpsc', 'nonlocking'] -test_timeout = 60 - exampleBuildIncludes = include_directories([ 'include' ]) -if 'mpi' in enabledBackends and 'hwloc' in enabledBackends - mpi = executable('mpi', [ 'source/mpi.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) - - if get_option('buildTests') - test('mpi', mpirunExecutable, args : [ '-n', '4', '--oversubscribe', '--host', 'localhost:16', mpi.full_path(), '3' ], timeout: test_timeout, suite: testSuite ) - endif -endif - -if 'lpf' in enabledBackends and 'hwloc' in enabledBackends - lpf = executable('lpf', [ 'source/lpf.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) - test('lpf', lpfrunExecutable, args : [ '-np', '4', '-engine', 'zero', lpf.full_path(), '3' ], timeout: test_timeout, suite: testSuite ) -endif +subdir('source/distributed') +subdir('source/local') \ No newline at end of file diff --git a/examples/channels/fixedSize/mpsc/nonlocking/source/lpf.cpp b/examples/channels/fixedSize/mpsc/nonlocking/source/distributed/lpf.cpp similarity index 92% rename from examples/channels/fixedSize/mpsc/nonlocking/source/lpf.cpp rename to examples/channels/fixedSize/mpsc/nonlocking/source/distributed/lpf.cpp index ee4c2fd8..559ba95f 100644 --- a/examples/channels/fixedSize/mpsc/nonlocking/source/lpf.cpp +++ b/examples/channels/fixedSize/mpsc/nonlocking/source/distributed/lpf.cpp @@ -20,8 +20,8 @@ #include #include #include -#include "include/consumer.hpp" -#include "include/producer.hpp" +#include "../include/consumer.hpp" +#include "../include/producer.hpp" // flag needed when using MPI to launch const int LPF_MPI_AUTO_INITIALIZE = 0; @@ -81,8 +81,8 @@ void spmd(lpf_t lpf, lpf_pid_t pid, lpf_pid_t nprocs, lpf_args_t args) size_t rankId = pid; // Rank 0 is consumer, the rest are producers - if (rankId == 0) consumerFc(m, c, firstMemorySpace, channelCapacity, producerCount); - if (rankId >= 1) producerFc(m, c, firstMemorySpace, channelCapacity, rankId - 1, producerCount); + if (rankId == 0) consumerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, producerCount); + if (rankId >= 1) producerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, rankId - 1, producerCount); } int main(int argc, char **argv) diff --git a/examples/channels/fixedSize/mpsc/nonlocking/source/distributed/meson.build b/examples/channels/fixedSize/mpsc/nonlocking/source/distributed/meson.build new file mode 100644 index 00000000..4a83cee1 --- /dev/null +++ b/examples/channels/fixedSize/mpsc/nonlocking/source/distributed/meson.build @@ -0,0 +1,15 @@ +testSuite = [ 'examples', 'channels', 'fixedSize', 'mpsc', 'nonlocking', 'distributed'] +test_timeout = 60 + +if 'mpi' in enabledBackends and 'hwloc' in enabledBackends + mpi = executable('mpi', [ 'mpi.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) + + if get_option('buildTests') + test('mpi', mpirunExecutable, args : [ '-n', '4', '--oversubscribe', '--host', 'localhost:16', mpi.full_path(), '3' ], timeout: test_timeout, suite: testSuite ) + endif +endif + +if 'lpf' in enabledBackends and 'hwloc' in enabledBackends + lpf = executable('lpf', [ 'lpf.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) + test('lpf', lpfrunExecutable, args : [ '-np', '4', '-engine', 'zero', lpf.full_path(), '3' ], timeout: test_timeout, suite: testSuite ) +endif diff --git a/examples/channels/fixedSize/mpsc/nonlocking/source/mpi.cpp b/examples/channels/fixedSize/mpsc/nonlocking/source/distributed/mpi.cpp similarity index 89% rename from examples/channels/fixedSize/mpsc/nonlocking/source/mpi.cpp rename to examples/channels/fixedSize/mpsc/nonlocking/source/distributed/mpi.cpp index 975b0cb1..44ac399f 100644 --- a/examples/channels/fixedSize/mpsc/nonlocking/source/mpi.cpp +++ b/examples/channels/fixedSize/mpsc/nonlocking/source/distributed/mpi.cpp @@ -18,8 +18,8 @@ #include #include #include -#include "include/consumer.hpp" -#include "include/producer.hpp" +#include "../include/consumer.hpp" +#include "../include/producer.hpp" int main(int argc, char **argv) { @@ -85,8 +85,8 @@ int main(int argc, char **argv) size_t producerCount = rankCount - 1; // Rank 0 is consumer, the rest are producers - if (rankId == 0) consumerFc(m, c, firstMemorySpace, channelCapacity, producerCount); - if (rankId >= 1) producerFc(m, c, firstMemorySpace, channelCapacity, rankId - 1, producerCount); + if (rankId == 0) consumerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, producerCount); + if (rankId >= 1) producerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, rankId - 1, producerCount); // Finalizing MPI MPI_Finalize(); diff --git a/examples/channels/fixedSize/mpsc/nonlocking/source/local/meson.build b/examples/channels/fixedSize/mpsc/nonlocking/source/local/meson.build new file mode 100644 index 00000000..c72379eb --- /dev/null +++ b/examples/channels/fixedSize/mpsc/nonlocking/source/local/meson.build @@ -0,0 +1,21 @@ +testSuite = ['examples', 'channels', 'fixedSize', 'mpsc', 'nonlocking', 'local'] +test_timeout = 60 + +if 'pthreads' in enabledBackends and 'hwloc' in enabledBackends + pthreads = executable( + 'pthreads', + ['pthreads.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test( + 'pthreads', + pthreads, + args: ['3', '4'], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif \ No newline at end of file diff --git a/examples/channels/fixedSize/mpsc/nonlocking/source/local/pthreads.cpp b/examples/channels/fixedSize/mpsc/nonlocking/source/local/pthreads.cpp new file mode 100644 index 00000000..f052828c --- /dev/null +++ b/examples/channels/fixedSize/mpsc/nonlocking/source/local/pthreads.cpp @@ -0,0 +1,132 @@ +/* + * Copyright 2025 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include + +#include "../include/consumer.hpp" +#include "../include/producer.hpp" + +int main(int argc, char **argv) +{ + // Checking arguments + if (argc != 3) + { + fprintf(stderr, "Error: Must provide the channel capacity and the thread pool size as argument.\n"); + return -1; + } + + // Reading argument + auto channelCapacity = std::atoi(argv[1]); + + // Capacity must be larger than zero + if (channelCapacity == 0) + { + fprintf(stderr, "Error: Cannot create channel with zero capacity.\n"); + return -1; + } + + // Reading argument + size_t threadPoolSize = std::atoi(argv[2]); + + // Capacity must be larger than zero + if (threadPoolSize == 0) + { + fprintf(stderr, "Error: Cannot create a thread pool with zero capacity.\n"); + return -1; + } + + // Creating HWloc topology object + hwloc_topology_t topology; + + // Reserving memory for hwloc + hwloc_topology_init(&topology); + + // Initializing host (CPU) topology manager + HiCR::backend::hwloc::TopologyManager dm(&topology); + + // Instantiating backend + HiCR::backend::hwloc::MemoryManager m(&topology); + + // Create shared memory + auto sharedMemoryFactory = HiCR::backend::pthreads::SharedMemoryFactory(); + auto &coordinationSharedMemory = sharedMemoryFactory.get(0, threadPoolSize); + auto &payloadSharedMemory = sharedMemoryFactory.get(1, threadPoolSize); + + // Create communication managers + std::vector coordinationCommunicationManagers; + std::vector payloadCommunicationManagers; + for (size_t i = 0; i < threadPoolSize; ++i) + { + coordinationCommunicationManagers.emplace_back(coordinationSharedMemory); + payloadCommunicationManagers.emplace_back(payloadSharedMemory); + } + + // Asking backend to check the available devices + const auto t = dm.queryTopology(); + + // Getting first device found + auto d = *t.getDevices().begin(); + + // Obtaining memory spaces + auto memSpaces = d->getMemorySpaceList(); + + // Getting a reference to the first memory space + auto firstMemorySpace = *memSpaces.begin(); + + // Create thread pool + std::vector threadPool; + for (size_t threadId = 0; threadId < threadPoolSize; ++threadId) + { + // The first thread is a consumer + if (threadId == 0) + { + threadPool.emplace_back(consumerFc, + std::ref(m), + std::ref(m), + std::ref(coordinationCommunicationManagers[threadId]), + std::ref(payloadCommunicationManagers[threadId]), + firstMemorySpace, + firstMemorySpace, + channelCapacity, + threadPoolSize - 1); + } + // All the others are producers + else + { + threadPool.emplace_back(producerFc, + std::ref(m), + std::ref(m), + std::ref(coordinationCommunicationManagers[threadId]), + std::ref(payloadCommunicationManagers[threadId]), + firstMemorySpace, + firstMemorySpace, + channelCapacity, + threadId - 1, + threadPoolSize - 1); + } + } + + // Wait for the execution to terminate + for (auto &thread : threadPool) { thread.join(); } + + return 0; +} diff --git a/examples/channels/fixedSize/spsc/include/consumer.hpp b/examples/channels/fixedSize/spsc/include/consumer.hpp index 3130fa46..a823c1d4 100644 --- a/examples/channels/fixedSize/spsc/include/consumer.hpp +++ b/examples/channels/fixedSize/spsc/include/consumer.hpp @@ -21,40 +21,45 @@ #include #include -void consumerFc(HiCR::MemoryManager &memoryManager, - HiCR::CommunicationManager &communicationManager, - std::shared_ptr bufferMemorySpace, +void consumerFc(HiCR::MemoryManager &coordinationMemoryManager, + HiCR::MemoryManager &payloadMemoryManager, + HiCR::CommunicationManager &coordinationCommunicationManager, + HiCR::CommunicationManager &payloadCommunicationManager, + std::shared_ptr coordinationMemorySpace, + std::shared_ptr payloadMemorySpace, const size_t channelCapacity) { // Getting required buffer sizes auto tokenBufferSize = HiCR::channel::fixedSize::Base::getTokenBufferSize(sizeof(ELEMENT_TYPE), channelCapacity); // Allocating token buffer as a local memory slot - auto tokenBufferSlot = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, tokenBufferSize); + auto tokenBufferSlot = payloadMemoryManager.allocateLocalMemorySlot(payloadMemorySpace, tokenBufferSize); // Getting required buffer size auto coordinationBufferSize = HiCR::channel::fixedSize::Base::getCoordinationBufferSize(); // Allocating coordination buffer as a local memory slot - auto coordinationBuffer = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBuffer = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); // Initializing coordination buffer (sets to zero the counters) HiCR::channel::fixedSize::Base::initializeCoordinationBuffer(coordinationBuffer); // Exchanging local memory slots to become global for them to be used by the remote end - communicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {{TOKEN_BUFFER_KEY, tokenBufferSlot}, {CONSUMER_COORDINATION_BUFFER_KEY, coordinationBuffer}}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {{CONSUMER_COORDINATION_BUFFER_KEY, coordinationBuffer}}); + payloadCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {{TOKEN_BUFFER_KEY, tokenBufferSlot}}); // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Obtaining the globally exchanged memory slots - auto globalTokenBufferSlot = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, TOKEN_BUFFER_KEY); - auto producerCoordinationBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, PRODUCER_COORDINATION_BUFFER_KEY); - auto consumerCoordinationBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); + auto globalTokenBufferSlot = payloadCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, TOKEN_BUFFER_KEY); + auto producerCoordinationBuffer = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, PRODUCER_COORDINATION_BUFFER_KEY); + auto consumerCoordinationBuffer = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); // Creating producer and consumer channels - auto consumer = - HiCR::channel::fixedSize::SPSC::Consumer(communicationManager, globalTokenBufferSlot, coordinationBuffer, producerCoordinationBuffer, sizeof(ELEMENT_TYPE), channelCapacity); + auto consumer = HiCR::channel::fixedSize::SPSC::Consumer( + coordinationCommunicationManager, payloadCommunicationManager, globalTokenBufferSlot, coordinationBuffer, producerCoordinationBuffer, sizeof(ELEMENT_TYPE), channelCapacity); // Getting a single value from the channel while (consumer.isEmpty()) consumer.updateDepth(); @@ -72,22 +77,24 @@ void consumerFc(HiCR::MemoryManager &memoryManager, consumer.pop(2); // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // De-registering global slots - communicationManager.deregisterGlobalMemorySlot(globalTokenBufferSlot); - communicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffer); - communicationManager.deregisterGlobalMemorySlot(consumerCoordinationBuffer); + payloadCommunicationManager.deregisterGlobalMemorySlot(globalTokenBufferSlot); + coordinationCommunicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffer); + coordinationCommunicationManager.deregisterGlobalMemorySlot(consumerCoordinationBuffer); // Destroying global slots - communicationManager.destroyGlobalMemorySlot(globalTokenBufferSlot); - communicationManager.destroyGlobalMemorySlot(producerCoordinationBuffer); - communicationManager.destroyGlobalMemorySlot(consumerCoordinationBuffer); + payloadCommunicationManager.destroyGlobalMemorySlot(globalTokenBufferSlot); + coordinationCommunicationManager.destroyGlobalMemorySlot(producerCoordinationBuffer); + coordinationCommunicationManager.destroyGlobalMemorySlot(consumerCoordinationBuffer); // Fence for the destroys to occur - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Freeing up local memory - memoryManager.freeLocalMemorySlot(tokenBufferSlot); - memoryManager.freeLocalMemorySlot(coordinationBuffer); + payloadMemoryManager.freeLocalMemorySlot(tokenBufferSlot); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBuffer); } diff --git a/examples/channels/fixedSize/spsc/include/producer.hpp b/examples/channels/fixedSize/spsc/include/producer.hpp index 6dae505d..43eb3f01 100644 --- a/examples/channels/fixedSize/spsc/include/producer.hpp +++ b/examples/channels/fixedSize/spsc/include/producer.hpp @@ -21,39 +21,44 @@ #include #include -void producerFc(HiCR::MemoryManager &memoryManager, - HiCR::CommunicationManager &communicationManager, - std::shared_ptr bufferMemorySpace, +void producerFc(HiCR::MemoryManager &coordinationMemoryManager, + HiCR::MemoryManager &payloadMemoryManager, + HiCR::CommunicationManager &coordinationCommunicationManager, + HiCR::CommunicationManager &payloadCommunicationManager, + std::shared_ptr coordinationMemorySpace, + std::shared_ptr payloadMemorySpace, const size_t channelCapacity) { // Getting required buffer size auto coordinationBufferSize = HiCR::channel::fixedSize::Base::getCoordinationBufferSize(); // Allocating token buffer as a local memory slot - auto coordinationBuffer = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBuffer = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); // Initializing coordination buffer (sets to zero the counters) HiCR::channel::fixedSize::Base::initializeCoordinationBuffer(coordinationBuffer); // Exchanging local memory slots to become global for them to be used by the remote end - communicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {{PRODUCER_COORDINATION_BUFFER_KEY, coordinationBuffer}}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {{PRODUCER_COORDINATION_BUFFER_KEY, coordinationBuffer}}); + payloadCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {}); // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Obtaining the globally exchanged memory slots - auto tokenBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, TOKEN_BUFFER_KEY); - auto producerCoordinationBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, PRODUCER_COORDINATION_BUFFER_KEY); - auto consumerCoordinationBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); + auto tokenBuffer = payloadCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, TOKEN_BUFFER_KEY); + auto producerCoordinationBuffer = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, PRODUCER_COORDINATION_BUFFER_KEY); + auto consumerCoordinationBuffer = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); // Creating producer and consumer channels - auto producer = - HiCR::channel::fixedSize::SPSC::Producer(communicationManager, tokenBuffer, coordinationBuffer, consumerCoordinationBuffer, sizeof(ELEMENT_TYPE), channelCapacity); + auto producer = HiCR::channel::fixedSize::SPSC::Producer( + coordinationCommunicationManager, payloadCommunicationManager, tokenBuffer, coordinationBuffer, consumerCoordinationBuffer, sizeof(ELEMENT_TYPE), channelCapacity); // Allocating a send slot to put the values we want to communicate ELEMENT_TYPE sendBuffer = 0; auto sendBufferPtr = &sendBuffer; - auto sendSlot = memoryManager.registerLocalMemorySlot(bufferMemorySpace, sendBufferPtr, sizeof(ELEMENT_TYPE)); + auto sendSlot = payloadMemoryManager.registerLocalMemorySlot(payloadMemorySpace, sendBufferPtr, sizeof(ELEMENT_TYPE)); // Pushing values to the channel, one by one, suspending when/if the channel is full sendBuffer = 42; @@ -77,19 +82,21 @@ void producerFc(HiCR::MemoryManager &memoryManager, printf("Sent Value: %u\n", *sendBufferPtr); // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // De-registering global slots - communicationManager.deregisterGlobalMemorySlot(tokenBuffer); - communicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffer); - communicationManager.deregisterGlobalMemorySlot(consumerCoordinationBuffer); + payloadCommunicationManager.deregisterGlobalMemorySlot(tokenBuffer); + coordinationCommunicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffer); + coordinationCommunicationManager.deregisterGlobalMemorySlot(consumerCoordinationBuffer); // Destroying global slots - communicationManager.destroyGlobalMemorySlot(consumerCoordinationBuffer); + coordinationCommunicationManager.destroyGlobalMemorySlot(consumerCoordinationBuffer); // Fence for the destroys to occur - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Freeing up local memory - memoryManager.freeLocalMemorySlot(coordinationBuffer); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBuffer); } diff --git a/examples/channels/fixedSize/spsc/meson.build b/examples/channels/fixedSize/spsc/meson.build index 36c435c2..9b6d51fe 100644 --- a/examples/channels/fixedSize/spsc/meson.build +++ b/examples/channels/fixedSize/spsc/meson.build @@ -1,22 +1,6 @@ -testSuite = [ 'examples', 'channels', 'fixedSize', 'spsc'] -test_timeout = 60 - exampleBuildIncludes = include_directories([ 'include' ]) - -if 'mpi' in enabledBackends and 'hwloc' in enabledBackends - mpi = executable('mpi', [ 'source/mpi.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) - - if get_option('buildTests') - test('mpi', mpirunExecutable, args : [ '-n', '2', '--oversubscribe', mpi.full_path(), '3' ], timeout: test_timeout, suite: testSuite ) - endif -endif - -if 'lpf' in enabledBackends and 'hwloc' in enabledBackends - lpf = executable('lpf', [ 'source/lpf.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) - if get_option('buildTests') - test('lpf', lpfrunExecutable, args : [ '-np', '2', '-engine', 'zero', lpf.full_path(), '3' ], timeout: test_timeout, suite: testSuite ) - endif -endif +subdir('source/distributed') +subdir('source/local') \ No newline at end of file diff --git a/examples/channels/fixedSize/spsc/source/lpf.cpp b/examples/channels/fixedSize/spsc/source/distributed/lpf.cpp similarity index 93% rename from examples/channels/fixedSize/spsc/source/lpf.cpp rename to examples/channels/fixedSize/spsc/source/distributed/lpf.cpp index 9f8dfc83..fbf41d70 100644 --- a/examples/channels/fixedSize/spsc/source/lpf.cpp +++ b/examples/channels/fixedSize/spsc/source/distributed/lpf.cpp @@ -20,8 +20,8 @@ #include #include #include -#include "include/consumer.hpp" -#include "include/producer.hpp" +#include "../include/consumer.hpp" +#include "../include/producer.hpp" // flag needed when using MPI to launch const int LPF_MPI_AUTO_INITIALIZE = 0; @@ -78,8 +78,8 @@ void spmd(lpf_t lpf, lpf_pid_t pid, lpf_pid_t nprocs, lpf_args_t args) auto firstMemorySpace = *memSpaces.begin(); // Rank 0 is producer, Rank 1 is consumer - if (pid == 0) producerFc(m, c, firstMemorySpace, channelCapacity); - if (pid == 1) consumerFc(m, c, firstMemorySpace, channelCapacity); + if (pid == 0) producerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity); + if (pid == 1) consumerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity); } int main(int argc, char **argv) diff --git a/examples/channels/fixedSize/spsc/source/distributed/meson.build b/examples/channels/fixedSize/spsc/source/distributed/meson.build new file mode 100644 index 00000000..062251ae --- /dev/null +++ b/examples/channels/fixedSize/spsc/source/distributed/meson.build @@ -0,0 +1,40 @@ +testSuite = ['examples', 'channels', 'fixedSize', 'spsc', 'distributed'] +test_timeout = 60 + +if 'mpi' in enabledBackends and 'hwloc' in enabledBackends + mpi = executable( + 'mpi', + ['mpi.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test( + 'mpi', + mpirunExecutable, + args: ['-n', '2', '--oversubscribe', mpi.full_path(), '3'], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif + +if 'lpf' in enabledBackends and 'hwloc' in enabledBackends + lpf = executable( + 'lpf', + ['lpf.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test( + 'lpf', + lpfrunExecutable, + args: ['-np', '2', '-engine', 'zero', lpf.full_path(), '3'], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif \ No newline at end of file diff --git a/examples/channels/fixedSize/spsc/source/mpi.cpp b/examples/channels/fixedSize/spsc/source/distributed/mpi.cpp similarity index 90% rename from examples/channels/fixedSize/spsc/source/mpi.cpp rename to examples/channels/fixedSize/spsc/source/distributed/mpi.cpp index 76f125b6..50523efa 100644 --- a/examples/channels/fixedSize/spsc/source/mpi.cpp +++ b/examples/channels/fixedSize/spsc/source/distributed/mpi.cpp @@ -18,8 +18,8 @@ #include #include #include -#include "include/consumer.hpp" -#include "include/producer.hpp" +#include "../include/consumer.hpp" +#include "../include/producer.hpp" int main(int argc, char **argv) { @@ -82,8 +82,8 @@ int main(int argc, char **argv) auto firstMemorySpace = *memSpaces.begin(); // Rank 0 is producer, Rank 1 is consumer - if (rankId == 0) producerFc(m, c, firstMemorySpace, channelCapacity); - if (rankId == 1) consumerFc(m, c, firstMemorySpace, channelCapacity); + if (rankId == 0) producerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity); + if (rankId == 1) consumerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity); // Finalizing MPI MPI_Finalize(); diff --git a/examples/channels/fixedSize/spsc/source/local/meson.build b/examples/channels/fixedSize/spsc/source/local/meson.build new file mode 100644 index 00000000..acbcc913 --- /dev/null +++ b/examples/channels/fixedSize/spsc/source/local/meson.build @@ -0,0 +1,21 @@ +testSuite = ['examples', 'channels', 'fixedSize', 'spsc', 'local'] +test_timeout = 60 + +if 'pthreads' in enabledBackends and 'hwloc' in enabledBackends + pthreads = executable( + 'pthreads', + ['pthreads.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test( + 'pthreads', + pthreads, + args: ['3'], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif \ No newline at end of file diff --git a/examples/channels/fixedSize/spsc/source/local/pthreads.cpp b/examples/channels/fixedSize/spsc/source/local/pthreads.cpp new file mode 100644 index 00000000..bd4d2ca3 --- /dev/null +++ b/examples/channels/fixedSize/spsc/source/local/pthreads.cpp @@ -0,0 +1,104 @@ +/* + * Copyright 2025 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include "../include/consumer.hpp" +#include "../include/producer.hpp" + +int main(int argc, char **argv) +{ + // Checking arguments + if (argc != 2) + { + fprintf(stderr, "Error: Must provide the channel capacity as argument.\n"); + return -1; + } + + // Reading argument + auto channelCapacity = std::atoi(argv[1]); + + // Capacity must be larger than zero + if (channelCapacity == 0) + { + fprintf(stderr, "Error: Cannot create channel with zero capacity.\n"); + return -1; + } + + // Creating HWloc topology object + hwloc_topology_t topology; + + // Reserving memory for hwloc + hwloc_topology_init(&topology); + + // Initializing host (CPU) topology manager + HiCR::backend::hwloc::TopologyManager dm(&topology); + + // Instantiating backend + HiCR::backend::hwloc::MemoryManager m(&topology); + + // Create shared memory + auto sharedMemoryFactory = HiCR::backend::pthreads::SharedMemoryFactory(); + auto &coordinationSharedMemory = sharedMemoryFactory.get(0, 2); + auto &payloadSharedMemory = sharedMemoryFactory.get(1, 2); + + // Set the fence count to the number of threads who are participating in the exchange + HiCR::backend::pthreads::CommunicationManager consumerCoordinationCommunicationManager(coordinationSharedMemory); + HiCR::backend::pthreads::CommunicationManager producerCoordinationCommunicationManager(coordinationSharedMemory); + HiCR::backend::pthreads::CommunicationManager consumerPayloadCommunicationManager(payloadSharedMemory); + HiCR::backend::pthreads::CommunicationManager producerPayloadCommunicationManager(payloadSharedMemory); + + // Asking backend to check the available devices + const auto t = dm.queryTopology(); + + // Getting first device found + auto d = *t.getDevices().begin(); + + // Obtaining memory spaces + auto memSpaces = d->getMemorySpaceList(); + + // Getting a reference to the first memory space + auto firstMemorySpace = *memSpaces.begin(); + + // Rank 0 is producer, Rank 1 is consumer + auto producerThread = std::thread(producerFc, + std::ref(m), + std::ref(m), + std::ref(producerCoordinationCommunicationManager), + std::ref(producerPayloadCommunicationManager), + firstMemorySpace, + firstMemorySpace, + channelCapacity); + auto consumerThread = std::thread(consumerFc, + std::ref(m), + std::ref(m), + std::ref(consumerCoordinationCommunicationManager), + std::ref(consumerPayloadCommunicationManager), + firstMemorySpace, + firstMemorySpace, + channelCapacity); + + // Wait for the execution to terminate + producerThread.join(); + consumerThread.join(); + + return 0; +} diff --git a/examples/channels/variableSize/mpsc/locking/include/consumer.hpp b/examples/channels/variableSize/mpsc/locking/include/consumer.hpp index 2a776dcc..bf2e7fc4 100644 --- a/examples/channels/variableSize/mpsc/locking/include/consumer.hpp +++ b/examples/channels/variableSize/mpsc/locking/include/consumer.hpp @@ -21,9 +21,12 @@ #include #include "common.hpp" -void consumerFc(HiCR::MemoryManager &memoryManager, - HiCR::CommunicationManager &communicationManager, - std::shared_ptr bufferMemorySpace, +void consumerFc(HiCR::MemoryManager &coordinationMemoryManager, + HiCR::MemoryManager &payloadMemoryManager, + HiCR::CommunicationManager &coordinationCommunicationManager, + HiCR::CommunicationManager &payloadCommunicationManager, + std::shared_ptr coordinationMemorySpace, + std::shared_ptr payloadMemorySpace, const size_t channelCapacity, const size_t producerCount) { @@ -31,21 +34,21 @@ void consumerFc(HiCR::MemoryManager &memoryManager, auto sizesBufferSize = HiCR::channel::variableSize::Base::getTokenBufferSize(sizeof(size_t), channelCapacity); // Allocating sizes buffer as a local memory slot - auto sizesBufferSlot = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, sizesBufferSize); + auto sizesBufferSlot = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, sizesBufferSize); const size_t payloadCapacity = channelCapacity * sizeof(ELEMENT_TYPE); // Allocating payload buffer as a local memory slot - auto payloadBufferSlot = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, payloadCapacity); + auto payloadBufferSlot = payloadMemoryManager.allocateLocalMemorySlot(payloadMemorySpace, payloadCapacity); // Getting required buffer size auto coordinationBufferSize = HiCR::channel::variableSize::Base::getCoordinationBufferSize(); // Allocating coordination buffer for internal message size metadata - auto coordinationBufferForCounts = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBufferForCounts = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); // Allocating coordination buffer for internal payload metadata - auto coordinationBufferForPayloads = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBufferForPayloads = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); // Initializing coordination buffer (sets to zero the counters) HiCR::channel::variableSize::Base::initializeCoordinationBuffer(coordinationBufferForCounts); @@ -53,23 +56,25 @@ void consumerFc(HiCR::MemoryManager &memoryManager, HiCR::channel::variableSize::Base::initializeCoordinationBuffer(coordinationBufferForPayloads); // Exchanging local memory slots to become global for them to be used by the remote end - communicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, - {{SIZES_BUFFER_KEY, sizesBufferSlot}, - {CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY, coordinationBufferForCounts}, - {CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, coordinationBufferForPayloads}, - {CONSUMER_PAYLOAD_KEY, payloadBufferSlot}}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, + {{SIZES_BUFFER_KEY, sizesBufferSlot}, + {CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY, coordinationBufferForCounts}, + {CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, coordinationBufferForPayloads}}); + payloadCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {{CONSUMER_PAYLOAD_KEY, payloadBufferSlot}}); // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Obtaining the globally exchanged memory slots - auto globalSizesBufferSlot = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, SIZES_BUFFER_KEY); - auto consumerCoordinationBufferForCounts = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); - auto consumerCoordinationBufferForPayloads = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); - auto globalPayloadBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_PAYLOAD_KEY); + auto globalSizesBufferSlot = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, SIZES_BUFFER_KEY); + auto consumerCoordinationBufferForCounts = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); + auto consumerCoordinationBufferForPayloads = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + auto globalPayloadBuffer = payloadCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_PAYLOAD_KEY); // Creating producer and consumer channels - auto consumer = HiCR::channel::variableSize::MPSC::locking::Consumer(communicationManager, + auto consumer = HiCR::channel::variableSize::MPSC::locking::Consumer(coordinationCommunicationManager, + payloadCommunicationManager, globalPayloadBuffer, /* payloadBuffer */ globalSizesBufferSlot, /* tokenSizeBuffer */ coordinationBufferForCounts, @@ -104,26 +109,28 @@ void consumerFc(HiCR::MemoryManager &memoryManager, } // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // De-registering global slots - communicationManager.deregisterGlobalMemorySlot(globalSizesBufferSlot); - communicationManager.deregisterGlobalMemorySlot(globalPayloadBuffer); - communicationManager.deregisterGlobalMemorySlot(consumerCoordinationBufferForCounts); - communicationManager.deregisterGlobalMemorySlot(consumerCoordinationBufferForPayloads); + coordinationCommunicationManager.deregisterGlobalMemorySlot(globalSizesBufferSlot); + coordinationCommunicationManager.deregisterGlobalMemorySlot(globalPayloadBuffer); + coordinationCommunicationManager.deregisterGlobalMemorySlot(consumerCoordinationBufferForCounts); + coordinationCommunicationManager.deregisterGlobalMemorySlot(consumerCoordinationBufferForPayloads); // Destroying global slots (collective calls) - communicationManager.destroyGlobalMemorySlot(globalSizesBufferSlot); - communicationManager.destroyGlobalMemorySlot(globalPayloadBuffer); - communicationManager.destroyGlobalMemorySlot(consumerCoordinationBufferForCounts); - communicationManager.destroyGlobalMemorySlot(consumerCoordinationBufferForPayloads); + coordinationCommunicationManager.destroyGlobalMemorySlot(globalSizesBufferSlot); + coordinationCommunicationManager.destroyGlobalMemorySlot(consumerCoordinationBufferForCounts); + coordinationCommunicationManager.destroyGlobalMemorySlot(consumerCoordinationBufferForPayloads); + payloadCommunicationManager.destroyGlobalMemorySlot(globalPayloadBuffer); // Fence for Global Slot destruction/cleanup - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Freeing up local memory - memoryManager.freeLocalMemorySlot(payloadBufferSlot); - memoryManager.freeLocalMemorySlot(sizesBufferSlot); - memoryManager.freeLocalMemorySlot(coordinationBufferForCounts); - memoryManager.freeLocalMemorySlot(coordinationBufferForPayloads); + payloadMemoryManager.freeLocalMemorySlot(payloadBufferSlot); + coordinationMemoryManager.freeLocalMemorySlot(sizesBufferSlot); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBufferForCounts); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBufferForPayloads); } diff --git a/examples/channels/variableSize/mpsc/locking/include/producer.hpp b/examples/channels/variableSize/mpsc/locking/include/producer.hpp index 51b41a6a..53bb8118 100644 --- a/examples/channels/variableSize/mpsc/locking/include/producer.hpp +++ b/examples/channels/variableSize/mpsc/locking/include/producer.hpp @@ -21,9 +21,12 @@ #include #include "common.hpp" -void producerFc(HiCR::MemoryManager &memoryManager, - HiCR::CommunicationManager &communicationManager, - std::shared_ptr bufferMemorySpace, +void producerFc(HiCR::MemoryManager &coordinationMemoryManager, + HiCR::MemoryManager &payloadMemoryManager, + HiCR::CommunicationManager &coordinationCommunicationManager, + HiCR::CommunicationManager &payloadCommunicationManager, + std::shared_ptr coordinationMemorySpace, + std::shared_ptr payloadMemorySpace, const size_t channelCapacity, const unsigned int producerId) { @@ -31,31 +34,34 @@ void producerFc(HiCR::MemoryManager &memoryManager, auto coordinationBufferSize = HiCR::channel::variableSize::Base::getCoordinationBufferSize(); // Allocating sizes buffer as a local memory slot - auto coordinationBufferForCounts = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBufferForCounts = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); - auto coordinationBufferForPayloads = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBufferForPayloads = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); - auto sizeInfoBuffer = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, sizeof(size_t)); + auto sizeInfoBuffer = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, sizeof(size_t)); // Initializing coordination buffers for message sizes and payloads (sets to zero the counters) HiCR::channel::variableSize::Base::initializeCoordinationBuffer(coordinationBufferForCounts); HiCR::channel::variableSize::Base::initializeCoordinationBuffer(coordinationBufferForPayloads); // Exchanging local memory slots to become global for them to be used by the remote end - communicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {}); + payloadCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {}); // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Obtaining the globally exchanged memory slots - auto sizesBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, SIZES_BUFFER_KEY); - auto consumerCoordinationBufferForCounts = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); - auto consumerCoordinationBufferForPayloads = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); - auto payloadBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_PAYLOAD_KEY); + auto sizesBuffer = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, SIZES_BUFFER_KEY); + auto consumerCoordinationBufferForCounts = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); + auto consumerCoordinationBufferForPayloads = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + auto payloadBuffer = payloadCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_PAYLOAD_KEY); const size_t payloadCapacity = channelCapacity * sizeof(ELEMENT_TYPE); // Creating producer and consumer channels - auto producer = HiCR::channel::variableSize::MPSC::locking::Producer(communicationManager, + auto producer = HiCR::channel::variableSize::MPSC::locking::Producer(coordinationCommunicationManager, + payloadCommunicationManager, sizeInfoBuffer, payloadBuffer, sizesBuffer, @@ -74,9 +80,9 @@ void producerFc(HiCR::MemoryManager &memoryManager, auto sendBuffer1Ptr = &sendBuffer1; auto sendBuffer2Ptr = &sendBuffer2; auto sendBuffer3Ptr = &sendBuffer3; - auto sendSlot1 = memoryManager.registerLocalMemorySlot(bufferMemorySpace, sendBuffer1Ptr, sizeof(sendBuffer1)); - auto sendSlot2 = memoryManager.registerLocalMemorySlot(bufferMemorySpace, sendBuffer2Ptr, sizeof(sendBuffer2)); - auto sendSlot3 = memoryManager.registerLocalMemorySlot(bufferMemorySpace, sendBuffer3Ptr, sizeof(sendBuffer3)); + auto sendSlot1 = payloadMemoryManager.registerLocalMemorySlot(payloadMemorySpace, sendBuffer1Ptr, sizeof(sendBuffer1)); + auto sendSlot2 = payloadMemoryManager.registerLocalMemorySlot(payloadMemorySpace, sendBuffer2Ptr, sizeof(sendBuffer2)); + auto sendSlot3 = payloadMemoryManager.registerLocalMemorySlot(payloadMemorySpace, sendBuffer3Ptr, sizeof(sendBuffer3)); // Pushing first batch char prefix[64] = {'\0'}; @@ -93,18 +99,20 @@ void producerFc(HiCR::MemoryManager &memoryManager, Printer::printBytes(prefix, sendBuffer3Ptr, sizeof(sendBuffer3), 0, sizeof(sendBuffer3)); // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // De-registering global slots - communicationManager.deregisterGlobalMemorySlot(sizesBuffer); - communicationManager.deregisterGlobalMemorySlot(consumerCoordinationBufferForCounts); - communicationManager.deregisterGlobalMemorySlot(consumerCoordinationBufferForPayloads); + coordinationCommunicationManager.deregisterGlobalMemorySlot(sizesBuffer); + coordinationCommunicationManager.deregisterGlobalMemorySlot(consumerCoordinationBufferForCounts); + coordinationCommunicationManager.deregisterGlobalMemorySlot(consumerCoordinationBufferForPayloads); // Fence for Global Slot destruction/cleanup - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Freeing up local memory - memoryManager.freeLocalMemorySlot(coordinationBufferForCounts); - memoryManager.freeLocalMemorySlot(coordinationBufferForPayloads); - memoryManager.freeLocalMemorySlot(sizeInfoBuffer); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBufferForCounts); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBufferForPayloads); + coordinationMemoryManager.freeLocalMemorySlot(sizeInfoBuffer); } diff --git a/examples/channels/variableSize/mpsc/locking/meson.build b/examples/channels/variableSize/mpsc/locking/meson.build index 2f42d0f6..53285f35 100644 --- a/examples/channels/variableSize/mpsc/locking/meson.build +++ b/examples/channels/variableSize/mpsc/locking/meson.build @@ -1,22 +1,6 @@ -testSuite = [ 'examples', 'channels', 'variableSize', 'mpsc', 'locking'] -test_timeout = 60 - exampleBuildIncludes = include_directories([ 'include' ]) -if 'mpi' in enabledBackends and 'hwloc' in enabledBackends - mpi = executable('mpi', [ 'source/mpi.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) - - if get_option('buildTests') - test('mpi', mpirunExecutable, args : [ '-n', '8', '--oversubscribe', mpi.full_path(), '256' ], timeout: test_timeout, suite: testSuite ) - endif -endif - -if 'lpf' in enabledBackends and 'hwloc' in enabledBackends - lpf = executable('lpf', [ 'source/lpf.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) - - if get_option('buildTests') - test('lpf', lpfrunExecutable, args : [ '-np', '8', '-engine', 'zero', lpf.full_path(), '256' ], timeout: test_timeout, suite: testSuite ) - endif -endif +subdir('source/distributed') +subdir('source/local') \ No newline at end of file diff --git a/examples/channels/variableSize/mpsc/locking/source/lpf.cpp b/examples/channels/variableSize/mpsc/locking/source/distributed/lpf.cpp similarity index 93% rename from examples/channels/variableSize/mpsc/locking/source/lpf.cpp rename to examples/channels/variableSize/mpsc/locking/source/distributed/lpf.cpp index 04989466..bf820be4 100644 --- a/examples/channels/variableSize/mpsc/locking/source/lpf.cpp +++ b/examples/channels/variableSize/mpsc/locking/source/distributed/lpf.cpp @@ -20,8 +20,8 @@ #include #include #include -#include "include/consumer.hpp" -#include "include/producer.hpp" +#include "../include/consumer.hpp" +#include "../include/producer.hpp" // flag needed when using MPI to launch const int LPF_MPI_AUTO_INITIALIZE = 0; @@ -80,8 +80,8 @@ void spmd(lpf_t lpf, lpf_pid_t pid, lpf_pid_t nprocs, lpf_args_t args) size_t producerCount = nprocs - 1; // Rank 0 is consumer, the rest are producers - if (pid == 0) consumerFc(m, c, firstMemorySpace, channelCapacity, producerCount); - if (pid >= 1) producerFc(m, c, firstMemorySpace, channelCapacity, pid); + if (pid == 0) consumerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, producerCount); + if (pid >= 1) producerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, pid); } int main(int argc, char **argv) diff --git a/examples/channels/variableSize/mpsc/locking/source/distributed/meson.build b/examples/channels/variableSize/mpsc/locking/source/distributed/meson.build new file mode 100644 index 00000000..8d18c804 --- /dev/null +++ b/examples/channels/variableSize/mpsc/locking/source/distributed/meson.build @@ -0,0 +1,40 @@ +testSuite = ['examples', 'channels', 'variableSize', 'mpsc', 'locking', 'distributed'] +test_timeout = 60 + +if 'mpi' in enabledBackends and 'hwloc' in enabledBackends + mpi = executable( + 'mpi', + ['mpi.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test( + 'mpi', + mpirunExecutable, + args: ['-n', '8', '--oversubscribe', mpi.full_path(), '256'], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif + +if 'lpf' in enabledBackends and 'hwloc' in enabledBackends + lpf = executable( + 'lpf', + ['lpf.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test( + 'lpf', + lpfrunExecutable, + args: ['-np', '8', '-engine', 'zero', lpf.full_path(), '256'], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif \ No newline at end of file diff --git a/examples/channels/variableSize/mpsc/locking/source/mpi.cpp b/examples/channels/variableSize/mpsc/locking/source/distributed/mpi.cpp similarity index 89% rename from examples/channels/variableSize/mpsc/locking/source/mpi.cpp rename to examples/channels/variableSize/mpsc/locking/source/distributed/mpi.cpp index 79bffe43..d578e956 100644 --- a/examples/channels/variableSize/mpsc/locking/source/mpi.cpp +++ b/examples/channels/variableSize/mpsc/locking/source/distributed/mpi.cpp @@ -18,8 +18,8 @@ #include #include #include -#include "include/consumer.hpp" -#include "include/producer.hpp" +#include "../include/consumer.hpp" +#include "../include/producer.hpp" int main(int argc, char **argv) { @@ -85,8 +85,8 @@ int main(int argc, char **argv) size_t producerCount = rankCount - 1; // Rank 0 is consumer, the rest are producers - if (rankId == 0) consumerFc(m, c, firstMemorySpace, channelCapacity, producerCount); - if (rankId >= 1) producerFc(m, c, firstMemorySpace, channelCapacity, rankId); + if (rankId == 0) consumerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, producerCount); + if (rankId >= 1) producerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, rankId); // Finalizing MPI MPI_Finalize(); diff --git a/examples/channels/variableSize/mpsc/locking/source/local/meson.build b/examples/channels/variableSize/mpsc/locking/source/local/meson.build new file mode 100644 index 00000000..11fa590f --- /dev/null +++ b/examples/channels/variableSize/mpsc/locking/source/local/meson.build @@ -0,0 +1,22 @@ +testSuite = ['examples', 'channels', 'variableSize', 'mpsc', 'locking', 'local'] +test_timeout = 60 + +if 'pthreads' in enabledBackends and 'hwloc' in enabledBackends + pthreads = executable( + 'pthreads', + ['pthreads.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test( + 'pthreads', + pthreads, + args: ['256', '8'], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif + diff --git a/examples/channels/variableSize/mpsc/locking/source/local/pthreads.cpp b/examples/channels/variableSize/mpsc/locking/source/local/pthreads.cpp new file mode 100644 index 00000000..f3d4fd73 --- /dev/null +++ b/examples/channels/variableSize/mpsc/locking/source/local/pthreads.cpp @@ -0,0 +1,131 @@ +/* + * Copyright 2025 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include + +#include "../include/consumer.hpp" +#include "../include/producer.hpp" + +int main(int argc, char **argv) +{ + // Checking arguments + if (argc != 3) + { + fprintf(stderr, "Error: Must provide the channel capacity and the thread pool size as argument.\n"); + return -1; + } + + // Reading argument + auto channelCapacity = std::atoi(argv[1]); + + // Capacity must be larger than zero + if (channelCapacity == 0) + { + fprintf(stderr, "Error: Cannot create channel with zero capacity.\n"); + return -1; + } + + // Reading argument + size_t threadPoolSize = std::atoi(argv[2]); + + // Capacity must be larger than zero + if (threadPoolSize == 0) + { + fprintf(stderr, "Error: Cannot create a thread pool with zero capacity.\n"); + return -1; + } + + // Creating HWloc topology object + hwloc_topology_t topology; + + // Reserving memory for hwloc + hwloc_topology_init(&topology); + + // Initializing host (CPU) topology manager + HiCR::backend::hwloc::TopologyManager dm(&topology); + + // Instantiating backend + HiCR::backend::hwloc::MemoryManager m(&topology); + + // Create shared memory + auto sharedMemoryFactory = HiCR::backend::pthreads::SharedMemoryFactory(); + auto &coordinationSharedMemory = sharedMemoryFactory.get(0, threadPoolSize); + auto &payloadSharedMemory = sharedMemoryFactory.get(1, threadPoolSize); + + // Create communication managers + std::vector coordinationCommunicationManagers; + std::vector payloadCommunicationManagers; + for (size_t i = 0; i < threadPoolSize; ++i) + { + coordinationCommunicationManagers.emplace_back(coordinationSharedMemory); + payloadCommunicationManagers.emplace_back(payloadSharedMemory); + } + + // Asking backend to check the available devices + const auto t = dm.queryTopology(); + + // Getting first device found + auto d = *t.getDevices().begin(); + + // Obtaining memory spaces + auto memSpaces = d->getMemorySpaceList(); + + // Getting a reference to the first memory space + auto firstMemorySpace = *memSpaces.begin(); + + // Create thread pool + std::vector threadPool; + for (size_t threadId = 0; threadId < threadPoolSize; ++threadId) + { + // The first thread is a consumer + if (threadId == 0) + { + threadPool.emplace_back(consumerFc, + std::ref(m), + std::ref(m), + std::ref(coordinationCommunicationManagers[threadId]), + std::ref(payloadCommunicationManagers[threadId]), + firstMemorySpace, + firstMemorySpace, + channelCapacity, + threadPoolSize - 1); + } + // All the others are producers + else + { + threadPool.emplace_back(producerFc, + std::ref(m), + std::ref(m), + std::ref(coordinationCommunicationManagers[threadId]), + std::ref(payloadCommunicationManagers[threadId]), + firstMemorySpace, + firstMemorySpace, + channelCapacity, + threadId - 1); + } + } + + // Wait for the execution to terminate + for (auto &thread : threadPool) { thread.join(); } + + return 0; +} diff --git a/examples/channels/variableSize/mpsc/nonlocking/include/consumer.hpp b/examples/channels/variableSize/mpsc/nonlocking/include/consumer.hpp index 7f78a685..8abda3ab 100644 --- a/examples/channels/variableSize/mpsc/nonlocking/include/consumer.hpp +++ b/examples/channels/variableSize/mpsc/nonlocking/include/consumer.hpp @@ -21,9 +21,12 @@ #include #include "common.hpp" -void consumerFc(HiCR::MemoryManager &memoryManager, - HiCR::CommunicationManager &communicationManager, - std::shared_ptr bufferMemorySpace, +void consumerFc(HiCR::MemoryManager &coordinationMemoryManager, + HiCR::MemoryManager &payloadMemoryManager, + HiCR::CommunicationManager &coordinationCommunicationManager, + HiCR::CommunicationManager &payloadCommunicationManager, + std::shared_ptr coordinationMemorySpace, + std::shared_ptr payloadMemorySpace, const size_t channelCapacity, const size_t producerCount) { @@ -55,8 +58,8 @@ void consumerFc(HiCR::MemoryManager &memoryManager, // consumer needs to allocate #producers {size, payload} coord. buffers for #producers SPSCs for (size_t i = 0; i < producerCount; i++) { - auto coordinationBufferForCounts = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); - auto coordinationBufferForPayloads = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBufferForCounts = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); + auto coordinationBufferForPayloads = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); // very important to initialize coordination buffers, or undefined behaviour possible! HiCR::channel::variableSize::Base::initializeCoordinationBuffer(coordinationBufferForCounts); HiCR::channel::variableSize::Base::initializeCoordinationBuffer(coordinationBufferForPayloads); @@ -64,49 +67,50 @@ void consumerFc(HiCR::MemoryManager &memoryManager, consumerCoordinationBuffersForPayloads.push_back(std::make_pair(i, coordinationBufferForPayloads)); internalCoordinationBuffersForCounts.push_back(coordinationBufferForCounts); internalCoordinationBuffersForPayloads.push_back(coordinationBufferForPayloads); - auto consumerSizesBuffer = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, sizesBufferSize); + auto consumerSizesBuffer = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, sizesBufferSize); localBuffersForCounts.push_back(std::make_pair(i, consumerSizesBuffer)); - auto consumerPayloadBuffer = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, payloadBufferSize); + auto consumerPayloadBuffer = payloadMemoryManager.allocateLocalMemorySlot(payloadMemorySpace, payloadBufferSize); localBuffersForPayloads.push_back(std::make_pair(i, consumerPayloadBuffer)); } // communicate to producers the token buffer references - communicationManager.exchangeGlobalMemorySlots(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY, consumerCoordinationBuffersForCounts); - communicationManager.exchangeGlobalMemorySlots(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, consumerCoordinationBuffersForPayloads); - communicationManager.exchangeGlobalMemorySlots(CONSUMER_TOKEN_KEY, localBuffersForCounts); - communicationManager.exchangeGlobalMemorySlots(CONSUMER_PAYLOAD_KEY, localBuffersForPayloads); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY, consumerCoordinationBuffersForCounts); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, consumerCoordinationBuffersForPayloads); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CONSUMER_TOKEN_KEY, localBuffersForCounts); + payloadCommunicationManager.exchangeGlobalMemorySlots(CONSUMER_PAYLOAD_KEY, localBuffersForPayloads); - communicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); - communicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); - communicationManager.fence(CONSUMER_TOKEN_KEY); - communicationManager.fence(CONSUMER_PAYLOAD_KEY); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + coordinationCommunicationManager.fence(CONSUMER_TOKEN_KEY); + payloadCommunicationManager.fence(CONSUMER_PAYLOAD_KEY); // get from producers their coordination buffer references - communicationManager.exchangeGlobalMemorySlots(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY, {}); - communicationManager.exchangeGlobalMemorySlots(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, {}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY, {}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, {}); - communicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY); - communicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); // get all global slot information required (local operations) for (size_t i = 0; i < producerCount; i++) { - auto producerCoordinationBufferForCounts = communicationManager.getGlobalMemorySlot(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY, i); + auto producerCoordinationBufferForCounts = coordinationCommunicationManager.getGlobalMemorySlot(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY, i); producerCoordinationBuffersForCounts.push_back(producerCoordinationBufferForCounts); - auto producerCoordinationBufferForPayloads = communicationManager.getGlobalMemorySlot(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, i); + auto producerCoordinationBufferForPayloads = coordinationCommunicationManager.getGlobalMemorySlot(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, i); producerCoordinationBuffersForPayloads.push_back(producerCoordinationBufferForPayloads); - auto consumerCoordinationBufferForPayloads = communicationManager.getGlobalMemorySlot(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, i); + auto consumerCoordinationBufferForPayloads = coordinationCommunicationManager.getGlobalMemorySlot(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, i); coordinationBuffersForPayloadsAsSlots.push_back(consumerCoordinationBufferForPayloads); - auto consumerCoordinationBufferForCounts = communicationManager.getGlobalMemorySlot(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY, i); + auto consumerCoordinationBufferForCounts = coordinationCommunicationManager.getGlobalMemorySlot(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY, i); coordinationBuffersForCountsAsSlots.push_back(consumerCoordinationBufferForCounts); - auto consumerBufferForPayloads = communicationManager.getGlobalMemorySlot(CONSUMER_PAYLOAD_KEY, i); + auto consumerBufferForPayloads = payloadCommunicationManager.getGlobalMemorySlot(CONSUMER_PAYLOAD_KEY, i); globalBuffersForPayloads.push_back(consumerBufferForPayloads); - auto consumerBufferForSizes = communicationManager.getGlobalMemorySlot(CONSUMER_TOKEN_KEY, i); + auto consumerBufferForSizes = coordinationCommunicationManager.getGlobalMemorySlot(CONSUMER_TOKEN_KEY, i); globalBuffersForCounts.push_back(consumerBufferForSizes); } // Creating consumer channels - auto consumer = new HiCR::channel::variableSize::MPSC::nonlocking::Consumer(communicationManager, + auto consumer = new HiCR::channel::variableSize::MPSC::nonlocking::Consumer(coordinationCommunicationManager, + payloadCommunicationManager, globalBuffersForPayloads, globalBuffersForCounts, internalCoordinationBuffersForCounts, @@ -146,32 +150,32 @@ void consumerFc(HiCR::MemoryManager &memoryManager, // deregister global slots -- needs to be in synch at consumer and ALL producers for (size_t i = 0; i < producerCount; i++) { - communicationManager.deregisterGlobalMemorySlot(globalBuffersForCounts[i]); - communicationManager.deregisterGlobalMemorySlot(globalBuffersForPayloads[i]); - communicationManager.deregisterGlobalMemorySlot(coordinationBuffersForCountsAsSlots[i]); - communicationManager.deregisterGlobalMemorySlot(coordinationBuffersForPayloadsAsSlots[i]); - communicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffersForCounts[i]); - communicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffersForPayloads[i]); - - communicationManager.destroyGlobalMemorySlot(globalBuffersForCounts[i]); - communicationManager.destroyGlobalMemorySlot(globalBuffersForPayloads[i]); - communicationManager.destroyGlobalMemorySlot(coordinationBuffersForCountsAsSlots[i]); - communicationManager.destroyGlobalMemorySlot(coordinationBuffersForPayloadsAsSlots[i]); + payloadCommunicationManager.deregisterGlobalMemorySlot(globalBuffersForPayloads[i]); + coordinationCommunicationManager.deregisterGlobalMemorySlot(globalBuffersForCounts[i]); + coordinationCommunicationManager.deregisterGlobalMemorySlot(coordinationBuffersForCountsAsSlots[i]); + coordinationCommunicationManager.deregisterGlobalMemorySlot(coordinationBuffersForPayloadsAsSlots[i]); + coordinationCommunicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffersForCounts[i]); + coordinationCommunicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffersForPayloads[i]); + + payloadCommunicationManager.destroyGlobalMemorySlot(globalBuffersForPayloads[i]); + coordinationCommunicationManager.destroyGlobalMemorySlot(globalBuffersForCounts[i]); + coordinationCommunicationManager.destroyGlobalMemorySlot(coordinationBuffersForCountsAsSlots[i]); + coordinationCommunicationManager.destroyGlobalMemorySlot(coordinationBuffersForPayloadsAsSlots[i]); } // Fences for global slots destructions/cleanup - communicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); - communicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); - communicationManager.fence(CONSUMER_TOKEN_KEY); - communicationManager.fence(CONSUMER_PAYLOAD_KEY); - communicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY); - communicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + payloadCommunicationManager.fence(CONSUMER_PAYLOAD_KEY); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + coordinationCommunicationManager.fence(CONSUMER_TOKEN_KEY); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); for (size_t i = 0; i < producerCount; i++) { - memoryManager.freeLocalMemorySlot(internalCoordinationBuffersForCounts[i]); - memoryManager.freeLocalMemorySlot(internalCoordinationBuffersForPayloads[i]); - memoryManager.freeLocalMemorySlot(localBuffersForCounts[i].second); - memoryManager.freeLocalMemorySlot(localBuffersForPayloads[i].second); + coordinationMemoryManager.freeLocalMemorySlot(internalCoordinationBuffersForCounts[i]); + coordinationMemoryManager.freeLocalMemorySlot(internalCoordinationBuffersForPayloads[i]); + coordinationMemoryManager.freeLocalMemorySlot(localBuffersForCounts[i].second); + coordinationMemoryManager.freeLocalMemorySlot(localBuffersForPayloads[i].second); } } diff --git a/examples/channels/variableSize/mpsc/nonlocking/include/producer.hpp b/examples/channels/variableSize/mpsc/nonlocking/include/producer.hpp index e226e7fb..74bb6830 100644 --- a/examples/channels/variableSize/mpsc/nonlocking/include/producer.hpp +++ b/examples/channels/variableSize/mpsc/nonlocking/include/producer.hpp @@ -21,9 +21,12 @@ #include #include "common.hpp" -void producerFc(HiCR::MemoryManager &memoryManager, - HiCR::CommunicationManager &communicationManager, - std::shared_ptr bufferMemorySpace, +void producerFc(HiCR::MemoryManager &coordinationMemoryManager, + HiCR::MemoryManager &payloadMemoryManager, + HiCR::CommunicationManager &coordinationCommunicationManager, + HiCR::CommunicationManager &payloadCommunicationManager, + std::shared_ptr coordinationMemorySpace, + std::shared_ptr payloadMemorySpace, const size_t channelCapacity, const size_t producerId, const size_t producerCount) @@ -32,8 +35,8 @@ void producerFc(HiCR::MemoryManager &memoryManager, // local slots at producer side for coordination buffers; needed to construct producer auto coordinationBufferSize = HiCR::channel::variableSize::Base::getCoordinationBufferSize(); - auto coordinationBufferForCounts = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); - auto coordinationBufferForPayloads = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBufferForCounts = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); + auto coordinationBufferForPayloads = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); HiCR::channel::variableSize::Base::initializeCoordinationBuffer(coordinationBufferForCounts); HiCR::channel::variableSize::Base::initializeCoordinationBuffer(coordinationBufferForPayloads); @@ -50,44 +53,45 @@ void producerFc(HiCR::MemoryManager &memoryManager, // is needed to construct producer; the rest are needed for synchronous deregister of global slots std::vector> globalBuffersForPayloads; std::vector> globalBuffersForCounts; - auto countsBuffer = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, sizeof(size_t)); + auto countsBuffer = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, sizeof(size_t)); // get from consumer global count/payload slots - communicationManager.exchangeGlobalMemorySlots(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY, {}); - communicationManager.exchangeGlobalMemorySlots(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, {}); - communicationManager.exchangeGlobalMemorySlots(CONSUMER_TOKEN_KEY, {}); - communicationManager.exchangeGlobalMemorySlots(CONSUMER_PAYLOAD_KEY, {}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY, {}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, {}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CONSUMER_TOKEN_KEY, {}); + payloadCommunicationManager.exchangeGlobalMemorySlots(CONSUMER_PAYLOAD_KEY, {}); - communicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); - communicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); - communicationManager.fence(CONSUMER_TOKEN_KEY); - communicationManager.fence(CONSUMER_PAYLOAD_KEY); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + coordinationCommunicationManager.fence(CONSUMER_TOKEN_KEY); + payloadCommunicationManager.fence(CONSUMER_PAYLOAD_KEY); // send to consumers my coordination buffers - communicationManager.exchangeGlobalMemorySlots(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY, {{producerId, coordinationBufferForCounts}}); - communicationManager.exchangeGlobalMemorySlots(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, {{producerId, coordinationBufferForPayloads}}); - communicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY); - communicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + coordinationCommunicationManager.exchangeGlobalMemorySlots(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY, {{producerId, coordinationBufferForCounts}}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, {{producerId, coordinationBufferForPayloads}}); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); // get all global slot information required (local operations) for (size_t i = 0; i < producerCount; i++) { - auto producerCoordinationBufferForCounts = communicationManager.getGlobalMemorySlot(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY, i); + auto producerCoordinationBufferForCounts = coordinationCommunicationManager.getGlobalMemorySlot(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY, i); producerCoordinationBuffersForCounts.push_back(producerCoordinationBufferForCounts); - auto producerCoordinationBufferForPayloads = communicationManager.getGlobalMemorySlot(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, i); + auto producerCoordinationBufferForPayloads = coordinationCommunicationManager.getGlobalMemorySlot(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, i); producerCoordinationBuffersForPayloads.push_back(producerCoordinationBufferForPayloads); - auto consumerCoordinationBufferForPayloads = communicationManager.getGlobalMemorySlot(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, i); + auto consumerCoordinationBufferForPayloads = coordinationCommunicationManager.getGlobalMemorySlot(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, i); coordinationBuffersForPayloadsAsSlots.push_back(consumerCoordinationBufferForPayloads); - auto consumerCoordinationBufferForCounts = communicationManager.getGlobalMemorySlot(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY, i); + auto consumerCoordinationBufferForCounts = coordinationCommunicationManager.getGlobalMemorySlot(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY, i); coordinationBuffersForCountsAsSlots.push_back(consumerCoordinationBufferForCounts); - auto consumerBufferForPayloads = communicationManager.getGlobalMemorySlot(CONSUMER_PAYLOAD_KEY, i); + auto consumerBufferForPayloads = payloadCommunicationManager.getGlobalMemorySlot(CONSUMER_PAYLOAD_KEY, i); globalBuffersForPayloads.push_back(consumerBufferForPayloads); - auto consumerBufferForSizes = communicationManager.getGlobalMemorySlot(CONSUMER_TOKEN_KEY, i); + auto consumerBufferForSizes = coordinationCommunicationManager.getGlobalMemorySlot(CONSUMER_TOKEN_KEY, i); globalBuffersForCounts.push_back(consumerBufferForSizes); } // Creating producer channel - auto producer = HiCR::channel::variableSize::MPSC::nonlocking::Producer(communicationManager, + auto producer = HiCR::channel::variableSize::MPSC::nonlocking::Producer(coordinationCommunicationManager, + payloadCommunicationManager, countsBuffer, globalBuffersForPayloads[producerId], globalBuffersForCounts[producerId], @@ -120,7 +124,7 @@ void producerFc(HiCR::MemoryManager &memoryManager, for (size_t i = 0; i < MESSAGES_PER_PRODUCER; i++) { size_t nextElemSize = elements[i].second * sizeof(ELEMENT_TYPE); - auto sendSlot = memoryManager.registerLocalMemorySlot(bufferMemorySpace, elements[i].first, nextElemSize); + auto sendSlot = payloadMemoryManager.registerLocalMemorySlot(payloadMemorySpace, elements[i].first, nextElemSize); // is the message sizes buffer full? // Note: it might be necessary sometimes to also check the payload buffers @@ -134,26 +138,26 @@ void producerFc(HiCR::MemoryManager &memoryManager, // deregister global slots -- needs to be in synch at consumer and ALL producers for (size_t i = 0; i < producerCount; i++) { - communicationManager.deregisterGlobalMemorySlot(globalBuffersForCounts[i]); - communicationManager.deregisterGlobalMemorySlot(globalBuffersForPayloads[i]); - communicationManager.deregisterGlobalMemorySlot(coordinationBuffersForCountsAsSlots[i]); - communicationManager.deregisterGlobalMemorySlot(coordinationBuffersForPayloadsAsSlots[i]); - communicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffersForCounts[i]); - communicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffersForPayloads[i]); + payloadCommunicationManager.deregisterGlobalMemorySlot(globalBuffersForPayloads[i]); + coordinationCommunicationManager.deregisterGlobalMemorySlot(globalBuffersForCounts[i]); + coordinationCommunicationManager.deregisterGlobalMemorySlot(coordinationBuffersForCountsAsSlots[i]); + coordinationCommunicationManager.deregisterGlobalMemorySlot(coordinationBuffersForPayloadsAsSlots[i]); + coordinationCommunicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffersForCounts[i]); + coordinationCommunicationManager.deregisterGlobalMemorySlot(producerCoordinationBuffersForPayloads[i]); } - communicationManager.destroyGlobalMemorySlot(producerCoordinationBuffersForCounts[producerId]); - communicationManager.destroyGlobalMemorySlot(producerCoordinationBuffersForPayloads[producerId]); + coordinationCommunicationManager.destroyGlobalMemorySlot(producerCoordinationBuffersForCounts[producerId]); + coordinationCommunicationManager.destroyGlobalMemorySlot(producerCoordinationBuffersForPayloads[producerId]); // Fences for global slots destructions/cleanup - communicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); - communicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); - communicationManager.fence(CONSUMER_TOKEN_KEY); - communicationManager.fence(CONSUMER_PAYLOAD_KEY); - communicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY); - communicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); - - memoryManager.freeLocalMemorySlot(coordinationBufferForCounts); - memoryManager.freeLocalMemorySlot(coordinationBufferForPayloads); - memoryManager.freeLocalMemorySlot(countsBuffer); + payloadCommunicationManager.fence(CONSUMER_PAYLOAD_KEY); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); + coordinationCommunicationManager.fence(CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + coordinationCommunicationManager.fence(CONSUMER_TOKEN_KEY); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY); + coordinationCommunicationManager.fence(PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + + coordinationMemoryManager.freeLocalMemorySlot(coordinationBufferForCounts); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBufferForPayloads); + coordinationMemoryManager.freeLocalMemorySlot(countsBuffer); } diff --git a/examples/channels/variableSize/mpsc/nonlocking/meson.build b/examples/channels/variableSize/mpsc/nonlocking/meson.build index bfdc44f4..241c6516 100644 --- a/examples/channels/variableSize/mpsc/nonlocking/meson.build +++ b/examples/channels/variableSize/mpsc/nonlocking/meson.build @@ -1,22 +1,4 @@ -testSuite = [ 'examples', 'channels', 'variableSize', 'mpsc', 'nonlocking'] -test_timeout = 60 +exampleBuildIncludes = include_directories(['include']) -exampleBuildIncludes = include_directories([ - 'include' - ]) - -if 'mpi' in enabledBackends and 'hwloc' in enabledBackends - mpi = executable('mpi', [ 'source/mpi.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) - - if get_option('buildTests') - test('mpi', mpirunExecutable, args : [ '-n', '8', '--oversubscribe', mpi.full_path(), '256' ], timeout: test_timeout, suite: testSuite ) - endif -endif - -if 'lpf' in enabledBackends and 'hwloc' in enabledBackends - lpf = executable('lpf', [ 'source/lpf.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) - - if get_option('buildTests') - test('lpf', lpfrunExecutable, args : [ '-np', '8', '-engine', 'zero', lpf.full_path(), '256' ], timeout: test_timeout, suite: testSuite ) - endif -endif +subdir('source/distributed') +subdir('source/local') \ No newline at end of file diff --git a/examples/channels/variableSize/mpsc/nonlocking/source/lpf.cpp b/examples/channels/variableSize/mpsc/nonlocking/source/distributed/lpf.cpp similarity index 92% rename from examples/channels/variableSize/mpsc/nonlocking/source/lpf.cpp rename to examples/channels/variableSize/mpsc/nonlocking/source/distributed/lpf.cpp index b4ef7cc6..9102b5e2 100644 --- a/examples/channels/variableSize/mpsc/nonlocking/source/lpf.cpp +++ b/examples/channels/variableSize/mpsc/nonlocking/source/distributed/lpf.cpp @@ -20,8 +20,8 @@ #include #include #include -#include "include/consumer.hpp" -#include "include/producer.hpp" +#include "../include/consumer.hpp" +#include "../include/producer.hpp" // flag needed when using MPI to launch const int LPF_MPI_AUTO_INITIALIZE = 0; @@ -80,8 +80,8 @@ void spmd(lpf_t lpf, lpf_pid_t pid, lpf_pid_t nprocs, lpf_args_t args) size_t producerCount = nprocs - 1; // Rank 0 is consumer, the rest are producers - if (pid == 0) consumerFc(m, c, firstMemorySpace, channelCapacity, producerCount); - if (pid >= 1) producerFc(m, c, firstMemorySpace, channelCapacity, pid - 1, producerCount); + if (pid == 0) consumerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, producerCount); + if (pid >= 1) producerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, pid - 1, producerCount); } int main(int argc, char **argv) diff --git a/examples/channels/variableSize/mpsc/nonlocking/source/distributed/meson.build b/examples/channels/variableSize/mpsc/nonlocking/source/distributed/meson.build new file mode 100644 index 00000000..40653165 --- /dev/null +++ b/examples/channels/variableSize/mpsc/nonlocking/source/distributed/meson.build @@ -0,0 +1,40 @@ +testSuite = ['examples', 'channels', 'variableSize', 'mpsc', 'nonlocking'] +test_timeout = 60 + +if 'mpi' in enabledBackends and 'hwloc' in enabledBackends + mpi = executable( + 'mpi', + ['mpi.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test( + 'mpi', + mpirunExecutable, + args: ['-n', '8', '--oversubscribe', mpi.full_path(), '256'], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif + +if 'lpf' in enabledBackends and 'hwloc' in enabledBackends + lpf = executable( + 'lpf', + ['lpf.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test( + 'lpf', + lpfrunExecutable, + args: ['-np', '8', '-engine', 'zero', lpf.full_path(), '256'], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif \ No newline at end of file diff --git a/examples/channels/variableSize/mpsc/nonlocking/source/mpi.cpp b/examples/channels/variableSize/mpsc/nonlocking/source/distributed/mpi.cpp similarity index 89% rename from examples/channels/variableSize/mpsc/nonlocking/source/mpi.cpp rename to examples/channels/variableSize/mpsc/nonlocking/source/distributed/mpi.cpp index eddf7db6..6b4469e8 100644 --- a/examples/channels/variableSize/mpsc/nonlocking/source/mpi.cpp +++ b/examples/channels/variableSize/mpsc/nonlocking/source/distributed/mpi.cpp @@ -18,8 +18,8 @@ #include #include #include -#include "include/consumer.hpp" -#include "include/producer.hpp" +#include "../include/consumer.hpp" +#include "../include/producer.hpp" int main(int argc, char **argv) { @@ -85,8 +85,8 @@ int main(int argc, char **argv) size_t producerCount = rankCount - 1; // Rank 0 is consumer, the rest are producers - if (rankId == 0) consumerFc(m, c, firstMemorySpace, channelCapacity, producerCount); - if (rankId >= 1) producerFc(m, c, firstMemorySpace, channelCapacity, rankId - 1, producerCount); + if (rankId == 0) consumerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, producerCount); + if (rankId >= 1) producerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity, rankId - 1, producerCount); // Finalizing MPI MPI_Finalize(); diff --git a/examples/channels/variableSize/mpsc/nonlocking/source/local/meson.build b/examples/channels/variableSize/mpsc/nonlocking/source/local/meson.build new file mode 100644 index 00000000..3a00caf4 --- /dev/null +++ b/examples/channels/variableSize/mpsc/nonlocking/source/local/meson.build @@ -0,0 +1,21 @@ +testSuite = ['examples', 'channels', 'variableSize', 'mpsc', 'nonlocking'] +test_timeout = 60 + +if 'pthreads' in enabledBackends and 'hwloc' in enabledBackends + pthreads = executable( + 'pthreads', + ['pthreads.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test( + 'pthreads', + pthreads, + args: ['256', '8'], + timeout: test_timeout, + suite: testSuite, + ) + endif +endif \ No newline at end of file diff --git a/examples/channels/variableSize/mpsc/nonlocking/source/local/pthreads.cpp b/examples/channels/variableSize/mpsc/nonlocking/source/local/pthreads.cpp new file mode 100644 index 00000000..f052828c --- /dev/null +++ b/examples/channels/variableSize/mpsc/nonlocking/source/local/pthreads.cpp @@ -0,0 +1,132 @@ +/* + * Copyright 2025 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include + +#include "../include/consumer.hpp" +#include "../include/producer.hpp" + +int main(int argc, char **argv) +{ + // Checking arguments + if (argc != 3) + { + fprintf(stderr, "Error: Must provide the channel capacity and the thread pool size as argument.\n"); + return -1; + } + + // Reading argument + auto channelCapacity = std::atoi(argv[1]); + + // Capacity must be larger than zero + if (channelCapacity == 0) + { + fprintf(stderr, "Error: Cannot create channel with zero capacity.\n"); + return -1; + } + + // Reading argument + size_t threadPoolSize = std::atoi(argv[2]); + + // Capacity must be larger than zero + if (threadPoolSize == 0) + { + fprintf(stderr, "Error: Cannot create a thread pool with zero capacity.\n"); + return -1; + } + + // Creating HWloc topology object + hwloc_topology_t topology; + + // Reserving memory for hwloc + hwloc_topology_init(&topology); + + // Initializing host (CPU) topology manager + HiCR::backend::hwloc::TopologyManager dm(&topology); + + // Instantiating backend + HiCR::backend::hwloc::MemoryManager m(&topology); + + // Create shared memory + auto sharedMemoryFactory = HiCR::backend::pthreads::SharedMemoryFactory(); + auto &coordinationSharedMemory = sharedMemoryFactory.get(0, threadPoolSize); + auto &payloadSharedMemory = sharedMemoryFactory.get(1, threadPoolSize); + + // Create communication managers + std::vector coordinationCommunicationManagers; + std::vector payloadCommunicationManagers; + for (size_t i = 0; i < threadPoolSize; ++i) + { + coordinationCommunicationManagers.emplace_back(coordinationSharedMemory); + payloadCommunicationManagers.emplace_back(payloadSharedMemory); + } + + // Asking backend to check the available devices + const auto t = dm.queryTopology(); + + // Getting first device found + auto d = *t.getDevices().begin(); + + // Obtaining memory spaces + auto memSpaces = d->getMemorySpaceList(); + + // Getting a reference to the first memory space + auto firstMemorySpace = *memSpaces.begin(); + + // Create thread pool + std::vector threadPool; + for (size_t threadId = 0; threadId < threadPoolSize; ++threadId) + { + // The first thread is a consumer + if (threadId == 0) + { + threadPool.emplace_back(consumerFc, + std::ref(m), + std::ref(m), + std::ref(coordinationCommunicationManagers[threadId]), + std::ref(payloadCommunicationManagers[threadId]), + firstMemorySpace, + firstMemorySpace, + channelCapacity, + threadPoolSize - 1); + } + // All the others are producers + else + { + threadPool.emplace_back(producerFc, + std::ref(m), + std::ref(m), + std::ref(coordinationCommunicationManagers[threadId]), + std::ref(payloadCommunicationManagers[threadId]), + firstMemorySpace, + firstMemorySpace, + channelCapacity, + threadId - 1, + threadPoolSize - 1); + } + } + + // Wait for the execution to terminate + for (auto &thread : threadPool) { thread.join(); } + + return 0; +} diff --git a/examples/channels/variableSize/spsc/include/consumer.hpp b/examples/channels/variableSize/spsc/include/consumer.hpp index 3c384b2c..9bd079fe 100644 --- a/examples/channels/variableSize/spsc/include/consumer.hpp +++ b/examples/channels/variableSize/spsc/include/consumer.hpp @@ -21,28 +21,31 @@ #include #include "common.hpp" -void consumerFc(HiCR::MemoryManager &memoryManager, - HiCR::CommunicationManager &communicationManager, - std::shared_ptr bufferMemorySpace, +void consumerFc(HiCR::MemoryManager &coordinationMemoryManager, + HiCR::MemoryManager &payloadMemoryManager, + HiCR::CommunicationManager &coordinationCommunicationManager, + HiCR::CommunicationManager &payloadCommunicationManager, + std::shared_ptr coordinationMemorySpace, + std::shared_ptr payloadMemorySpace, const size_t channelCapacity) { // Getting required buffer sizes auto sizesBufferSize = HiCR::channel::variableSize::Base::getTokenBufferSize(sizeof(size_t), channelCapacity); // Allocating sizes buffer as a local memory slot - auto sizesBufferSlot = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, sizesBufferSize); + auto sizesBufferSlot = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, sizesBufferSize); // Allocating payload buffer as a local memory slot - auto payloadBufferSlot = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, PAYLOAD_CAPACITY); + auto payloadBufferSlot = payloadMemoryManager.allocateLocalMemorySlot(payloadMemorySpace, PAYLOAD_CAPACITY); // Getting required buffer size auto coordinationBufferSize = HiCR::channel::variableSize::Base::getCoordinationBufferSize(); // Allocating coordination buffer for internal message size metadata - auto coordinationBufferForCounts = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBufferForCounts = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); // Allocating coordination buffer for internal payload metadata - auto coordinationBufferForPayloads = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBufferForPayloads = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); // Initializing coordination buffer (sets to zero the counters) HiCR::channel::variableSize::Base::initializeCoordinationBuffer(coordinationBufferForCounts); @@ -50,26 +53,29 @@ void consumerFc(HiCR::MemoryManager &memoryManager, HiCR::channel::variableSize::Base::initializeCoordinationBuffer(coordinationBufferForPayloads); // Exchanging local memory slots to become global for them to be used by the remote end - communicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, - {{SIZES_BUFFER_KEY, sizesBufferSlot}, - {CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY, coordinationBufferForCounts}, - {CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, coordinationBufferForPayloads}, - {CONSUMER_PAYLOAD_KEY, payloadBufferSlot}}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, + {{SIZES_BUFFER_KEY, sizesBufferSlot}, + {CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY, coordinationBufferForCounts}, + {CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, coordinationBufferForPayloads}}); + + payloadCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {{CONSUMER_PAYLOAD_KEY, payloadBufferSlot}}); // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Obtaining the globally exchanged memory slots - std::shared_ptr globalSizesBufferSlot = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, SIZES_BUFFER_KEY); + std::shared_ptr globalSizesBufferSlot = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, SIZES_BUFFER_KEY); - auto producerCoordinationBufferForCounts = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY); - auto producerCoordinationBufferForPayloads = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); - auto consumerCoordinationBufferForCounts = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); - auto consumerCoordinationBufferForPayloads = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); - auto payloadBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_PAYLOAD_KEY); + auto producerCoordinationBufferForCounts = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY); + auto producerCoordinationBufferForPayloads = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + auto consumerCoordinationBufferForCounts = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); + auto consumerCoordinationBufferForPayloads = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + auto payloadBuffer = payloadCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_PAYLOAD_KEY); // Creating producer and consumer channels - auto consumer = HiCR::channel::variableSize::SPSC::Consumer(communicationManager, + auto consumer = HiCR::channel::variableSize::SPSC::Consumer(coordinationCommunicationManager, + payloadCommunicationManager, payloadBuffer /*payload buffer */, globalSizesBufferSlot, coordinationBufferForCounts, @@ -97,25 +103,27 @@ void consumerFc(HiCR::MemoryManager &memoryManager, } // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // De-registering global slots - communicationManager.deregisterGlobalMemorySlot(globalSizesBufferSlot); - communicationManager.deregisterGlobalMemorySlot(producerCoordinationBufferForCounts); - communicationManager.deregisterGlobalMemorySlot(producerCoordinationBufferForPayloads); - communicationManager.deregisterGlobalMemorySlot(consumerCoordinationBufferForCounts); - communicationManager.deregisterGlobalMemorySlot(consumerCoordinationBufferForPayloads); + coordinationCommunicationManager.deregisterGlobalMemorySlot(globalSizesBufferSlot); + coordinationCommunicationManager.deregisterGlobalMemorySlot(producerCoordinationBufferForCounts); + coordinationCommunicationManager.deregisterGlobalMemorySlot(producerCoordinationBufferForPayloads); + coordinationCommunicationManager.deregisterGlobalMemorySlot(consumerCoordinationBufferForCounts); + coordinationCommunicationManager.deregisterGlobalMemorySlot(consumerCoordinationBufferForPayloads); // Destroying global slots (collective calls) - communicationManager.destroyGlobalMemorySlot(consumerCoordinationBufferForCounts); - communicationManager.destroyGlobalMemorySlot(consumerCoordinationBufferForPayloads); - communicationManager.destroyGlobalMemorySlot(payloadBuffer); + coordinationCommunicationManager.destroyGlobalMemorySlot(consumerCoordinationBufferForCounts); + coordinationCommunicationManager.destroyGlobalMemorySlot(consumerCoordinationBufferForPayloads); + payloadCommunicationManager.destroyGlobalMemorySlot(payloadBuffer); - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Freeing up local memory - memoryManager.freeLocalMemorySlot(payloadBufferSlot); - memoryManager.freeLocalMemorySlot(sizesBufferSlot); - memoryManager.freeLocalMemorySlot(coordinationBufferForCounts); - memoryManager.freeLocalMemorySlot(coordinationBufferForPayloads); + payloadMemoryManager.freeLocalMemorySlot(payloadBufferSlot); + coordinationMemoryManager.freeLocalMemorySlot(sizesBufferSlot); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBufferForCounts); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBufferForPayloads); } diff --git a/examples/channels/variableSize/spsc/include/producer.hpp b/examples/channels/variableSize/spsc/include/producer.hpp index c30c54ad..90c253e9 100644 --- a/examples/channels/variableSize/spsc/include/producer.hpp +++ b/examples/channels/variableSize/spsc/include/producer.hpp @@ -21,43 +21,50 @@ #include #include -void producerFc(HiCR::MemoryManager &memoryManager, - HiCR::CommunicationManager &communicationManager, - std::shared_ptr bufferMemorySpace, +void producerFc(HiCR::MemoryManager &coordinationMemoryManager, + HiCR::MemoryManager &payloadMemoryManager, + HiCR::CommunicationManager &coordinationCommunicationManager, + HiCR::CommunicationManager &payloadCommunicationManager, + std::shared_ptr coordinationMemorySpace, + std::shared_ptr payloadMemorySpace, const size_t channelCapacity) { // Getting required buffer size auto coordinationBufferSize = HiCR::channel::variableSize::Base::getCoordinationBufferSize(); // Allocating sizes buffer as a local memory slot - auto coordinationBufferForCounts = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBufferForCounts = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); - auto coordinationBufferForPayloads = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, coordinationBufferSize); + auto coordinationBufferForPayloads = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, coordinationBufferSize); - auto sizeInfoBuffer = memoryManager.allocateLocalMemorySlot(bufferMemorySpace, sizeof(size_t)); + auto sizeInfoBuffer = coordinationMemoryManager.allocateLocalMemorySlot(coordinationMemorySpace, sizeof(size_t)); // Initializing coordination buffers for message sizes and payloads (sets to zero the counters) HiCR::channel::variableSize::Base::initializeCoordinationBuffer(coordinationBufferForCounts); HiCR::channel::variableSize::Base::initializeCoordinationBuffer(coordinationBufferForPayloads); // Exchanging local memory slots to become global for them to be used by the remote end - communicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, /* global tag */ - {{PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY, coordinationBufferForCounts}, /* key-slot pairs */ - {PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, coordinationBufferForPayloads}}); + coordinationCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, /* global tag */ + {{PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY, coordinationBufferForCounts}, /* key-slot pairs */ + {PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY, coordinationBufferForPayloads}}); + + payloadCommunicationManager.exchangeGlobalMemorySlots(CHANNEL_TAG, {}); // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Obtaining the globally exchanged memory slots - auto sizesBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, SIZES_BUFFER_KEY); - auto producerCoordinationBufferForCounts = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY); - auto producerCoordinationBufferForPayloads = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); - auto consumerCoordinationBufferForCounts = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); - auto consumerCoordinationBufferForPayloads = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); - auto payloadBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_PAYLOAD_KEY); + auto sizesBuffer = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, SIZES_BUFFER_KEY); + auto producerCoordinationBufferForCounts = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, PRODUCER_COORDINATION_BUFFER_FOR_SIZES_KEY); + auto producerCoordinationBufferForPayloads = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, PRODUCER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + auto consumerCoordinationBufferForCounts = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_SIZES_KEY); + auto consumerCoordinationBufferForPayloads = coordinationCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_FOR_PAYLOADS_KEY); + auto payloadBuffer = payloadCommunicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_PAYLOAD_KEY); // Creating producer and consumer channels - auto producer = HiCR::channel::variableSize::SPSC::Producer(communicationManager, + auto producer = HiCR::channel::variableSize::SPSC::Producer(coordinationCommunicationManager, + payloadCommunicationManager, sizeInfoBuffer, payloadBuffer, sizesBuffer, @@ -76,9 +83,9 @@ void producerFc(HiCR::MemoryManager &memoryManager, auto sendBufferPtr = &sendBuffer; auto sendBuffer2Ptr = &sendBuffer2; auto sendBuffer3Ptr = &sendBuffer3; - auto sendSlot = memoryManager.registerLocalMemorySlot(bufferMemorySpace, sendBufferPtr, sizeof(sendBuffer)); - auto sendSlot2 = memoryManager.registerLocalMemorySlot(bufferMemorySpace, sendBuffer2Ptr, sizeof(sendBuffer2)); - auto sendSlot3 = memoryManager.registerLocalMemorySlot(bufferMemorySpace, sendBuffer3Ptr, sizeof(sendBuffer3)); + auto sendSlot = payloadMemoryManager.registerLocalMemorySlot(payloadMemorySpace, sendBufferPtr, sizeof(sendBuffer)); + auto sendSlot2 = payloadMemoryManager.registerLocalMemorySlot(payloadMemorySpace, sendBuffer2Ptr, sizeof(sendBuffer2)); + auto sendSlot3 = payloadMemoryManager.registerLocalMemorySlot(payloadMemorySpace, sendBuffer3Ptr, sizeof(sendBuffer3)); // Pushing first batch producer.push(sendSlot); @@ -99,17 +106,19 @@ void producerFc(HiCR::MemoryManager &memoryManager, Printer::printBytes("PRODUCER sent:", sendBuffer3Ptr, sizeof(sendBuffer3), 0, sizeof(sendBuffer3)); // Synchronizing so that all actors have finished registering their global memory slots - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Destroying global slots (collective calls) - communicationManager.destroyGlobalMemorySlot(sizesBuffer); - communicationManager.destroyGlobalMemorySlot(producerCoordinationBufferForCounts); - communicationManager.destroyGlobalMemorySlot(producerCoordinationBufferForPayloads); + coordinationCommunicationManager.destroyGlobalMemorySlot(sizesBuffer); + coordinationCommunicationManager.destroyGlobalMemorySlot(producerCoordinationBufferForCounts); + coordinationCommunicationManager.destroyGlobalMemorySlot(producerCoordinationBufferForPayloads); - communicationManager.fence(CHANNEL_TAG); + coordinationCommunicationManager.fence(CHANNEL_TAG); + payloadCommunicationManager.fence(CHANNEL_TAG); // Freeing up local memory - memoryManager.freeLocalMemorySlot(coordinationBufferForCounts); - memoryManager.freeLocalMemorySlot(coordinationBufferForPayloads); - memoryManager.freeLocalMemorySlot(sizeInfoBuffer); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBufferForCounts); + coordinationMemoryManager.freeLocalMemorySlot(coordinationBufferForPayloads); + coordinationMemoryManager.freeLocalMemorySlot(sizeInfoBuffer); } diff --git a/examples/channels/variableSize/spsc/meson.build b/examples/channels/variableSize/spsc/meson.build index 1eb8bd48..241c6516 100644 --- a/examples/channels/variableSize/spsc/meson.build +++ b/examples/channels/variableSize/spsc/meson.build @@ -1,22 +1,4 @@ -testSuite = [ 'examples', 'channels', 'variableSize', 'spsc'] -test_timeout = 60 +exampleBuildIncludes = include_directories(['include']) -exampleBuildIncludes = include_directories([ - 'include' - ]) - -if 'mpi' in enabledBackends and 'hwloc' in enabledBackends - mpi = executable('mpi', [ 'source/mpi.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) - - if get_option('buildTests') - test('mpi', mpirunExecutable, args : [ '-n', '2', '--oversubscribe', mpi.full_path(), '3' ], timeout: test_timeout, suite: testSuite ) - endif -endif - -if 'lpf' in enabledBackends and 'hwloc' in enabledBackends - lpf = executable('lpf', [ 'source/lpf.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) - - if get_option('buildTests') - test('lpf', lpfrunExecutable, args : [ '-np', '2', '-engine', 'zero', lpf.full_path(), '3' ], timeout: test_timeout, suite: testSuite ) - endif -endif +subdir('source/distributed') +subdir('source/local') \ No newline at end of file diff --git a/examples/channels/variableSize/spsc/source/lpf.cpp b/examples/channels/variableSize/spsc/source/distributed/lpf.cpp similarity index 93% rename from examples/channels/variableSize/spsc/source/lpf.cpp rename to examples/channels/variableSize/spsc/source/distributed/lpf.cpp index ba3f0c4f..70484952 100644 --- a/examples/channels/variableSize/spsc/source/lpf.cpp +++ b/examples/channels/variableSize/spsc/source/distributed/lpf.cpp @@ -20,8 +20,8 @@ #include #include #include -#include "include/consumer.hpp" -#include "include/producer.hpp" +#include "../include/consumer.hpp" +#include "../include/producer.hpp" // flag needed when using MPI to launch const int LPF_MPI_AUTO_INITIALIZE = 0; @@ -78,8 +78,8 @@ void spmd(lpf_t lpf, lpf_pid_t pid, lpf_pid_t nprocs, lpf_args_t args) auto firstMemorySpace = *memSpaces.begin(); // Rank 0 is producer, Rank 1 is consumer - if (pid == 0) producerFc(m, c, firstMemorySpace, channelCapacity); - if (pid == 1) consumerFc(m, c, firstMemorySpace, channelCapacity); + if (pid == 0) producerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity); + if (pid == 1) consumerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity); } int main(int argc, char **argv) diff --git a/examples/channels/variableSize/spsc/source/distributed/meson.build b/examples/channels/variableSize/spsc/source/distributed/meson.build new file mode 100644 index 00000000..34ce4a46 --- /dev/null +++ b/examples/channels/variableSize/spsc/source/distributed/meson.build @@ -0,0 +1,18 @@ +testSuite = ['examples', 'channels', 'variableSize', 'spsc', 'distributed'] +test_timeout = 60 + +if 'mpi' in enabledBackends and 'hwloc' in enabledBackends + mpi = executable('mpi', [ 'mpi.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) + + if get_option('buildTests') + test('mpi', mpirunExecutable, args : [ '-n', '2', '--oversubscribe', mpi.full_path(), '3' ], timeout: test_timeout, suite: testSuite ) + endif +endif + +if 'lpf' in enabledBackends and 'hwloc' in enabledBackends + lpf = executable('lpf', [ 'lpf.cpp' ], dependencies: hicrBuildDep, include_directories: [exampleBuildIncludes] ) + + if get_option('buildTests') + test('lpf', lpfrunExecutable, args : [ '-np', '2', '-engine', 'zero', lpf.full_path(), '3' ], timeout: test_timeout, suite: testSuite ) + endif +endif diff --git a/examples/channels/variableSize/spsc/source/mpi.cpp b/examples/channels/variableSize/spsc/source/distributed/mpi.cpp similarity index 90% rename from examples/channels/variableSize/spsc/source/mpi.cpp rename to examples/channels/variableSize/spsc/source/distributed/mpi.cpp index 1b59649d..1ced78ac 100644 --- a/examples/channels/variableSize/spsc/source/mpi.cpp +++ b/examples/channels/variableSize/spsc/source/distributed/mpi.cpp @@ -18,8 +18,8 @@ #include #include #include -#include "include/consumer.hpp" -#include "include/producer.hpp" +#include "../include/consumer.hpp" +#include "../include/producer.hpp" int main(int argc, char **argv) { @@ -82,8 +82,8 @@ int main(int argc, char **argv) auto firstMemorySpace = *memSpaces.begin(); // Rank 0 is producer, Rank 1 is consumer - if (rankId == 0) producerFc(m, c, firstMemorySpace, channelCapacity); - if (rankId == 1) consumerFc(m, c, firstMemorySpace, channelCapacity); + if (rankId == 0) producerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity); + if (rankId == 1) consumerFc(m, m, c, c, firstMemorySpace, firstMemorySpace, channelCapacity); // Finalizing MPI MPI_Finalize(); diff --git a/examples/channels/variableSize/spsc/source/local/meson.build b/examples/channels/variableSize/spsc/source/local/meson.build new file mode 100644 index 00000000..64177000 --- /dev/null +++ b/examples/channels/variableSize/spsc/source/local/meson.build @@ -0,0 +1,15 @@ +testSuite = ['examples', 'channels', 'variableSize', 'spsc', 'local'] +test_timeout = 60 + +if 'pthreads' in enabledBackends and 'hwloc' in enabledBackends + pthreads = executable( + 'pthreads', + ['pthreads.cpp'], + dependencies: hicrBuildDep, + include_directories: [exampleBuildIncludes], + ) + + if get_option('buildTests') + test('pthreads', pthreads, args: ['3'], timeout: test_timeout, suite: testSuite) + endif +endif \ No newline at end of file diff --git a/examples/channels/variableSize/spsc/source/local/pthreads.cpp b/examples/channels/variableSize/spsc/source/local/pthreads.cpp new file mode 100644 index 00000000..bd4d2ca3 --- /dev/null +++ b/examples/channels/variableSize/spsc/source/local/pthreads.cpp @@ -0,0 +1,104 @@ +/* + * Copyright 2025 Huawei Technologies Co., Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include +#include +#include +#include + +#include "../include/consumer.hpp" +#include "../include/producer.hpp" + +int main(int argc, char **argv) +{ + // Checking arguments + if (argc != 2) + { + fprintf(stderr, "Error: Must provide the channel capacity as argument.\n"); + return -1; + } + + // Reading argument + auto channelCapacity = std::atoi(argv[1]); + + // Capacity must be larger than zero + if (channelCapacity == 0) + { + fprintf(stderr, "Error: Cannot create channel with zero capacity.\n"); + return -1; + } + + // Creating HWloc topology object + hwloc_topology_t topology; + + // Reserving memory for hwloc + hwloc_topology_init(&topology); + + // Initializing host (CPU) topology manager + HiCR::backend::hwloc::TopologyManager dm(&topology); + + // Instantiating backend + HiCR::backend::hwloc::MemoryManager m(&topology); + + // Create shared memory + auto sharedMemoryFactory = HiCR::backend::pthreads::SharedMemoryFactory(); + auto &coordinationSharedMemory = sharedMemoryFactory.get(0, 2); + auto &payloadSharedMemory = sharedMemoryFactory.get(1, 2); + + // Set the fence count to the number of threads who are participating in the exchange + HiCR::backend::pthreads::CommunicationManager consumerCoordinationCommunicationManager(coordinationSharedMemory); + HiCR::backend::pthreads::CommunicationManager producerCoordinationCommunicationManager(coordinationSharedMemory); + HiCR::backend::pthreads::CommunicationManager consumerPayloadCommunicationManager(payloadSharedMemory); + HiCR::backend::pthreads::CommunicationManager producerPayloadCommunicationManager(payloadSharedMemory); + + // Asking backend to check the available devices + const auto t = dm.queryTopology(); + + // Getting first device found + auto d = *t.getDevices().begin(); + + // Obtaining memory spaces + auto memSpaces = d->getMemorySpaceList(); + + // Getting a reference to the first memory space + auto firstMemorySpace = *memSpaces.begin(); + + // Rank 0 is producer, Rank 1 is consumer + auto producerThread = std::thread(producerFc, + std::ref(m), + std::ref(m), + std::ref(producerCoordinationCommunicationManager), + std::ref(producerPayloadCommunicationManager), + firstMemorySpace, + firstMemorySpace, + channelCapacity); + auto consumerThread = std::thread(consumerFc, + std::ref(m), + std::ref(m), + std::ref(consumerCoordinationCommunicationManager), + std::ref(consumerPayloadCommunicationManager), + firstMemorySpace, + firstMemorySpace, + channelCapacity); + + // Wait for the execution to terminate + producerThread.join(); + consumerThread.join(); + + return 0; +} diff --git a/examples/objectStore/publishRead/source/include/ownerInstance.hpp b/examples/objectStore/publishRead/source/include/ownerInstance.hpp index f43e5ee5..85573d51 100644 --- a/examples/objectStore/publishRead/source/include/ownerInstance.hpp +++ b/examples/objectStore/publishRead/source/include/ownerInstance.hpp @@ -54,6 +54,7 @@ void owner(HiCR::MemoryManager &memoryManager, HiCR::CommunicationManager &commu // Creating producer and consumer channels auto producer = HiCR::channel::variableSize::SPSC::Producer(communicationManager, + communicationManager, sizeInfoBuffer, payloadBuffer, sizesBuffer, diff --git a/examples/objectStore/publishRead/source/include/readerInstance.hpp b/examples/objectStore/publishRead/source/include/readerInstance.hpp index bed511f4..eac51b1e 100644 --- a/examples/objectStore/publishRead/source/include/readerInstance.hpp +++ b/examples/objectStore/publishRead/source/include/readerInstance.hpp @@ -66,6 +66,7 @@ void reader(HiCR::MemoryManager &memoryManager, HiCR::CommunicationManager &comm // Creating producer and consumer channels auto consumer = HiCR::channel::variableSize::SPSC::Consumer(communicationManager, + communicationManager, payloadBuffer /*payload buffer */, globalSizesBufferSlot, coordinationBufferForCounts, diff --git a/examples/pingPong/include/consumer.hpp b/examples/pingPong/include/consumer.hpp index e5a17a7f..f498fd08 100644 --- a/examples/pingPong/include/consumer.hpp +++ b/examples/pingPong/include/consumer.hpp @@ -52,10 +52,10 @@ void consumerFc(HiCR::MemoryManager &memoryManager, auto consumerPongCoordinationBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_PONG_COORDINATION_BUFFER_KEY); // Creating producer and consumer channels - auto pingChannel = - HiCR::channel::fixedSize::SPSC::Consumer(communicationManager, pingTokenBufferSlot, pingCoordinationBuffer, producerPingCoordinationBuffer, tokenSize, channelCapacity); - auto pongChannel = - HiCR::channel::fixedSize::SPSC::Producer(communicationManager, pongTokenBufferSlot, pongCoordinationBuffer, producerPongCoordinationBuffer, tokenSize, channelCapacity); + auto pingChannel = HiCR::channel::fixedSize::SPSC::Consumer( + communicationManager, communicationManager, pingTokenBufferSlot, pingCoordinationBuffer, producerPingCoordinationBuffer, tokenSize, channelCapacity); + auto pongChannel = HiCR::channel::fixedSize::SPSC::Producer( + communicationManager, communicationManager, pongTokenBufferSlot, pongCoordinationBuffer, producerPongCoordinationBuffer, tokenSize, channelCapacity); // Getting a single value from the channel for (int i = 0; i < msgCount; i++) diff --git a/examples/pingPong/include/producer.hpp b/examples/pingPong/include/producer.hpp index 3f622e94..4e93fc4c 100644 --- a/examples/pingPong/include/producer.hpp +++ b/examples/pingPong/include/producer.hpp @@ -49,11 +49,11 @@ void producerFc(HiCR::MemoryManager &memoryManager, auto consumerPongCoordinationBuffer = communicationManager.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_PONG_COORDINATION_BUFFER_KEY); // Creating producer and consumer channels - auto pingChannel = - HiCR::channel::fixedSize::SPSC::Producer(communicationManager, pingTokenBufferSlot, pingCoordinationBuffer, consumerPingCoordinationBuffer, tokenSize, channelCapacity); + auto pingChannel = HiCR::channel::fixedSize::SPSC::Producer( + communicationManager, communicationManager, pingTokenBufferSlot, pingCoordinationBuffer, consumerPingCoordinationBuffer, tokenSize, channelCapacity); // For the ponger, the consumer buffer i - auto pongChannel = - HiCR::channel::fixedSize::SPSC::Consumer(communicationManager, pongTokenBufferSlot, pongCoordinationBuffer, consumerPongCoordinationBuffer, tokenSize, channelCapacity); + auto pongChannel = HiCR::channel::fixedSize::SPSC::Consumer( + communicationManager, communicationManager, pongTokenBufferSlot, pongCoordinationBuffer, consumerPongCoordinationBuffer, tokenSize, channelCapacity); // Allocating a send slot to put the values we want to communicate ELEMENT_TYPE sendBuffer[tokenSize] = {'a'}; diff --git a/include/hicr/backends/pthreads/communicationManager.hpp b/include/hicr/backends/pthreads/communicationManager.hpp index 2deecdd3..94b3a6b4 100644 --- a/include/hicr/backends/pthreads/communicationManager.hpp +++ b/include/hicr/backends/pthreads/communicationManager.hpp @@ -108,9 +108,6 @@ class CommunicationManager final : public HiCR::CommunicationManager // Creating new memory slot auto globalMemorySlot = std::make_shared(tag, globalKey, memorySlot); - // Registering memory slot - registerGlobalMemorySlot(globalMemorySlot); - // Push it to shared memory _sharedMemory.insert(tag, globalKey, globalMemorySlot); } @@ -122,11 +119,18 @@ class CommunicationManager final : public HiCR::CommunicationManager } /** - * Implementation of the fence operation for the pthreads backend. In this case, nothing needs to be done, as - * the system's memcpy operation is synchronous. This means that it's mere execution (whether immediate or deferred) - * ensures its completion. + * Implementation of the fence operation for the pthreads backend. After all threads exchanged + * their slots, each one of those updates their internal map of global memory slots + * + * \param[in] tag */ - __INLINE__ void fenceImpl(const HiCR::GlobalMemorySlot::tag_t tag) override { _sharedMemory.barrier(); } + __INLINE__ void fenceImpl(const HiCR::GlobalMemorySlot::tag_t tag) override + { + // Wait all threads to reach this point + _sharedMemory.barrier(); + // Registering memory slot + for (const auto &[key, slot] : _sharedMemory.getKeyMemorySlots(tag)) { registerGlobalMemorySlot(slot); } + } __INLINE__ void memcpyImpl(const std::shared_ptr &destination, const size_t dst_offset, diff --git a/include/hicr/backends/pthreads/sharedMemory.hpp b/include/hicr/backends/pthreads/sharedMemory.hpp index 99e7793e..d59751a7 100644 --- a/include/hicr/backends/pthreads/sharedMemory.hpp +++ b/include/hicr/backends/pthreads/sharedMemory.hpp @@ -49,6 +49,11 @@ class SharedMemory */ friend class SharedMemoryFactory; + /** + * Identifier for shared memory +*/ + using sharedMemoryId_t = uint64_t; + public: ~SharedMemory() @@ -92,7 +97,7 @@ class SharedMemory * * \return shared pointer to global memory slot if present, nullptr otherwise */ - __INLINE__ std::shared_ptr get(const GlobalMemorySlot::tag_t tag, const GlobalMemorySlot::globalKey_t key) + __INLINE__ std::shared_ptr get(const GlobalMemorySlot::tag_t tag, const GlobalMemorySlot::globalKey_t key) const { std::shared_ptr value = nullptr; @@ -103,7 +108,7 @@ class SharedMemory if (_globalMemorySlots.find(tag) != _globalMemorySlots.end()) { // Look for the key - if (_globalMemorySlots[tag].find(key) != _globalMemorySlots[tag].end()) { value = _globalMemorySlots[tag][key]; } + if (_globalMemorySlots.at(tag).find(key) != _globalMemorySlots.at(tag).end()) { value = _globalMemorySlots.at(tag).at(key); } } // Unlock the resource @@ -118,7 +123,7 @@ class SharedMemory * \param[in] tag slot tag * \param[in] key slot key */ - __INLINE__ void remove(const GlobalMemorySlot::tag_t tag, const GlobalMemorySlot::globalKey_t globalKey) + __INLINE__ void remove(const GlobalMemorySlot::tag_t tag, const GlobalMemorySlot::globalKey_t key) { // Lock the resource pthread_mutex_lock(&_mutex); @@ -127,27 +132,69 @@ class SharedMemory if (_globalMemorySlots.find(tag) != _globalMemorySlots.end()) { // Look for the key - if (_globalMemorySlots[tag].find(globalKey) != _globalMemorySlots[tag].end()) { _globalMemorySlots[tag].erase(globalKey); } + if (_globalMemorySlots[tag].find(key) != _globalMemorySlots[tag].end()) { _globalMemorySlots[tag].erase(key); } } // Unlock the resource pthread_mutex_unlock(&_mutex); } + /** + * Return the pair key-slots for a given tag + * + * \param[in] tag + * + * \return key-slots pair, empty map otherwise +*/ + __INLINE__ CommunicationManager::globalKeyToMemorySlotMap_t getKeyMemorySlots(const GlobalMemorySlot::tag_t tag) const + { + CommunicationManager::globalKeyToMemorySlotMap_t keyMemorySlots; + + // Lock the resource + pthread_mutex_lock(&_mutex); + + // Search for the tag + auto it = _globalMemorySlots.find(tag); + + // Fail if not found + if (it == _globalMemorySlots.end()) + { + // Unlock the resource + pthread_mutex_unlock(&_mutex); + + return keyMemorySlots; + } + + // Unlock the resource + pthread_mutex_unlock(&_mutex); + + // Return key slot pairs + return it->second; + } + /** * A barrier implementation that synchronizes all threads in the HiCR instance */ __INLINE__ void barrier() { pthread_barrier_wait(&_barrier); } + /** + * Id getter + * + * \return Identifier of the shared memory instance + */ + __INLINE__ sharedMemoryId_t getId() const { return _id; } + private: /** * Private constructor. Can be called only by \ref SharedMemoryFactory * + * \param[in] id Identifier for the instance of shared memory * \param[in] fenceCount barrier size. Indicates how many threads should reach the barrier before continuing */ - SharedMemory(const size_t fenceCount) - : _fenceCount(fenceCount) + SharedMemory(const sharedMemoryId_t id, const size_t fenceCount) + : _id(id), + _fenceCount(fenceCount) { // Init barrier pthread_barrier_init(&_barrier, nullptr, _fenceCount); @@ -156,15 +203,22 @@ class SharedMemory pthread_mutex_init(&_mutex, nullptr); } + /** + * Shared Memory ID + */ + const sharedMemoryId_t _id; + /** * Stores a barrier object to check on a barrier operation */ pthread_barrier_t _barrier{}; /** - * A mutex to make sure threads do not bother each other during certain operations + * A mutex to make sure threads do not bother each other during certain operations. + * Mutability allows const getter functions to lock the mutex, because this does not modify the logical + * state of the shared memory */ - pthread_mutex_t _mutex{}; + mutable pthread_mutex_t _mutex{}; /** * How many threads should reach the fence before proceeding diff --git a/include/hicr/backends/pthreads/sharedMemoryFactory.hpp b/include/hicr/backends/pthreads/sharedMemoryFactory.hpp index 4240d84f..1ef47d45 100644 --- a/include/hicr/backends/pthreads/sharedMemoryFactory.hpp +++ b/include/hicr/backends/pthreads/sharedMemoryFactory.hpp @@ -74,7 +74,7 @@ class SharedMemoryFactory if (it == _sharedMemoryMap.end()) { // Create a new shared memory - auto sharedMemoryPtr = std::unique_ptr(new SharedMemory(fenceCount)); + auto sharedMemoryPtr = std::unique_ptr(new SharedMemory(id, fenceCount)); auto &sharedMemory = *sharedMemoryPtr; // Store it into the global slots map diff --git a/include/hicr/core/globalMemorySlot.hpp b/include/hicr/core/globalMemorySlot.hpp index f83fbe97..6fb89e10 100644 --- a/include/hicr/core/globalMemorySlot.hpp +++ b/include/hicr/core/globalMemorySlot.hpp @@ -86,6 +86,11 @@ class GlobalMemorySlot */ __INLINE__ std::shared_ptr getSourceLocalMemorySlot() noexcept { return _sourceLocalMemorySlot; } + /** + * Set the source local memory slot for the global slot + * + * \param[in] sourceLocalMemorySlot the source local memory slot + */ __INLINE__ void setSourceLocalMemorySlot(std::shared_ptr sourceLocalMemorySlot) { _sourceLocalMemorySlot = sourceLocalMemorySlot; } private: diff --git a/include/hicr/frontends/RPCEngine/RPCEngine.hpp b/include/hicr/frontends/RPCEngine/RPCEngine.hpp index 6f1924e6..ab9e7932 100644 --- a/include/hicr/frontends/RPCEngine/RPCEngine.hpp +++ b/include/hicr/frontends/RPCEngine/RPCEngine.hpp @@ -200,6 +200,9 @@ class RPCEngine return _RPCConsumerChannel->getDepth() > 0; } + /** + * Parse information of the incoming RPC and trigger its execution + */ __INLINE__ void parseAndExecuteRPC() { // Once a request has arrived, gather its value from the channel @@ -462,8 +465,13 @@ class RPCEngine globalConsumerCoordinationBuffers.push_back(consumerCoordinationBuffer); } - _RPCConsumerChannel = std::make_shared( - _communicationManager, globalConsumerTokenBuffers, localConsumerCoordinationBuffers, globalProducerCoordinationBuffers, tokenSize, _HICR_RPC_ENGINE_CHANNEL_COUNT_CAPACITY); + _RPCConsumerChannel = std::make_shared(_communicationManager, + _communicationManager, + globalConsumerTokenBuffers, + localConsumerCoordinationBuffers, + globalProducerCoordinationBuffers, + tokenSize, + _HICR_RPC_ENGINE_CHANNEL_COUNT_CAPACITY); } ////////// Creating producer channels to send fixed sized RPC requests to other instances @@ -499,6 +507,7 @@ class RPCEngine // the producer of MPSC::nonlocking has the same view _RPCProducerChannels[consumerInstanceId] = std::make_shared(_communicationManager, + _communicationManager, globalConsumerTokenBuffers[i], globalProducerCoordinationBuffers[i]->getSourceLocalMemorySlot(), globalConsumerCoordinationBuffers[i], @@ -564,6 +573,7 @@ class RPCEngine // Creating channel _returnValueConsumerChannel = std::make_shared(_communicationManager, + _communicationManager, consumerMessagePayloadBuffer, consumerMessageSizesBuffer, localConsumerCoordinationBufferMessageSizes, @@ -596,6 +606,7 @@ class RPCEngine // Creating channel _returnValueProducerChannels[consumerInstanceId] = std::make_shared(_communicationManager, + _communicationManager, localProducerSizeInfoBuffer, consumerMessagePayloadBuffer, consumerMessageSizesBuffer, diff --git a/include/hicr/frontends/channel/base.hpp b/include/hicr/frontends/channel/base.hpp index e28edd53..d34f6e85 100644 --- a/include/hicr/frontends/channel/base.hpp +++ b/include/hicr/frontends/channel/base.hpp @@ -173,7 +173,8 @@ class Base * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend's memory manager to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] coordinationBuffer This is a small buffer that needs to be allocated at the producer side. * enables the consumer to signal how many tokens it has popped. It may also be used for other coordination signals. * \param[in] tokenSize The size of each token. @@ -185,8 +186,13 @@ class Base * before. That is, if the received message counter starts as zero, it will transition to 1 and then to to 2, if * 'A' arrives before than 'B', or; directly to 2, if 'B' arrives before 'A'. */ - Base(CommunicationManager &communicationManager, const std::shared_ptr &coordinationBuffer, const size_t tokenSize, const size_t capacity) - : _communicationManager(&communicationManager), + Base(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, + const std::shared_ptr &coordinationBuffer, + const size_t tokenSize, + const size_t capacity) + : _coordinationCommunicationManager(&coordinationCommunicationManager), + _payloadCommunicationManager(&payloadCommunicationManager), _coordinationBuffer(coordinationBuffer), _tokenSize(tokenSize) { @@ -211,10 +217,16 @@ class Base ~Base() = default; /** - * Get the internal communication buffer assigned to this channel - * @return The internal communication buffer assigned to this channel + * Get the payload communication manager assigned to this channel + * @return The payload communication manager assigned to this channel */ - [[nodiscard]] __INLINE__ CommunicationManager *getCommunicationManager() const { return _communicationManager; } + [[nodiscard]] __INLINE__ CommunicationManager *getPayloadCommunicationManager() const { return _payloadCommunicationManager; } + + /** + * Get the coordination communication manager assigned to this channel + * @return The coordination communication manager assigned to this channel + */ + [[nodiscard]] __INLINE__ CommunicationManager *getCoordinationCommunicationManager() const { return _coordinationCommunicationManager; } /** * Get the internal coordination buffer assigned to this channel @@ -225,9 +237,14 @@ class Base private: /** - * Pointer to the backend that is in charge of executing the memory transfer operations + * Pointer to the backend that is in charge of updating coordination buffers + */ + CommunicationManager *const _coordinationCommunicationManager; + + /** + * Pointer to the backend that is in charge of executing the payload memory transfer operations */ - CommunicationManager *const _communicationManager; + CommunicationManager *const _payloadCommunicationManager; /** * Local storage of coordination metadata diff --git a/include/hicr/frontends/channel/fixedSize/base.hpp b/include/hicr/frontends/channel/fixedSize/base.hpp index 4289b2d3..91265d7b 100644 --- a/include/hicr/frontends/channel/fixedSize/base.hpp +++ b/include/hicr/frontends/channel/fixedSize/base.hpp @@ -47,7 +47,8 @@ class Base : public channel::Base * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend's memory manager to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] coordinationBuffer This is a small buffer that needs to be allocated at the producer side. * enables the consumer to signal how many tokens it has popped. It may also be used for other coordination signals. * \param[in] tokenSize The size of each token. @@ -59,8 +60,12 @@ class Base : public channel::Base * before. That is, if the received message counter starts as zero, it will transition to 1 and then to to 2, if * 'A' arrives before than 'B', or; directly to 2, if 'B' arrives before 'A'. */ - Base(CommunicationManager &communicationManager, const std::shared_ptr &coordinationBuffer, const size_t tokenSize, const size_t capacity) - : channel::Base(communicationManager, coordinationBuffer, tokenSize, capacity) + Base(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, + const std::shared_ptr &coordinationBuffer, + const size_t tokenSize, + const size_t capacity) + : channel::Base(coordinationCommunicationManager, payloadCommunicationManager, coordinationBuffer, tokenSize, capacity) {} }; diff --git a/include/hicr/frontends/channel/fixedSize/mpsc/locking/consumer.hpp b/include/hicr/frontends/channel/fixedSize/mpsc/locking/consumer.hpp index 5c5c22dc..f5db257b 100644 --- a/include/hicr/frontends/channel/fixedSize/mpsc/locking/consumer.hpp +++ b/include/hicr/frontends/channel/fixedSize/mpsc/locking/consumer.hpp @@ -59,7 +59,8 @@ class Consumer final : public channel::fixedSize::Base * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] tokenBuffer The memory slot pertaining to the token buffer. The producer will push new * tokens into this buffer, while there is enough space. This buffer should be big enough to hold at least one * token. @@ -68,13 +69,14 @@ class Consumer final : public channel::fixedSize::Base * \param[in] tokenSize The size of each token. * \param[in] capacity The maximum number of tokens that will be held by this channel */ - Consumer(CommunicationManager &communicationManager, + Consumer(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, std::shared_ptr tokenBuffer, const std::shared_ptr &internalCoordinationBuffer, std::shared_ptr consumerCoordinationBuffer, const size_t tokenSize, const size_t capacity) - : channel::fixedSize::Base(communicationManager, internalCoordinationBuffer, tokenSize, capacity), + : channel::fixedSize::Base(coordinationCommunicationManager, payloadCommunicationManager, internalCoordinationBuffer, tokenSize, capacity), _tokenBuffer(std::move(tokenBuffer)), _consumerCoordinationBuffer(std::move(consumerCoordinationBuffer)) {} @@ -115,10 +117,13 @@ class Consumer final : public channel::fixedSize::Base // Value to return, initially set as -1 as default (not able to find the requested value) ssize_t bufferPos = -1; + auto coordinationCommunicationManager = getCoordinationCommunicationManager(); + // Obtaining coordination buffer slot lock - if (getCommunicationManager()->acquireGlobalLock(_consumerCoordinationBuffer) == false) return bufferPos; + if (coordinationCommunicationManager->acquireGlobalLock(_consumerCoordinationBuffer) == false) return bufferPos; - getCommunicationManager()->flushReceived(); + coordinationCommunicationManager->flushReceived(); + getPayloadCommunicationManager()->flushReceived(); // Calculating current channel depth const auto curDepth = getDepth(); @@ -126,7 +131,7 @@ class Consumer final : public channel::fixedSize::Base if (pos < curDepth) bufferPos = (ssize_t)((getCircularBuffer()->getTailPosition() + pos) % getCircularBuffer()->getCapacity()); // Releasing coordination buffer slot lock - getCommunicationManager()->releaseGlobalLock(_consumerCoordinationBuffer); + coordinationCommunicationManager->releaseGlobalLock(_consumerCoordinationBuffer); // Succeeded in pushing the token(s) return bufferPos; @@ -154,8 +159,10 @@ class Consumer final : public channel::fixedSize::Base // Flag to indicate whether the operaton was successful bool successFlag = false; + auto coordinationCommunicationManager = getCoordinationCommunicationManager(); + // Obtaining coordination buffer slot lock - if (getCommunicationManager()->acquireGlobalLock(_consumerCoordinationBuffer) == false) return successFlag; + if (coordinationCommunicationManager->acquireGlobalLock(_consumerCoordinationBuffer) == false) return successFlag; // If the exchange buffer does not have n tokens pushed, reject operation, otherwise succeed if (n <= getDepth()) @@ -168,7 +175,7 @@ class Consumer final : public channel::fixedSize::Base } // Releasing coordination buffer slot lock - getCommunicationManager()->releaseGlobalLock(_consumerCoordinationBuffer); + coordinationCommunicationManager->releaseGlobalLock(_consumerCoordinationBuffer); // Operation was successful return successFlag; diff --git a/include/hicr/frontends/channel/fixedSize/mpsc/locking/producer.hpp b/include/hicr/frontends/channel/fixedSize/mpsc/locking/producer.hpp index da69d0e0..047bde5d 100644 --- a/include/hicr/frontends/channel/fixedSize/mpsc/locking/producer.hpp +++ b/include/hicr/frontends/channel/fixedSize/mpsc/locking/producer.hpp @@ -58,7 +58,8 @@ class Producer final : public fixedSize::Base * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] tokenBuffer The memory slot pertaining to the token buffer. The producer will push new * tokens into this buffer, while there is enough space. This buffer should be big enough to hold at least one token. * \param[in] internalCoordinationBuffer This is a small buffer to hold the internal (loca) state of the channel's circular buffer @@ -66,13 +67,14 @@ class Producer final : public fixedSize::Base * \param[in] tokenSize The size of each token. * \param[in] capacity The maximum number of tokens that will be held by this channel */ - Producer(CommunicationManager &communicationManager, + Producer(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, std::shared_ptr tokenBuffer, const std::shared_ptr &internalCoordinationBuffer, std::shared_ptr consumerCoordinationBuffer, const size_t tokenSize, const size_t capacity) - : fixedSize::Base(communicationManager, internalCoordinationBuffer, tokenSize, capacity), + : fixedSize::Base(coordinationCommunicationManager, payloadCommunicationManager, internalCoordinationBuffer, tokenSize, capacity), _tokenBuffer(std::move(tokenBuffer)), _consumerCoordinationBuffer(std::move(consumerCoordinationBuffer)) {} @@ -111,46 +113,50 @@ class Producer final : public fixedSize::Base // Flag to record whether the operation was successful or not (it simplifies code by releasing locks only once) bool successFlag = false; + auto coordinationCommunicationManager = getCoordinationCommunicationManager(); + // Locking remote token and coordination buffer slots - if (getCommunicationManager()->acquireGlobalLock(_consumerCoordinationBuffer) == false) return successFlag; + if (coordinationCommunicationManager->acquireGlobalLock(_consumerCoordinationBuffer) == false) return successFlag; // Updating local coordination buffer - getCommunicationManager()->memcpy(getCoordinationBuffer(), 0, _consumerCoordinationBuffer, 0, getCoordinationBufferSize()); + coordinationCommunicationManager->memcpy(getCoordinationBuffer(), 0, _consumerCoordinationBuffer, 0, getCoordinationBufferSize()); // Adding fence operation to ensure buffers are ready for re-use - getCommunicationManager()->fence(getCoordinationBuffer(), 0, 1); + coordinationCommunicationManager->fence(getCoordinationBuffer(), 0, 1); // Calculating current channel depth const auto depth = getDepth(); + auto payloadCommunicationManager = getPayloadCommunicationManager(); + // If the exchange buffer does not have n free slots, reject the operation if (depth + n <= getCircularBuffer()->getCapacity()) { // Copying with source increasing offset per token for (size_t i = 0; i < n; i++) { - getCommunicationManager()->memcpy(_tokenBuffer, /* destination */ - getTokenSize() * getCircularBuffer()->getHeadPosition(), /* dst_offset */ - sourceSlot, /* source */ - i * getTokenSize(), /* src_offset */ - getTokenSize()); /* size*/ + payloadCommunicationManager->memcpy(_tokenBuffer, /* destination */ + getTokenSize() * getCircularBuffer()->getHeadPosition(), /* dst_offset */ + sourceSlot, /* source */ + i * getTokenSize(), /* src_offset */ + getTokenSize()); /* size*/ // Advance head here, since the memcpy relies on the up-to-date head position getCircularBuffer()->advanceHead(1); } - getCommunicationManager()->fence(sourceSlot, n, 0); + payloadCommunicationManager->fence(sourceSlot, n, 0); // Updating global coordination buffer - getCommunicationManager()->memcpy(_consumerCoordinationBuffer, 0, getCoordinationBuffer(), 0, getCoordinationBufferSize()); + coordinationCommunicationManager->memcpy(_consumerCoordinationBuffer, 0, getCoordinationBuffer(), 0, getCoordinationBufferSize()); // Adding fence operation to ensure buffers are ready for re-use - getCommunicationManager()->fence(getCoordinationBuffer(), 1, 0); + coordinationCommunicationManager->fence(getCoordinationBuffer(), 1, 0); // Mark operation as successful successFlag = true; } // Releasing remote token and coordination buffer slots - getCommunicationManager()->releaseGlobalLock(_consumerCoordinationBuffer); + coordinationCommunicationManager->releaseGlobalLock(_consumerCoordinationBuffer); // Succeeded return successFlag; diff --git a/include/hicr/frontends/channel/fixedSize/mpsc/nonlocking/consumer.hpp b/include/hicr/frontends/channel/fixedSize/mpsc/nonlocking/consumer.hpp index 1299931f..bfe35684 100644 --- a/include/hicr/frontends/channel/fixedSize/mpsc/nonlocking/consumer.hpp +++ b/include/hicr/frontends/channel/fixedSize/mpsc/nonlocking/consumer.hpp @@ -47,7 +47,8 @@ class Consumer * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] tokenBuffers The list of p memory slots pertaining for the p producers. Each producer will push new * tokens into its own buffer, relying on an SPSC channel * token. @@ -58,14 +59,16 @@ class Consumer * \param[in] tokenSize The size of each token. * \param[in] capacity The maximum number of tokens that will be held by this channel */ - Consumer(CommunicationManager &communicationManager, + Consumer(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, std::vector> tokenBuffers, std::vector> internalCoordinationBuffers, std::vector> producerCoordinationBuffers, const size_t tokenSize, const size_t capacity) : _tokenBuffers(tokenBuffers), - _communicationManager(&communicationManager) + _coordinationCommunicationManager(&coordinationCommunicationManager), + _payloadCommunicationManager(&payloadCommunicationManager) { // make sure producer and consumer sides provide p elements, equalling // the number of producers @@ -74,8 +77,8 @@ class Consumer // create p (= number of producers) SPSC channels for (size_t i = 0; i < internalCoordinationBuffers.size(); i++) { - std::shared_ptr consumerPtr( - new fixedSize::SPSC::Consumer(communicationManager, tokenBuffers[i], internalCoordinationBuffers[i], producerCoordinationBuffers[i], tokenSize, capacity)); + std::shared_ptr consumerPtr(new fixedSize::SPSC::Consumer( + coordinationCommunicationManager, payloadCommunicationManager, tokenBuffers[i], internalCoordinationBuffers[i], producerCoordinationBuffers[i], tokenSize, capacity)); _spscList.push_back(consumerPtr); _depths.push_back(0); } @@ -103,7 +106,8 @@ class Consumer // be of type std::vector instead of std::queue if (pos > 0) HICR_THROW_LOGIC("Nonblocking MPSC not yet implemented for peek with n!=0"); - _communicationManager->flushReceived(); + _coordinationCommunicationManager->flushReceived(); + _payloadCommunicationManager->flushReceived(); updateDepth(); if (_channelPushes.empty()) HICR_THROW_RUNTIME("Attempting to peek position (%lu) but supporting queue has size (%lu)", pos, _channelPushes.size()); @@ -226,10 +230,16 @@ class Consumer * A snapshot of the last recorded depths in all SPSC channels (initialized with 0s) */ std::vector _depths; + + /** + * Pointer to the backend that is in charge of updating coordination buffers + */ + CommunicationManager *const _coordinationCommunicationManager; + /** * Pointer to the backend that is in charge of executing the memory transfer operations */ - CommunicationManager *const _communicationManager; + CommunicationManager *const _payloadCommunicationManager; }; } // namespace HiCR::channel::fixedSize::MPSC::nonlocking diff --git a/include/hicr/frontends/channel/fixedSize/mpsc/nonlocking/producer.hpp b/include/hicr/frontends/channel/fixedSize/mpsc/nonlocking/producer.hpp index b34a9e21..958df295 100644 --- a/include/hicr/frontends/channel/fixedSize/mpsc/nonlocking/producer.hpp +++ b/include/hicr/frontends/channel/fixedSize/mpsc/nonlocking/producer.hpp @@ -44,7 +44,8 @@ class Producer final : public fixedSize::SPSC::Producer /** * This constructor simply calls the SPSC Producer constructor * - * \param[in] communicationManager The backend to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] tokenBuffer The memory slot pertaining to the token buffer. The producer will push new * tokens into this buffer, while there is enough space. This buffer should be big enough to hold at least one token. * \param[in] internalCoordinationBuffer This is a small buffer to hold the internal (loca) state of the channel's circular buffer @@ -52,13 +53,20 @@ class Producer final : public fixedSize::SPSC::Producer * \param[in] tokenSize The size of each token. * \param[in] capacity The maximum number of tokens that will be held by this channel */ - Producer(CommunicationManager &communicationManager, + Producer(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, std::shared_ptr tokenBuffer, const std::shared_ptr &internalCoordinationBuffer, const std::shared_ptr &producerCoordinationBuffer, const size_t tokenSize, const size_t capacity) - : fixedSize::SPSC::Producer(communicationManager, std::move(tokenBuffer), internalCoordinationBuffer, producerCoordinationBuffer, tokenSize, capacity) + : fixedSize::SPSC::Producer(coordinationCommunicationManager, + payloadCommunicationManager, + std::move(tokenBuffer), + internalCoordinationBuffer, + producerCoordinationBuffer, + tokenSize, + capacity) {} ~Producer() = default; }; diff --git a/include/hicr/frontends/channel/fixedSize/spsc/consumer.hpp b/include/hicr/frontends/channel/fixedSize/spsc/consumer.hpp index 5bf6c859..8afc7f23 100644 --- a/include/hicr/frontends/channel/fixedSize/spsc/consumer.hpp +++ b/include/hicr/frontends/channel/fixedSize/spsc/consumer.hpp @@ -60,7 +60,8 @@ class Consumer final : public channel::fixedSize::Base * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] tokenBuffer The memory slot pertaining to the token buffer. The producer will push new * tokens into this buffer, while there is enough space. This buffer should be big enough to hold at least one * token. @@ -69,13 +70,14 @@ class Consumer final : public channel::fixedSize::Base * \param[in] tokenSize The size of each token. * \param[in] capacity The maximum number of tokens that will be held by this channel */ - Consumer(CommunicationManager &communicationManager, + Consumer(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, const std::shared_ptr &tokenBuffer, const std::shared_ptr &internalCoordinationBuffer, std::shared_ptr producerCoordinationBuffer, const size_t tokenSize, const size_t capacity) - : channel::fixedSize::Base(communicationManager, internalCoordinationBuffer, tokenSize, capacity), + : channel::fixedSize::Base(coordinationCommunicationManager, payloadCommunicationManager, internalCoordinationBuffer, tokenSize, capacity), _tokenBuffer(tokenBuffer), _producerCoordinationBuffer(std::move(producerCoordinationBuffer)) @@ -123,7 +125,8 @@ class Consumer final : public channel::fixedSize::Base HICR_THROW_LOGIC("Attempting to peek for a token with position (%lu), which is beyond than the channel capacity (%lu)", pos, getCircularBuffer()->getCapacity()); // Make sure receiver queues are occasionally processed - getCommunicationManager()->flushReceived(); + getCoordinationCommunicationManager()->flushReceived(); + getPayloadCommunicationManager()->flushReceived(); // Updating channel depth updateDepth(); @@ -167,14 +170,16 @@ class Consumer final : public channel::fixedSize::Base // Advancing tail (removes elements from the circular buffer) getCircularBuffer()->advanceTail(n); - const auto coordBuffElemSize = sizeof(_HICR_CHANNEL_COORDINATION_BUFFER_ELEMENT_TYPE); + const auto coordBuffElemSize = sizeof(_HICR_CHANNEL_COORDINATION_BUFFER_ELEMENT_TYPE); + auto coordinationCommunicationManager = getCoordinationCommunicationManager(); + // Notifying producer(s) of buffer liberation - getCommunicationManager()->memcpy(_producerCoordinationBuffer, - _HICR_CHANNEL_TAIL_ADVANCE_COUNT_IDX * coordBuffElemSize, - getCoordinationBuffer(), - _HICR_CHANNEL_TAIL_ADVANCE_COUNT_IDX * coordBuffElemSize, - coordBuffElemSize); - getCommunicationManager()->fence(getCoordinationBuffer(), 1, 0); + coordinationCommunicationManager->memcpy(_producerCoordinationBuffer, + _HICR_CHANNEL_TAIL_ADVANCE_COUNT_IDX * coordBuffElemSize, + getCoordinationBuffer(), + _HICR_CHANNEL_TAIL_ADVANCE_COUNT_IDX * coordBuffElemSize, + coordBuffElemSize); + coordinationCommunicationManager->fence(getCoordinationBuffer(), 1, 0); } /** diff --git a/include/hicr/frontends/channel/fixedSize/spsc/producer.hpp b/include/hicr/frontends/channel/fixedSize/spsc/producer.hpp index 87e187af..1aaa3140 100644 --- a/include/hicr/frontends/channel/fixedSize/spsc/producer.hpp +++ b/include/hicr/frontends/channel/fixedSize/spsc/producer.hpp @@ -58,7 +58,8 @@ class Producer : public fixedSize::Base * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] tokenBuffer The memory slot pertaining to the token buffer. The producer will push new * tokens into this buffer, while there is enough space. This buffer should be big enough to hold at least one token. * \param[in] internalCoordinationBuffer This is a small buffer to hold the internal (loca) state of the channel's circular buffer @@ -67,13 +68,14 @@ class Producer : public fixedSize::Base * \param[in] tokenSize The size of each token. * \param[in] capacity The maximum number of tokens that will be held by this channel */ - Producer(CommunicationManager &communicationManager, + Producer(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, std::shared_ptr tokenBuffer, const std::shared_ptr &internalCoordinationBuffer, std::shared_ptr consumerCoordinationBuffer, const size_t tokenSize, const size_t capacity) - : fixedSize::Base(communicationManager, internalCoordinationBuffer, tokenSize, capacity), + : fixedSize::Base(coordinationCommunicationManager, payloadCommunicationManager, internalCoordinationBuffer, tokenSize, capacity), _tokenBuffer(std::move(tokenBuffer)), _consumerCoordinationBuffer(std::move(consumerCoordinationBuffer)) {} @@ -120,16 +122,17 @@ class Producer : public fixedSize::Base HICR_THROW_RUNTIME( "Attempting to push with (%lu) tokens while the channel has (%lu) tokens and this would exceed capacity (%lu).\n", n, curDepth, getCircularBuffer()->getCapacity()); + auto payloadCommunicationManager = getPayloadCommunicationManager(); for (size_t i = 0; i < n; i++) { // Copying with source increasing offset per token - getCommunicationManager()->memcpy(_tokenBuffer, /* destination */ - getTokenSize() * getCircularBuffer()->getHeadPosition(), /* dst_offset */ - sourceSlot, /* source */ - i * getTokenSize(), /* src_offset */ - getTokenSize()); /* size */ + payloadCommunicationManager->memcpy(_tokenBuffer, /* destination */ + getTokenSize() * getCircularBuffer()->getHeadPosition(), /* dst_offset */ + sourceSlot, /* source */ + i * getTokenSize(), /* src_offset */ + getTokenSize()); /* size */ } - getCommunicationManager()->fence(sourceSlot, n, 0); + payloadCommunicationManager->fence(sourceSlot, n, 0); // read possibly slightly outdated depth here (will be updated next round) getCircularBuffer()->advanceHead(n); @@ -140,12 +143,13 @@ class Producer : public fixedSize::Base * This implementation has some advantages for MPSC implementations * on top of SPSC */ - getCommunicationManager()->memcpy(_consumerCoordinationBuffer, - _HICR_CHANNEL_HEAD_ADVANCE_COUNT_IDX * sizeof(size_t), - getCoordinationBuffer(), - _HICR_CHANNEL_HEAD_ADVANCE_COUNT_IDX * sizeof(size_t), - sizeof(size_t)); - getCommunicationManager()->fence(getCoordinationBuffer(), 1, 0); + auto coordinationCommunicationManager = getCoordinationCommunicationManager(); + coordinationCommunicationManager->memcpy(_consumerCoordinationBuffer, + _HICR_CHANNEL_HEAD_ADVANCE_COUNT_IDX * sizeof(size_t), + getCoordinationBuffer(), + _HICR_CHANNEL_HEAD_ADVANCE_COUNT_IDX * sizeof(size_t), + sizeof(size_t)); + coordinationCommunicationManager->fence(getCoordinationBuffer(), 1, 0); } /** @@ -154,7 +158,7 @@ class Producer : public fixedSize::Base __INLINE__ void updateDepth() { // Perform a non-blocking check of the coordination and token buffers, to see and/or notify if there are new messages - getCommunicationManager()->queryMemorySlotUpdates(getCoordinationBuffer()); + getCoordinationCommunicationManager()->queryMemorySlotUpdates(getCoordinationBuffer()); } }; diff --git a/include/hicr/frontends/channel/variableSize/base.hpp b/include/hicr/frontends/channel/variableSize/base.hpp index ed3222cc..30b51966 100644 --- a/include/hicr/frontends/channel/variableSize/base.hpp +++ b/include/hicr/frontends/channel/variableSize/base.hpp @@ -46,7 +46,8 @@ class Base : public channel::Base * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend's memory manager to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] coordinationBufferForCounts This is a small buffer that enables the consumer to signal how many payloads (as a count) it has popped. * \param[in] coordinationBufferForPayloads This is a small buffer that enables the consumer to signal how many bytes from the payload data it has popped. * \param[in] capacity The maximum number of elements (possibly different-sized) that can be held by this channel @@ -55,12 +56,13 @@ class Base : public channel::Base * The key extension to the base channel class is the use of an extended circular buffer instead of a circular buffer. * This is because we need to manage payload head and tail in addition to the head an tail pointers for different elements. */ - Base(CommunicationManager &communicationManager, + Base(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, const std::shared_ptr &coordinationBufferForCounts, const std::shared_ptr &coordinationBufferForPayloads, const size_t capacity, const size_t payloadCapacity) - : channel::Base(communicationManager, coordinationBufferForCounts, sizeof(size_t), capacity), + : channel::Base(coordinationCommunicationManager, payloadCommunicationManager, coordinationBufferForCounts, sizeof(size_t), capacity), _coordinationBufferForCounts(coordinationBufferForCounts), _coordinationBufferForPayloads(coordinationBufferForPayloads) { diff --git a/include/hicr/frontends/channel/variableSize/mpsc/locking/consumer.hpp b/include/hicr/frontends/channel/variableSize/mpsc/locking/consumer.hpp index 7c5cd9b4..2f7c1d4f 100644 --- a/include/hicr/frontends/channel/variableSize/mpsc/locking/consumer.hpp +++ b/include/hicr/frontends/channel/variableSize/mpsc/locking/consumer.hpp @@ -44,7 +44,8 @@ class Consumer final : public variableSize::Base * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] payloadBuffer The memory slot pertaining to the payload buffer. The producer will push new tokens * into this buffer, while there is enough space (in bytes). This buffer should be big enough to hold at least the * largest message of the variable-sized messages to be pushed. @@ -62,7 +63,8 @@ class Consumer final : public variableSize::Base * \param[in] capacity The maximum number of tokens that will be held by this channel * @note: The token size in var-size channels is used only internally, and is passed as having a type size_t (with size sizeof(size_t)) */ - Consumer(CommunicationManager &communicationManager, + Consumer(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, std::shared_ptr payloadBuffer, std::shared_ptr tokenBuffer, const std::shared_ptr &internalCoordinationBufferForCounts, @@ -71,7 +73,12 @@ class Consumer final : public variableSize::Base std::shared_ptr consumerCoordinationBufferForPayloads, const size_t payloadCapacity, const size_t capacity) - : variableSize::Base(communicationManager, internalCoordinationBufferForCounts, internalCoordinationBufferForPayloads, capacity, payloadCapacity), + : variableSize::Base(coordinationCommunicationManager, + payloadCommunicationManager, + internalCoordinationBufferForCounts, + internalCoordinationBufferForPayloads, + capacity, + payloadCapacity), _payloadBuffer(std::move(payloadBuffer)), _tokenSizeBuffer(std::move(tokenBuffer)), _consumerCoordinationBufferForCounts(consumerCoordinationBufferForCounts), @@ -80,8 +87,8 @@ class Consumer final : public variableSize::Base assert(internalCoordinationBufferForCounts != nullptr); assert(internalCoordinationBufferForPayloads != nullptr); assert(consumerCoordinationBufferForCounts != nullptr); - getCommunicationManager()->queryMemorySlotUpdates(_tokenSizeBuffer->getSourceLocalMemorySlot()); - getCommunicationManager()->queryMemorySlotUpdates(_payloadBuffer->getSourceLocalMemorySlot()); + getCoordinationCommunicationManager()->queryMemorySlotUpdates(_tokenSizeBuffer->getSourceLocalMemorySlot()); + getPayloadCommunicationManager()->queryMemorySlotUpdates(_payloadBuffer->getSourceLocalMemorySlot()); } /** @@ -136,7 +143,8 @@ class Consumer final : public variableSize::Base */ __INLINE__ std::array peek(const size_t pos = 0) { - getCommunicationManager()->flushReceived(); + getCoordinationCommunicationManager()->flushReceived(); + getPayloadCommunicationManager()->flushReceived(); std::array result{}; if (pos != 0) { HICR_THROW_FATAL("peek only implemented for n = 0 at the moment!"); } if (pos >= getCircularBufferForCounts()->getDepth()) @@ -211,8 +219,10 @@ class Consumer final : public variableSize::Base { bool successFlag = false; + auto coordinationCommunicationManager = getCoordinationCommunicationManager(); + // Locking remote coordination buffer slot - if (getCommunicationManager()->acquireGlobalLock(_consumerCoordinationBufferForCounts) == false) return successFlag; + if (coordinationCommunicationManager->acquireGlobalLock(_consumerCoordinationBufferForCounts) == false) return successFlag; if (n > getCircularBufferForCounts()->getCapacity()) HICR_THROW_LOGIC("Attempting to pop (%lu) tokens, which is larger than the channel capacity (%lu)", n, getCircularBufferForCounts()->getCapacity()); @@ -227,7 +237,7 @@ class Consumer final : public variableSize::Base getCircularBufferForCounts()->advanceTail(n); getCircularBufferForPayloads()->advanceTail(bytesOldestEntry); - getCommunicationManager()->releaseGlobalLock(_consumerCoordinationBufferForCounts); + coordinationCommunicationManager->releaseGlobalLock(_consumerCoordinationBufferForCounts); successFlag = true; return successFlag; } diff --git a/include/hicr/frontends/channel/variableSize/mpsc/locking/producer.hpp b/include/hicr/frontends/channel/variableSize/mpsc/locking/producer.hpp index bb92fe2b..786b6905 100644 --- a/include/hicr/frontends/channel/variableSize/mpsc/locking/producer.hpp +++ b/include/hicr/frontends/channel/variableSize/mpsc/locking/producer.hpp @@ -42,9 +42,8 @@ class Producer final : public variableSize::Base /** * The constructor of the variable-sized producer channel class. * - * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. - * - * \param[in] communicationManager The backend to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] sizeInfoBuffer The local memory slot used to hold the information about the next message size * \param[in] payloadBuffer The global memory slot pertaining to the payload of all messages. The producer will push messages into this * buffer, while there is enough space. This buffer should be big enough to hold at least the largest of the variable-size messages. @@ -59,7 +58,8 @@ class Producer final : public variableSize::Base * \param[in] payloadSize size in bytes of the datatype used for variable-sized messages * \param[in] capacity The maximum number of tokens that will be held by this channel */ - Producer(CommunicationManager &communicationManager, + Producer(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, std::shared_ptr sizeInfoBuffer, std::shared_ptr payloadBuffer, std::shared_ptr tokenBuffer, @@ -70,7 +70,12 @@ class Producer final : public variableSize::Base const size_t payloadCapacity, const size_t payloadSize, const size_t capacity) - : variableSize::Base(communicationManager, internalCoordinationBufferForCounts, internalCoordinationBufferForPayloads, capacity, payloadCapacity), + : variableSize::Base(coordinationCommunicationManager, + payloadCommunicationManager, + internalCoordinationBufferForCounts, + internalCoordinationBufferForPayloads, + capacity, + payloadCapacity), _payloadBuffer(std::move(payloadBuffer)), _sizeInfoBuffer(std::move(sizeInfoBuffer)), _payloadSize(payloadSize), @@ -87,20 +92,21 @@ class Producer final : public variableSize::Base */ __INLINE__ void updateDepth() // NOTE: we DO know we have the lock!!!! { - getCommunicationManager()->memcpy(getCoordinationBufferForCounts(), /* destination */ - 0, /* dst_offset */ - _consumerCoordinationBufferForCounts, /* source */ - 0, /* src_offset */ - 2 * sizeof(_HICR_CHANNEL_COORDINATION_BUFFER_ELEMENT_TYPE)); /* size */ - - getCommunicationManager()->memcpy(getCoordinationBufferForPayloads(), /* destination */ - 0, /* dst_offset */ - _consumerCoordinationBufferForPayloads, /* source */ - 0, /* src_offset */ - 2 * sizeof(_HICR_CHANNEL_COORDINATION_BUFFER_ELEMENT_TYPE)); /* size */ - - getCommunicationManager()->fence(getCoordinationBufferForCounts(), 0, 1); - getCommunicationManager()->fence(getCoordinationBufferForPayloads(), 0, 1); + auto coordinationCommunicationManager = getCoordinationCommunicationManager(); + coordinationCommunicationManager->memcpy(getCoordinationBufferForCounts(), /* destination */ + 0, /* dst_offset */ + _consumerCoordinationBufferForCounts, /* source */ + 0, /* src_offset */ + 2 * sizeof(_HICR_CHANNEL_COORDINATION_BUFFER_ELEMENT_TYPE)); /* size */ + + coordinationCommunicationManager->memcpy(getCoordinationBufferForPayloads(), /* destination */ + 0, /* dst_offset */ + _consumerCoordinationBufferForPayloads, /* source */ + 0, /* src_offset */ + 2 * sizeof(_HICR_CHANNEL_COORDINATION_BUFFER_ELEMENT_TYPE)); /* size */ + + coordinationCommunicationManager->fence(getCoordinationBufferForCounts(), 0, 1); + coordinationCommunicationManager->fence(getCoordinationBufferForPayloads(), 0, 1); /** * Now we know the exact buffer state at the consumer */ @@ -146,8 +152,10 @@ class Producer final : public variableSize::Base // Flag to record whether the operation was successful or not (it simplifies code by releasing locks only once) bool successFlag = false; + auto coordinationCommunicationManager = getCoordinationCommunicationManager(); + // Locking remote token and coordination buffer slots - if (getCommunicationManager()->acquireGlobalLock(_consumerCoordinationBufferForCounts) == false) return successFlag; + if (coordinationCommunicationManager->acquireGlobalLock(_consumerCoordinationBufferForCounts) == false) return successFlag; // Updating depth of token (message sizes) and payload buffers from the consumer updateDepth(); @@ -157,7 +165,7 @@ class Producer final : public variableSize::Base // If not, reject the operation if (getCircularBufferForPayloads()->getDepth() + requiredBufferSize > getCircularBufferForPayloads()->getCapacity()) { - getCommunicationManager()->releaseGlobalLock(_consumerCoordinationBufferForCounts); + coordinationCommunicationManager->releaseGlobalLock(_consumerCoordinationBufferForCounts); return successFlag; } @@ -167,7 +175,7 @@ class Producer final : public variableSize::Base // Check if the consumer buffer has n free slots. If not, reject the operation if (getCircularBufferForCounts()->getDepth() + 1 > getCircularBufferForCounts()->getCapacity()) { - getCommunicationManager()->releaseGlobalLock(_consumerCoordinationBufferForCounts); + coordinationCommunicationManager->releaseGlobalLock(_consumerCoordinationBufferForCounts); return successFlag; } @@ -175,14 +183,16 @@ class Producer final : public variableSize::Base * Phase 1: Update the size (in bytes) of the pending payload * at the consumer */ - getCommunicationManager()->memcpy(_tokenSizeBuffer, /* destination */ - getTokenSize() * getCircularBufferForCounts()->getHeadPosition(), /* dst_offset */ - _sizeInfoBuffer, /* source */ - 0, /* src_offset */ - getTokenSize()); /* size */ - getCommunicationManager()->fence(_sizeInfoBuffer, 1, 0); + coordinationCommunicationManager->memcpy(_tokenSizeBuffer, /* destination */ + getTokenSize() * getCircularBufferForCounts()->getHeadPosition(), /* dst_offset */ + _sizeInfoBuffer, /* source */ + 0, /* src_offset */ + getTokenSize()); /* size */ + coordinationCommunicationManager->fence(_sizeInfoBuffer, 1, 0); successFlag = true; + auto payloadCommunicationManager = getPayloadCommunicationManager(); + /** * Phase 2: Payload copy: * - We have checked (requiredBufferSize <= depth) @@ -196,24 +206,24 @@ class Producer final : public variableSize::Base size_t first_chunk = getCircularBufferForPayloads()->getCapacity() - getCircularBufferForPayloads()->getHeadPosition(); size_t second_chunk = requiredBufferSize - first_chunk; // copy first part to end of buffer - getCommunicationManager()->memcpy(_payloadBuffer, /* destination */ - getCircularBufferForPayloads()->getHeadPosition(), /* dst_offset */ - sourceSlot, /* source */ - 0, /* src_offset */ - first_chunk); /* size */ + payloadCommunicationManager->memcpy(_payloadBuffer, /* destination */ + getCircularBufferForPayloads()->getHeadPosition(), /* dst_offset */ + sourceSlot, /* source */ + 0, /* src_offset */ + first_chunk); /* size */ // copy second part to beginning of buffer - getCommunicationManager()->memcpy(_payloadBuffer, /* destination */ - 0, /* dst_offset */ - sourceSlot, /* source */ - first_chunk, /* src_offset */ - second_chunk); /* size */ + payloadCommunicationManager->memcpy(_payloadBuffer, /* destination */ + 0, /* dst_offset */ + sourceSlot, /* source */ + first_chunk, /* src_offset */ + second_chunk); /* size */ - getCommunicationManager()->fence(sourceSlot, 2, 0); + payloadCommunicationManager->fence(sourceSlot, 2, 0); } else { - getCommunicationManager()->memcpy(_payloadBuffer, getCircularBufferForPayloads()->getHeadPosition(), sourceSlot, 0, requiredBufferSize); - getCommunicationManager()->fence(sourceSlot, 1, 0); + payloadCommunicationManager->memcpy(_payloadBuffer, getCircularBufferForPayloads()->getHeadPosition(), sourceSlot, 0, requiredBufferSize); + payloadCommunicationManager->fence(sourceSlot, 1, 0); } // Remotely push an element into consumer side, updating consumer head indices @@ -221,14 +231,15 @@ class Producer final : public variableSize::Base getCircularBufferForPayloads()->advanceHead(requiredBufferSize); // only update head index at consumer (byte size = one buffer element) - getCommunicationManager()->memcpy(_consumerCoordinationBufferForCounts, 0, getCoordinationBufferForCounts(), 0, sizeof(_HICR_CHANNEL_COORDINATION_BUFFER_ELEMENT_TYPE)); + coordinationCommunicationManager->memcpy(_consumerCoordinationBufferForCounts, 0, getCoordinationBufferForCounts(), 0, sizeof(_HICR_CHANNEL_COORDINATION_BUFFER_ELEMENT_TYPE)); // only update head index at consumer (byte size = one buffer element) - getCommunicationManager()->memcpy(_consumerCoordinationBufferForPayloads, 0, getCoordinationBufferForPayloads(), 0, sizeof(_HICR_CHANNEL_COORDINATION_BUFFER_ELEMENT_TYPE)); + coordinationCommunicationManager->memcpy( + _consumerCoordinationBufferForPayloads, 0, getCoordinationBufferForPayloads(), 0, sizeof(_HICR_CHANNEL_COORDINATION_BUFFER_ELEMENT_TYPE)); // backend LPF needs this to complete - getCommunicationManager()->fence(getCoordinationBufferForCounts(), 1, 0); - getCommunicationManager()->fence(getCoordinationBufferForPayloads(), 1, 0); + coordinationCommunicationManager->fence(getCoordinationBufferForCounts(), 1, 0); + coordinationCommunicationManager->fence(getCoordinationBufferForPayloads(), 1, 0); - getCommunicationManager()->releaseGlobalLock(_consumerCoordinationBufferForCounts); + coordinationCommunicationManager->releaseGlobalLock(_consumerCoordinationBufferForCounts); return successFlag; } diff --git a/include/hicr/frontends/channel/variableSize/mpsc/nonlocking/consumer.hpp b/include/hicr/frontends/channel/variableSize/mpsc/nonlocking/consumer.hpp index b1318b4c..31b2c291 100644 --- a/include/hicr/frontends/channel/variableSize/mpsc/nonlocking/consumer.hpp +++ b/include/hicr/frontends/channel/variableSize/mpsc/nonlocking/consumer.hpp @@ -42,7 +42,8 @@ class Consumer * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] payloadBuffers The list of memory slots pertaining to the payload buffers. The producers will push new messages * into these buffers as long as space allows. This buffer should be large enough to hold at least the * largest message of the variable-sized messages to be pushed. @@ -61,7 +62,8 @@ class Consumer * \param[in] capacity The maximum number of tokens that will be held by this channel * @note: The token size in var-size channels is used only internally, and is passed as having a type size_t (with size sizeof(size_t)) */ - Consumer(CommunicationManager &communicationManager, + Consumer(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, const std::vector> &payloadBuffers, const std::vector> &tokenBuffers, const std::vector> &internalCoordinationBufferForCounts, @@ -71,7 +73,8 @@ class Consumer const size_t payloadCapacity, const size_t payloadSize, const size_t capacity) - : _communicationManager(&communicationManager) + : _coordinationCommunicationManager(&coordinationCommunicationManager), + _payloadCommunicationManager(&payloadCommunicationManager) { // make sure producer and consumer sides have the same element size // the size is hopefully the producer count @@ -84,7 +87,8 @@ class Consumer // create p (= number of producers) SPSC channels for (size_t i = 0; i < producerCount; i++) { - std::shared_ptr consumerPtr(new variableSize::SPSC::Consumer(communicationManager, + std::shared_ptr consumerPtr(new variableSize::SPSC::Consumer(coordinationCommunicationManager, + payloadCommunicationManager, payloadBuffers[i], tokenBuffers[i], internalCoordinationBufferForCounts[i], @@ -129,7 +133,8 @@ class Consumer // be of type std::vector instead of std::queue if (pos > 0) HICR_THROW_LOGIC("Nonblocking MPSC not yet implemented for peek with n!=0"); - _communicationManager->flushReceived(); + _coordinationCommunicationManager->flushReceived(); + _payloadCommunicationManager->flushReceived(); updateDepth(); if (_channelPushes.empty()) HICR_THROW_RUNTIME("Attempting to peek position (%lu) but supporting queue has size (%lu)", pos, _channelPushes.size()); @@ -244,10 +249,16 @@ class Consumer * A snapshot of the last recorded depths in all SPSC channels (initialized with 0s) */ std::vector _depths; + + /** + * Pointer to the backend that is in charge of updating coordination buffers + */ + CommunicationManager *const _coordinationCommunicationManager; + /** * Pointer to the backend that is in charge of executing the memory transfer operations */ - CommunicationManager *const _communicationManager; + CommunicationManager *const _payloadCommunicationManager; }; } // namespace HiCR::channel::variableSize::MPSC::nonlocking diff --git a/include/hicr/frontends/channel/variableSize/mpsc/nonlocking/producer.hpp b/include/hicr/frontends/channel/variableSize/mpsc/nonlocking/producer.hpp index 04d846c4..5cf1de6d 100644 --- a/include/hicr/frontends/channel/variableSize/mpsc/nonlocking/producer.hpp +++ b/include/hicr/frontends/channel/variableSize/mpsc/nonlocking/producer.hpp @@ -44,7 +44,8 @@ class Producer final : public variableSize::SPSC::Producer * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] sizeInfoBuffer The local memory slot used to hold the information about the next message size * \param[in] payloadBuffer The global memory slot pertaining to the payload of all messages. The producer will push messages into this * buffer, while there is enough space. This buffer should be large enough to hold at least the largest of the variable-size messages. @@ -59,7 +60,8 @@ class Producer final : public variableSize::SPSC::Producer * \param[in] payloadSize size in bytes of the datatype used for variable-sized messages * \param[in] capacity The maximum number of tokens that will be held by this channel */ - Producer(CommunicationManager &communicationManager, + Producer(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, std::shared_ptr sizeInfoBuffer, std::shared_ptr payloadBuffer, std::shared_ptr tokenBuffer, @@ -70,7 +72,8 @@ class Producer final : public variableSize::SPSC::Producer const size_t payloadCapacity, const size_t payloadSize, const size_t capacity) - : variableSize::SPSC::Producer(communicationManager, + : variableSize::SPSC::Producer(coordinationCommunicationManager, + payloadCommunicationManager, std::move(sizeInfoBuffer), std::move(payloadBuffer), std::move(tokenBuffer), diff --git a/include/hicr/frontends/channel/variableSize/spsc/consumer.hpp b/include/hicr/frontends/channel/variableSize/spsc/consumer.hpp index ca981b14..d04b20ea 100644 --- a/include/hicr/frontends/channel/variableSize/spsc/consumer.hpp +++ b/include/hicr/frontends/channel/variableSize/spsc/consumer.hpp @@ -47,7 +47,8 @@ class Consumer final : public variableSize::Base * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] payloadBuffer The memory slot pertaining to the payload buffer. The producer will push new tokens * into this buffer, while there is enough space (in bytes). This buffer should be big enough to hold at least the * largest message of the variable-sized messages to be pushed. @@ -65,7 +66,8 @@ class Consumer final : public variableSize::Base * \param[in] capacity The maximum number of tokens that will be held by this channel * @note: The token size in var-size channels is used only internally, and is passed as having a type size_t (with size sizeof(size_t)) */ - Consumer(CommunicationManager &communicationManager, + Consumer(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, std::shared_ptr payloadBuffer, std::shared_ptr tokenBuffer, const std::shared_ptr &internalCoordinationBufferForCounts, @@ -74,7 +76,12 @@ class Consumer final : public variableSize::Base std::shared_ptr producerCoordinationBufferForPayloads, const size_t payloadCapacity, const size_t capacity) - : variableSize::Base(communicationManager, internalCoordinationBufferForCounts, internalCoordinationBufferForPayloads, capacity, payloadCapacity), + : variableSize::Base(coordinationCommunicationManager, + payloadCommunicationManager, + internalCoordinationBufferForCounts, + internalCoordinationBufferForPayloads, + capacity, + payloadCapacity), _payloadBuffer(std::move(payloadBuffer)), _tokenSizeBuffer(std::move(tokenBuffer)), @@ -201,22 +208,24 @@ class Consumer final : public variableSize::Base getCircularBufferForCounts()->advanceTail(n); getCircularBufferForPayloads()->advanceTail(payloadBytes); + auto coordinationCommunicationManager = getCoordinationCommunicationManager(); + const auto coordBuffElemSize = sizeof(_HICR_CHANNEL_COORDINATION_BUFFER_ELEMENT_TYPE); // Notifying producer(s) of buffer liberation - getCommunicationManager()->memcpy(_producerCoordinationBufferForCounts, /* destination */ - _HICR_CHANNEL_TAIL_ADVANCE_COUNT_IDX * coordBuffElemSize, - getCoordinationBufferForCounts(), - _HICR_CHANNEL_TAIL_ADVANCE_COUNT_IDX * coordBuffElemSize, - coordBuffElemSize); - - getCommunicationManager()->memcpy(_producerCoordinationBufferForPayloads, /* destination */ - _HICR_CHANNEL_TAIL_ADVANCE_COUNT_IDX * coordBuffElemSize, - getCoordinationBufferForPayloads(), /* source */ - _HICR_CHANNEL_TAIL_ADVANCE_COUNT_IDX * coordBuffElemSize, - coordBuffElemSize); - - getCommunicationManager()->fence(getCoordinationBufferForCounts(), 1, 0); - getCommunicationManager()->fence(getCoordinationBufferForPayloads(), 1, 0); + coordinationCommunicationManager->memcpy(_producerCoordinationBufferForCounts, /* destination */ + _HICR_CHANNEL_TAIL_ADVANCE_COUNT_IDX * coordBuffElemSize, + getCoordinationBufferForCounts(), + _HICR_CHANNEL_TAIL_ADVANCE_COUNT_IDX * coordBuffElemSize, + coordBuffElemSize); + + coordinationCommunicationManager->memcpy(_producerCoordinationBufferForPayloads, /* destination */ + _HICR_CHANNEL_TAIL_ADVANCE_COUNT_IDX * coordBuffElemSize, + getCoordinationBufferForPayloads(), /* source */ + _HICR_CHANNEL_TAIL_ADVANCE_COUNT_IDX * coordBuffElemSize, + coordBuffElemSize); + + coordinationCommunicationManager->fence(getCoordinationBufferForCounts(), 1, 0); + coordinationCommunicationManager->fence(getCoordinationBufferForPayloads(), 1, 0); } /** diff --git a/include/hicr/frontends/channel/variableSize/spsc/producer.hpp b/include/hicr/frontends/channel/variableSize/spsc/producer.hpp index 80065622..94ae8f0a 100644 --- a/include/hicr/frontends/channel/variableSize/spsc/producer.hpp +++ b/include/hicr/frontends/channel/variableSize/spsc/producer.hpp @@ -44,7 +44,8 @@ class Producer : public variableSize::Base * * It requires the user to provide the allocated memory slots for the exchange (data) and coordination buffers. * - * \param[in] communicationManager The backend to facilitate communication between the producer and consumer sides + * \param[in] coordinationCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer coordination buffers + * \param[in] payloadCommunicationManager The backend's memory manager to facilitate communication between the producer and consumer payload buffers * \param[in] sizeInfoBuffer The local memory slot used to hold the information about the next message size * \param[in] payloadBuffer The global memory slot pertaining to the payload of all messages. The producer will push messages into this * buffer, while there is enough space. This buffer should be large enough to hold at least the largest of the variable-size messages. @@ -59,7 +60,8 @@ class Producer : public variableSize::Base * \param[in] payloadSize size in bytes of the datatype used for variable-sized messages * \param[in] capacity The maximum number of tokens that will be held by this channel */ - Producer(CommunicationManager &communicationManager, + Producer(CommunicationManager &coordinationCommunicationManager, + CommunicationManager &payloadCommunicationManager, std::shared_ptr sizeInfoBuffer, std::shared_ptr payloadBuffer, std::shared_ptr tokenBuffer, @@ -70,7 +72,12 @@ class Producer : public variableSize::Base const size_t payloadCapacity, const size_t payloadSize, const size_t capacity) - : variableSize::Base(communicationManager, internalCoordinationBufferForCounts, internalCoordinationBufferForPayloads, capacity, payloadCapacity), + : variableSize::Base(coordinationCommunicationManager, + payloadCommunicationManager, + internalCoordinationBufferForCounts, + internalCoordinationBufferForPayloads, + capacity, + payloadCapacity), _payloadBuffer(std::move(payloadBuffer)), _sizeInfoBuffer(std::move(sizeInfoBuffer)), _payloadSize(payloadSize), @@ -156,6 +163,9 @@ class Producer : public variableSize::Base currentPayloadDepth, providedBufferCapacity); + // Get communication managers + auto payloadCommunicationManager = getPayloadCommunicationManager(); + /* * Payload copy: * - We have checked (requiredBufferSize <= depth) @@ -169,35 +179,37 @@ class Producer : public variableSize::Base size_t second_chunk = requiredBufferSize - first_chunk; // copy first part to end of buffer - getCommunicationManager()->memcpy(_payloadBuffer, /* destination */ - getPayloadHeadPosition(), /* dst_offset */ - sourceSlot, /* source */ - 0, /* src_offset */ - first_chunk); /* size */ + payloadCommunicationManager->memcpy(_payloadBuffer, /* destination */ + getPayloadHeadPosition(), /* dst_offset */ + sourceSlot, /* source */ + 0, /* src_offset */ + first_chunk); /* size */ // copy second part to beginning of buffer - getCommunicationManager()->memcpy(_payloadBuffer, /* destination */ - 0, /* dst_offset */ - sourceSlot, /* source */ - first_chunk, /* src_offset */ - second_chunk); /* size */ - getCommunicationManager()->fence(sourceSlot, 2, 0); + payloadCommunicationManager->memcpy(_payloadBuffer, /* destination */ + 0, /* dst_offset */ + sourceSlot, /* source */ + first_chunk, /* src_offset */ + second_chunk); /* size */ + payloadCommunicationManager->fence(sourceSlot, 2, 0); } else { - getCommunicationManager()->memcpy(_payloadBuffer, getPayloadHeadPosition(), sourceSlot, 0, requiredBufferSize); - getCommunicationManager()->fence(sourceSlot, 1, 0); + payloadCommunicationManager->memcpy(_payloadBuffer, getPayloadHeadPosition(), sourceSlot, 0, requiredBufferSize); + payloadCommunicationManager->fence(sourceSlot, 1, 0); } getCircularBufferForPayloads()->advanceHead(requiredBufferSize); + auto coordinationCommunicationManager = getCoordinationCommunicationManager(); + // update the consumer coordination buffers (consumer does not update // its own coordination head positions) - getCommunicationManager()->memcpy(_consumerCoordinationBufferForPayloads, - _HICR_CHANNEL_HEAD_ADVANCE_COUNT_IDX * sizeof(size_t), - getCoordinationBufferForPayloads(), - _HICR_CHANNEL_HEAD_ADVANCE_COUNT_IDX * sizeof(size_t), - sizeof(size_t)); - getCommunicationManager()->fence(getCoordinationBufferForPayloads(), 1, 0); + coordinationCommunicationManager->memcpy(_consumerCoordinationBufferForPayloads, + _HICR_CHANNEL_HEAD_ADVANCE_COUNT_IDX * sizeof(size_t), + getCoordinationBufferForPayloads(), + _HICR_CHANNEL_HEAD_ADVANCE_COUNT_IDX * sizeof(size_t), + sizeof(size_t)); + coordinationCommunicationManager->fence(getCoordinationBufferForPayloads(), 1, 0); /* * Part 2: Copy the message size @@ -213,20 +225,20 @@ class Producer : public variableSize::Base getDepth(), getCircularBufferForCounts()->getCapacity()); - getCommunicationManager()->memcpy(_tokenBuffer, /* destination */ - getTokenSize() * getCircularBufferForCounts()->getHeadPosition(), /* dst_offset */ - _sizeInfoBuffer, /* source */ - 0, /* src_offset */ - getTokenSize()); /* size */ - getCommunicationManager()->fence(_sizeInfoBuffer, 1, 0); + coordinationCommunicationManager->memcpy(_tokenBuffer, /* destination */ + getTokenSize() * getCircularBufferForCounts()->getHeadPosition(), /* dst_offset */ + _sizeInfoBuffer, /* source */ + 0, /* src_offset */ + getTokenSize()); /* size */ + coordinationCommunicationManager->fence(_sizeInfoBuffer, 1, 0); getCircularBufferForCounts()->advanceHead(1); - getCommunicationManager()->memcpy(_consumerCoordinationBufferForCounts, - _HICR_CHANNEL_HEAD_ADVANCE_COUNT_IDX * sizeof(size_t), - getCoordinationBufferForCounts(), - _HICR_CHANNEL_HEAD_ADVANCE_COUNT_IDX * sizeof(size_t), - sizeof(size_t)); - getCommunicationManager()->fence(getCoordinationBufferForCounts(), 1, 0); + coordinationCommunicationManager->memcpy(_consumerCoordinationBufferForCounts, + _HICR_CHANNEL_HEAD_ADVANCE_COUNT_IDX * sizeof(size_t), + getCoordinationBufferForCounts(), + _HICR_CHANNEL_HEAD_ADVANCE_COUNT_IDX * sizeof(size_t), + sizeof(size_t)); + coordinationCommunicationManager->fence(getCoordinationBufferForCounts(), 1, 0); } /** diff --git a/tests/frontends/channel/fixedSize/spsc/consumer.cpp b/tests/frontends/channel/fixedSize/spsc/consumer.cpp index 81f2282b..de644c5e 100644 --- a/tests/frontends/channel/fixedSize/spsc/consumer.cpp +++ b/tests/frontends/channel/fixedSize/spsc/consumer.cpp @@ -102,13 +102,13 @@ TEST(ConsumerChannel, Construction) auto badGlobalTokenBuffer = c.getGlobalMemorySlot(CHANNEL_TAG, BAD_TOKEN_BUFFER_KEY); // Creating with incorrect parameters - EXPECT_THROW(new HiCR::channel::fixedSize::SPSC::Consumer(c, globalTokenBuffer, badConsumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity), + EXPECT_THROW(new HiCR::channel::fixedSize::SPSC::Consumer(c, c, globalTokenBuffer, badConsumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity), HiCR::LogicException); - EXPECT_THROW(new HiCR::channel::fixedSize::SPSC::Consumer(c, badGlobalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity), + EXPECT_THROW(new HiCR::channel::fixedSize::SPSC::Consumer(c, c, badGlobalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity), HiCR::LogicException); // Creating with correct parameters - EXPECT_NO_THROW(new HiCR::channel::fixedSize::SPSC::Consumer(c, globalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity)); + EXPECT_NO_THROW(new HiCR::channel::fixedSize::SPSC::Consumer(c, c, globalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity)); } TEST(ConsumerChannel, PeekPop) @@ -167,8 +167,8 @@ TEST(ConsumerChannel, PeekPop) auto globalConsumerCoordinationBuffer = c.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); // Creating producer and Consumer channels - HiCR::channel::fixedSize::SPSC::Producer producer(c, globalTokenBuffer, producerCoordinationBuffer, globalConsumerCoordinationBuffer, tokenSize, channelCapacity); - HiCR::channel::fixedSize::SPSC::Consumer consumer(c, globalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity); + HiCR::channel::fixedSize::SPSC::Producer producer(c, c, globalTokenBuffer, producerCoordinationBuffer, globalConsumerCoordinationBuffer, tokenSize, channelCapacity); + HiCR::channel::fixedSize::SPSC::Consumer consumer(c, c, globalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity); // Creating send buffer auto sendBufferCapacity = channelCapacity + 1; @@ -260,8 +260,8 @@ TEST(ConsumerChannel, PeekOrderPop) auto globalConsumerCoordinationBuffer = c.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); // Creating producer and Consumer channels - HiCR::channel::fixedSize::SPSC::Producer producer(c, globalTokenBuffer, producerCoordinationBuffer, globalConsumerCoordinationBuffer, tokenSize, channelCapacity); - HiCR::channel::fixedSize::SPSC::Consumer consumer(c, globalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity); + HiCR::channel::fixedSize::SPSC::Producer producer(c, c, globalTokenBuffer, producerCoordinationBuffer, globalConsumerCoordinationBuffer, tokenSize, channelCapacity); + HiCR::channel::fixedSize::SPSC::Consumer consumer(c, c, globalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity); // Send tokens for (int i = 0; i < channelCapacity; i++) @@ -336,8 +336,8 @@ TEST(ConsumerChannel, PeekOrder) auto globalConsumerCoordinationBuffer = c.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); // Creating producer and Consumer channels - HiCR::channel::fixedSize::SPSC::Producer producer(c, globalTokenBuffer, producerCoordinationBuffer, globalConsumerCoordinationBuffer, tokenSize, channelCapacity); - HiCR::channel::fixedSize::SPSC::Consumer consumer(c, globalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity); + HiCR::channel::fixedSize::SPSC::Producer producer(c, c, globalTokenBuffer, producerCoordinationBuffer, globalConsumerCoordinationBuffer, tokenSize, channelCapacity); + HiCR::channel::fixedSize::SPSC::Consumer consumer(c, c, globalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity); // Send tokens for (int i = 0; i < channelCapacity; i++) @@ -411,8 +411,8 @@ TEST(ConsumerChannel, PeekWait) auto globalConsumerCoordinationBuffer = c.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); // Creating channel - HiCR::channel::fixedSize::SPSC::Producer producer(c, globalTokenBuffer, producerCoordinationBuffer, globalConsumerCoordinationBuffer, tokenSize, channelCapacity); - HiCR::channel::fixedSize::SPSC::Consumer consumer(c, globalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity); + HiCR::channel::fixedSize::SPSC::Producer producer(c, c, globalTokenBuffer, producerCoordinationBuffer, globalConsumerCoordinationBuffer, tokenSize, channelCapacity); + HiCR::channel::fixedSize::SPSC::Consumer consumer(c, c, globalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity); // Attempting to push more tokens than channel size (should throw exception) EXPECT_THROW(consumer.peek(channelCapacity + 1), HiCR::LogicException); diff --git a/tests/frontends/channel/fixedSize/spsc/producer.cpp b/tests/frontends/channel/fixedSize/spsc/producer.cpp index a57b42f6..aa96a87f 100644 --- a/tests/frontends/channel/fixedSize/spsc/producer.cpp +++ b/tests/frontends/channel/fixedSize/spsc/producer.cpp @@ -96,12 +96,12 @@ TEST(ProducerChannel, Construction) auto globalConsumerCoordinationBuffer = c.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); // Creating with incorrect parameters - EXPECT_THROW(new HiCR::channel::fixedSize::SPSC::Producer(c, globalTokenBuffer, badCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity), + EXPECT_THROW(new HiCR::channel::fixedSize::SPSC::Producer(c, c, globalTokenBuffer, badCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity), HiCR::LogicException); // Creating with correct parameters EXPECT_NO_THROW( - new HiCR::channel::fixedSize::SPSC::Producer(c, globalTokenBuffer, correctProducerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity)); + new HiCR::channel::fixedSize::SPSC::Producer(c, c, globalTokenBuffer, correctProducerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity)); } TEST(ProducerChannel, Push) @@ -161,7 +161,7 @@ TEST(ProducerChannel, Push) auto globalConsumerCoordinationBuffer = c.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); // Creating producer channel - HiCR::channel::fixedSize::SPSC::Producer producer(c, globalTokenBuffer, producerCoordinationBuffer, globalConsumerCoordinationBuffer, tokenSize, channelCapacity); + HiCR::channel::fixedSize::SPSC::Producer producer(c, c, globalTokenBuffer, producerCoordinationBuffer, globalConsumerCoordinationBuffer, tokenSize, channelCapacity); // Creating send buffer auto sendBufferCapacity = channelCapacity + 1; @@ -249,8 +249,8 @@ TEST(ProducerChannel, PushWait) auto globalConsumerCoordinationBuffer = c.getGlobalMemorySlot(CHANNEL_TAG, CONSUMER_COORDINATION_BUFFER_KEY); // Creating producer and consumer channels - HiCR::channel::fixedSize::SPSC::Producer producer(c, globalTokenBuffer, producerCoordinationBuffer, globalConsumerCoordinationBuffer, tokenSize, channelCapacity); - HiCR::channel::fixedSize::SPSC::Consumer consumer(c, globalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity); + HiCR::channel::fixedSize::SPSC::Producer producer(c, c, globalTokenBuffer, producerCoordinationBuffer, globalConsumerCoordinationBuffer, tokenSize, channelCapacity); + HiCR::channel::fixedSize::SPSC::Consumer consumer(c, c, globalTokenBuffer, consumerCoordinationBuffer, globalProducerCoordinationBuffer, tokenSize, channelCapacity); // Creating send buffer auto sendBufferCapacity = channelCapacity + 1;