Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 18 additions & 13 deletions HeterogeneousCore/AlpakaInterface/interface/CachingAllocator.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,23 +376,30 @@ namespace cms::alpakatools {
for (auto iBlock = begin; iBlock != end; ++iBlock) {
if ((reuseSameQueueAllocations_ and (*block.queue == *(iBlock->second.queue))) or
alpaka::isComplete(*(iBlock->second.event))) {
// associate the cached buffer to the new queue
auto queue = std::move(*(block.queue));
// TODO cache (or remove) the debug information and use std::move()
block = iBlock->second;
block.queue = std::move(queue);
// take ownership of the cached block, so that a failure below cannot leave a block that
// has been moved from inside the cache
auto node = cachedBlocks_.extract(iBlock);
BlockDescriptor& cached = node.mapped();
cachedBytes_.free -= cached.bytes;

// the queue and the event of the cached block, for the debug message below
auto const* previousQueue = cached.queue->m_spQueueImpl.get();
auto const* previousEvent = cached.event->m_spEventImpl.get();

// take the buffer and the event from the cached block. The queue, the bin and the
// requested size stay those of this allocation.
block.buffer = std::move(cached.buffer);
block.event = std::move(cached.event);
block.bytes = cached.bytes;

// if the new queue is on different device than the old event, create a new event
if (block.device() != alpaka::getDev(*(block.event))) {
block.event = Event{block.device()};
}

// insert the cached block into the live blocks
// TODO cache (or remove) the debug information and use std::move()
liveBlocks_[block.buffer->data()] = block;
liveBlocks_.insert_or_assign(block.buffer->data(), block);

// update the accounting information
cachedBytes_.free -= block.bytes;
cachedBytes_.live += block.bytes;
cachedBytes_.requested += block.requested;

Expand All @@ -401,13 +408,11 @@ namespace cms::alpakatools {
out << "\t" << deviceType_ << " " << alpaka::getName(device_) << " reused cached block at "
<< block.buffer->data() << " (" << block.bytes << " bytes) for queue "
<< block.queue->m_spQueueImpl.get() << ", event " << block.event->m_spEventImpl.get()
<< " (previously associated with queue " << iBlock->second.queue->m_spQueueImpl.get() << " , event "
<< iBlock->second.event->m_spEventImpl.get() << ")." << std::endl;
<< " (previously associated with queue " << previousQueue << " , event " << previousEvent << ")."
<< std::endl;
std::cout << out.str() << std::endl;
}

// remove the reused block from the list of cached blocks
cachedBlocks_.erase(iBlock);
return true;
}
}
Expand Down
6 changes: 6 additions & 0 deletions HeterogeneousCore/AlpakaInterface/test/BuildFile.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
<flags ALPAKA_BACKENDS="1"/>
</bin>

<bin name="alpakaTestCachingAllocator" file="testCachingAllocator.cc">
<use name="alpaka"/>
<use name="catch2"/>
<use name="HeterogeneousCore/AlpakaInterface"/>
</bin>

<bin name="alpakaTestBackend" file="testBackend.cc">
<use name="catch2"/>
<use name="HeterogeneousCore/AlpakaInterface"/>
Expand Down
57 changes: 57 additions & 0 deletions HeterogeneousCore/AlpakaInterface/test/testCachingAllocator.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include <cstddef>

#define CATCH_CONFIG_MAIN
#include <catch2/catch_all.hpp>

#include <alpaka/alpaka.hpp>

#include "HeterogeneousCore/AlpakaInterface/interface/AllocatorConfig.h"
#include "HeterogeneousCore/AlpakaInterface/interface/CachingAllocator.h"
#include "HeterogeneousCore/AlpakaInterface/interface/host.h"

// The allocator is exercised on the host device, so the test needs no accelerator.
using Device = alpaka::DevCpu;
using Queue = alpaka::QueueCpuBlocking;
using Allocator = cms::alpakatools::CachingAllocator<Device, Queue>;

TEST_CASE("A reused block reports the size of the new allocation", "[CachingAllocator]") {
auto const& device = cms::alpakatools::host();
cms::alpakatools::AllocatorConfig config;
// reuse blocks that are associated to the same queue, as the device allocators do
Allocator allocator{device, config, true, false};
Queue queue{device};

// 300 and 500 bytes fall in the same bin, so the second allocation reuses the first block
void* first = allocator.allocate(300, queue);
REQUIRE(allocator.cacheStatus().requested == 300);
REQUIRE(allocator.cacheStatus().live == 512);

allocator.free(first);
REQUIRE(allocator.cacheStatus().requested == 0);
REQUIRE(allocator.cacheStatus().live == 0);
REQUIRE(allocator.cacheStatus().free == 512);

void* second = allocator.allocate(500, queue);
REQUIRE(second == first);
REQUIRE(allocator.cacheStatus().live == 512);
// the block is the same, but the reported size is the one of this allocation
REQUIRE(allocator.cacheStatus().requested == 500);

allocator.free(second);
REQUIRE(allocator.cacheStatus().requested == 0);
}

TEST_CASE("The accounting returns to zero after many reuses", "[CachingAllocator]") {
auto const& device = cms::alpakatools::host();
cms::alpakatools::AllocatorConfig config;
Allocator allocator{device, config, true, false};
Queue queue{device};

for (size_t bytes : {300ul, 500ul, 260ul, 511ul, 300ul}) {
void* p = allocator.allocate(bytes, queue);
REQUIRE(allocator.cacheStatus().requested == bytes);
allocator.free(p);
REQUIRE(allocator.cacheStatus().requested == 0);
REQUIRE(allocator.cacheStatus().live == 0);
}
}