mirror of
https://github.com/intel/compute-runtime.git
synced 2026-01-09 22:43:00 +08:00
refactor: extract generic parts of small buffers allocator
Currently the whole code resides within the opencl/ tree, but the mechanism is meant to be reused in L0 for kernel-ISA allocations optimization (further work). This commit is a preparation step, which extracts the generic mechanism and moves the extracted part under the shared/ tree. Related-To: NEO-7788 Signed-off-by: Maciej Bielski <maciej.bielski@intel.com>
This commit is contained in:
committed by
Compute-Runtime-Automation
parent
73d05045b0
commit
7ea8ed1757
90
shared/source/utilities/buffer_pool_allocator.h
Normal file
90
shared/source/utilities/buffer_pool_allocator.h
Normal file
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "shared/source/helpers/constants.h"
|
||||
#include "shared/source/utilities/stackvec.h"
|
||||
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace NEO {
|
||||
|
||||
class GraphicsAllocation;
|
||||
class HeapAllocator;
|
||||
class MemoryManager;
|
||||
|
||||
template <typename PoolT>
|
||||
struct SmallBuffersParams {
|
||||
protected:
|
||||
static constexpr auto aggregatedSmallBuffersPoolSize = 64 * KB;
|
||||
static constexpr auto smallBufferThreshold = 4 * KB;
|
||||
static constexpr auto chunkAlignment = 512u;
|
||||
static constexpr auto startingOffset = chunkAlignment;
|
||||
};
|
||||
|
||||
template <typename PoolT, typename BufferType, typename BufferParentType = BufferType>
|
||||
struct AbstractBuffersPool : public SmallBuffersParams<PoolT> {
|
||||
// The prototype of a function allocating the `mainStorage` is not specified.
|
||||
// That would be an unnecessary limitation here - it is completely up to derived class implementation.
|
||||
// Perhaps the allocating function needs to leverage `HeapAllocator::allocate()` and also
|
||||
// a BufferType-dependent function reserving chunks within `mainStorage`.
|
||||
// Example: see `NEO::Context::BufferPool::allocate()`
|
||||
using Params = SmallBuffersParams<PoolT>;
|
||||
using Params::aggregatedSmallBuffersPoolSize;
|
||||
using Params::chunkAlignment;
|
||||
using Params::smallBufferThreshold;
|
||||
using Params::startingOffset;
|
||||
using AllocsVecCRef = const StackVec<NEO::GraphicsAllocation *, 1> &;
|
||||
|
||||
AbstractBuffersPool(MemoryManager *memoryManager);
|
||||
AbstractBuffersPool(AbstractBuffersPool<PoolT, BufferType, BufferParentType> &&bufferPool);
|
||||
void tryFreeFromPoolBuffer(BufferParentType *possiblePoolBuffer, size_t offset, size_t size);
|
||||
bool isPoolBuffer(const BufferParentType *buffer) const;
|
||||
void drain();
|
||||
|
||||
// Derived class needs to provide its own implementation of getAllocationsVector().
|
||||
// This is a CRTP-replacement for virtual functions.
|
||||
AllocsVecCRef getAllocationsVector() {
|
||||
return static_cast<PoolT *>(this)->getAllocationsVector();
|
||||
}
|
||||
|
||||
MemoryManager *memoryManager{nullptr};
|
||||
std::unique_ptr<BufferType> mainStorage;
|
||||
std::unique_ptr<HeapAllocator> chunkAllocator;
|
||||
std::vector<std::pair<uint64_t, size_t>> chunksToFree;
|
||||
};
|
||||
|
||||
template <typename BuffersPoolType, typename BufferType, typename BufferParentType = BufferType>
|
||||
class AbstractBuffersAllocator : public SmallBuffersParams<BuffersPoolType> {
|
||||
// The prototype of a function allocating buffers from the pool is not specified (see similar comment in `AbstractBufersPool`).
|
||||
// By common sense, in order to allocate buffers from the pool the function should leverage a call provided by `BuffersPoolType`.
|
||||
// Example: see `NEO::Context::BufferPoolAllocator::allocateBufferFromPool()`.
|
||||
public:
|
||||
using Params = SmallBuffersParams<BuffersPoolType>;
|
||||
using Params::aggregatedSmallBuffersPoolSize;
|
||||
using Params::chunkAlignment;
|
||||
using Params::smallBufferThreshold;
|
||||
using Params::startingOffset;
|
||||
static_assert(aggregatedSmallBuffersPoolSize > smallBufferThreshold, "Largest allowed buffer needs to fit in pool");
|
||||
|
||||
void releaseSmallBufferPool() { this->bufferPools.clear(); }
|
||||
bool isPoolBuffer(const BufferParentType *buffer) const;
|
||||
void tryFreeFromPoolBuffer(BufferParentType *possiblePoolBuffer, size_t offset, size_t size);
|
||||
|
||||
protected:
|
||||
inline bool isSizeWithinThreshold(size_t size) const { return smallBufferThreshold >= size; }
|
||||
void drain();
|
||||
void addNewBufferPool(BuffersPoolType &&bufferPool);
|
||||
|
||||
std::mutex mutex;
|
||||
std::vector<BuffersPoolType> bufferPools;
|
||||
};
|
||||
} // namespace NEO
|
||||
88
shared/source/utilities/buffer_pool_allocator.inl
Normal file
88
shared/source/utilities/buffer_pool_allocator.inl
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include "shared/source/memory_manager/memory_manager.h"
|
||||
#include "shared/source/utilities/buffer_pool_allocator.h"
|
||||
#include "shared/source/utilities/heap_allocator.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace NEO {
|
||||
|
||||
template <typename PoolT, typename BufferType, typename BufferParentType>
|
||||
AbstractBuffersPool<PoolT, BufferType, BufferParentType>::AbstractBuffersPool(MemoryManager *mm) : memoryManager{mm} {
|
||||
static_assert(std::is_base_of_v<BufferParentType, BufferType>);
|
||||
}
|
||||
|
||||
template <typename PoolT, typename BufferType, typename BufferParentType>
|
||||
AbstractBuffersPool<PoolT, BufferType, BufferParentType>::AbstractBuffersPool(AbstractBuffersPool<PoolT, BufferType, BufferParentType> &&bufferPool)
|
||||
: memoryManager{bufferPool.memoryManager},
|
||||
mainStorage{std::move(bufferPool.mainStorage)},
|
||||
chunkAllocator{std::move(bufferPool.chunkAllocator)} {}
|
||||
|
||||
template <typename PoolT, typename BufferType, typename BufferParentType>
|
||||
void AbstractBuffersPool<PoolT, BufferType, BufferParentType>::tryFreeFromPoolBuffer(BufferParentType *possiblePoolBuffer, size_t offset, size_t size) {
|
||||
if (this->isPoolBuffer(possiblePoolBuffer)) {
|
||||
this->chunksToFree.push_back({offset + startingOffset, size});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename PoolT, typename BufferType, typename BufferParentType>
|
||||
bool AbstractBuffersPool<PoolT, BufferType, BufferParentType>::isPoolBuffer(const BufferParentType *buffer) const {
|
||||
static_assert(std::is_base_of_v<BufferParentType, BufferType>);
|
||||
|
||||
return (buffer && this->mainStorage.get() == buffer);
|
||||
}
|
||||
|
||||
template <typename PoolT, typename BufferType, typename BufferParentType>
|
||||
void AbstractBuffersPool<PoolT, BufferType, BufferParentType>::drain() {
|
||||
const auto &allocationsVec = this->getAllocationsVector();
|
||||
for (auto allocation : allocationsVec) {
|
||||
if (allocation && this->memoryManager->allocInUse(*allocation)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (auto &chunk : this->chunksToFree) {
|
||||
this->chunkAllocator->free(chunk.first, chunk.second);
|
||||
}
|
||||
this->chunksToFree.clear();
|
||||
}
|
||||
|
||||
template <typename BuffersPoolType, typename BufferType, typename BufferParentType>
|
||||
bool AbstractBuffersAllocator<BuffersPoolType, BufferType, BufferParentType>::isPoolBuffer(const BufferParentType *buffer) const {
|
||||
static_assert(std::is_base_of_v<BufferParentType, BufferType>);
|
||||
|
||||
for (auto &bufferPool : this->bufferPools) {
|
||||
if (bufferPool.isPoolBuffer(buffer)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename BuffersPoolType, typename BufferType, typename BufferParentType>
|
||||
void AbstractBuffersAllocator<BuffersPoolType, BufferType, BufferParentType>::tryFreeFromPoolBuffer(BufferParentType *possiblePoolBuffer, size_t offset, size_t size) {
|
||||
auto lock = std::unique_lock<std::mutex>(this->mutex);
|
||||
for (auto &bufferPool : this->bufferPools) {
|
||||
bufferPool.tryFreeFromPoolBuffer(possiblePoolBuffer, offset, size); // NOLINT(clang-analyzer-cplusplus.NewDelete)
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BuffersPoolType, typename BufferType, typename BufferParentType>
|
||||
void AbstractBuffersAllocator<BuffersPoolType, BufferType, BufferParentType>::drain() {
|
||||
for (auto &bufferPool : this->bufferPools) {
|
||||
bufferPool.drain();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename BuffersPoolType, typename BufferType, typename BufferParentType>
|
||||
void AbstractBuffersAllocator<BuffersPoolType, BufferType, BufferParentType>::addNewBufferPool(BuffersPoolType &&bufferPool) {
|
||||
if (bufferPool.mainStorage) {
|
||||
this->bufferPools.push_back(std::move(bufferPool));
|
||||
}
|
||||
}
|
||||
} // namespace NEO
|
||||
Reference in New Issue
Block a user