Memory Operations

After allocating buffers, the next step is moving or initializing data inside them. One of the most commonly used memory operations is the copy operation, which copies data from one buffer to another. All memory operations support any dimension >=1.

  • alpaka::onHost::memcpy() always works with the entire buffer unless you specify the extent. The extent defines the number of elements, not the size in bytes.

    // copy the data to the host to the device
    onHost::memcpy(asyncComputeQueue, hostBuffer, computeBuffer);
    

    The next snippet copies only the first four elements.

    onHost::memcpy(asyncComputeQueue, hostBuffer, computeBuffer, Vec{4u});
    
  • You can also set all values of a buffer to a specific value using alpaka::onHost::fill().

    onHost::fill(asyncComputeQueue, computeBuffer, 42);
    
  • With alpaka::onHost::memset(), all bytes of a buffer can be set to a specific byte value. This is typically used to set all bytes to zero. Attention: The optional extent still defines the number of elements and not the size in bytes.

    onHost::memset(hostQueue, hostBuffer, 0);
    

    The value_type is int, and we want to set the bytes of the first four elements to 0. On x86_64, an int has the size of 4 bytes. So in the example, the first 16 bytes of the buffer are set to 0.

    // the value type of the buffer is int, which has size of 4 bytes on an x86_64 architecture
    static_assert(std::same_as<typename ALPAKA_TYPEOF(hostBuffer)::value_type, int>);
    onHost::memset(hostQueue, hostBuffer, 0, Vec{4u});
    
  • alpaka also supports std::vector for memory operations; during the call, a view to the vector data is created automatically.

    // use std::vector instead of an alpaka view
    std::vector stdVec = std::vector<int>(10, 0);
    
    onHost::fill(asyncComputeQueue, computeBuffer, 42);
    onHost::memcpy(asyncComputeQueue, stdVec, computeBuffer);
    

Complete Source File

041_memoryOperations.cpp
  1/* Copyright 2025 René Widera
  2 * SPDX-License-Identifier: ISC
  3 */
  4
  5#include "docsTest.hpp"
  6
  7#include <alpaka/alpaka.hpp>
  8
  9#include <catch2/catch_template_test_macros.hpp>
 10#include <catch2/catch_test_macros.hpp>
 11
 12#include <vector>
 13
 14using namespace alpaka;
 15
 16TEMPLATE_LIST_TEST_CASE("memory", "[docs]", docs::test::TestBackends)
 17{
 18    auto computeDevSelector = alpaka::onHost::makeDeviceSelector(TestType::makeDict());
 19    if(!computeDevSelector.isAvailable())
 20        return;
 21
 22    // using the typed interface and not concept + auto
 23    onHost::Device computeDev = computeDevSelector.makeDevice(0);
 24    onHost::Queue asyncComputeQueue = computeDev.makeQueue();
 25
 26    onHost::Queue hostQueue = onHost::makeHostDevice().makeQueue();
 27
 28    // Allocate a memory view on the compute device.
 29    // The memory will be freed automatically when the view goes out of scope.
 30    onHost::SharedBuffer computeBuffer = onHost::alloc<int>(computeDev, 10);
 31    // Derive the properties except the location.
 32    onHost::SharedBuffer hostBuffer = onHost::allocHostLike(computeBuffer);
 33
 34    // To operate on host memory views, we need a host queue. Sett all bytes to zero.
 35    onHost::memset(hostQueue, hostBuffer, 0);
 36    onHost::wait(hostQueue);
 37
 38    // Both memory views are not filled with valid data yet, so let us assign a value to each element and copy it to
 39    // the host. note: currently you cannot use a compute queue of a GPU device to fill a host memory view. @todo add
 40    // host task execution to any compute queue
 41    onHost::fill(asyncComputeQueue, computeBuffer, 42);
 42
 43    // copy the data to the host to the device
 44    onHost::memcpy(asyncComputeQueue, hostBuffer, computeBuffer);
 45
 46    // wait because all previous operations are asynchronous
 47    onHost::wait(asyncComputeQueue);
 48
 49    // check that the data is valid
 50    for(auto const& v : hostBuffer)
 51        CHECK(v == 42);
 52
 53    onHost::memset(hostQueue, hostBuffer, 0);
 54    onHost::wait(hostQueue);
 55
 56    onHost::memcpy(asyncComputeQueue, hostBuffer, computeBuffer, Vec{4u});
 57    onHost::wait(asyncComputeQueue);
 58
 59    CHECK(hostBuffer[0] == 42);
 60    CHECK(hostBuffer[1] == 42);
 61    CHECK(hostBuffer[2] == 42);
 62    CHECK(hostBuffer[3] == 42);
 63    CHECK(hostBuffer[4] == 0);
 64    CHECK(hostBuffer[5] == 0);
 65
 66    onHost::fill(hostQueue, hostBuffer, 42);
 67    onHost::wait(hostQueue);
 68
 69    // the value type of the buffer is int, which has size of 4 bytes on an x86_64 architecture
 70    static_assert(std::same_as<typename ALPAKA_TYPEOF(hostBuffer)::value_type, int>);
 71    onHost::memset(hostQueue, hostBuffer, 0, Vec{4u});
 72    onHost::wait(hostQueue);
 73
 74    CHECK(hostBuffer[0] == 0);
 75    CHECK(hostBuffer[1] == 0);
 76    CHECK(hostBuffer[2] == 0);
 77    CHECK(hostBuffer[3] == 0);
 78    CHECK(hostBuffer[4] == 42);
 79    CHECK(hostBuffer[5] == 42);
 80}
 81
 82TEMPLATE_LIST_TEST_CASE("memory using std::vector", "[docs]", docs::test::TestBackends)
 83{
 84    auto computeDevSpec = alpaka::onHost::DeviceSpec{TestType::makeDict()};
 85    auto computeDevSelector = alpaka::onHost::makeDeviceSelector(computeDevSpec);
 86    if(!computeDevSelector.isAvailable())
 87        return;
 88
 89    onHost::Device computeDev = computeDevSelector.makeDevice(0);
 90    onHost::Queue asyncComputeQueue = computeDev.makeQueue();
 91
 92    onHost::SharedBuffer computeBuffer = onHost::alloc<int>(computeDev, 10);
 93
 94    // use std::vector instead of an alpaka view
 95    std::vector stdVec = std::vector<int>(10, 0);
 96
 97    onHost::fill(asyncComputeQueue, computeBuffer, 42);
 98    onHost::memcpy(asyncComputeQueue, stdVec, computeBuffer);
 99    onHost::wait(asyncComputeQueue);
100
101    // check that the data is valid
102    for(auto const& v : stdVec)
103        CHECK(v == 42);
104}