Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SYCL][UR][L0] Access UMF pool handles through usm::pool_manager #905

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
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
216 changes: 115 additions & 101 deletions source/adapters/level_zero/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ UR_APIEXPORT ur_result_t UR_APICALL urContextCreate(
ur_context_handle_t_ *Context =
new ur_context_handle_t_(ZeContext, DeviceCount, Devices, true);

Context->initialize();
auto Ret = Context->initialize();
if (Ret) {
delete Context;
return Ret;
}

*RetContext = reinterpret_cast<ur_context_handle_t>(Context);
if (IndirectAccessTrackingEnabled) {
std::scoped_lock<ur_shared_mutex> Lock(Platform->ContextsMutex);
Expand Down Expand Up @@ -178,111 +183,120 @@ UR_APIEXPORT ur_result_t UR_APICALL urContextSetExtendedDeleter(
return UR_RESULT_ERROR_UNSUPPORTED_FEATURE;
}

// Template helper function for creating USM pools for given pool descriptor.
template <typename P, typename... Args>
std::pair<ur_result_t, umf::pool_unique_handle_t>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if there's a benefit to using it since I call umf::poolMakeUnique that returns a pair anyway.

createUMFPoolForDesc(usm::pool_descriptor &Desc, Args &&...args) {
umf_result_t UmfRet = UMF_RESULT_SUCCESS;
umf::provider_unique_handle_t MemProvider = nullptr;

switch (Desc.type) {
case UR_USM_TYPE_HOST: {
std::tie(UmfRet, MemProvider) =
umf::memoryProviderMakeUnique<L0HostMemoryProvider>(Desc.hContext,
Desc.hDevice);
break;
}
case UR_USM_TYPE_DEVICE: {
std::tie(UmfRet, MemProvider) =
umf::memoryProviderMakeUnique<L0DeviceMemoryProvider>(Desc.hContext,
Desc.hDevice);
break;
}
case UR_USM_TYPE_SHARED: {
if (Desc.deviceReadOnly) {
std::tie(UmfRet, MemProvider) =
umf::memoryProviderMakeUnique<L0SharedReadOnlyMemoryProvider>(
Desc.hContext, Desc.hDevice);
} else {
std::tie(UmfRet, MemProvider) =
umf::memoryProviderMakeUnique<L0SharedMemoryProvider>(Desc.hContext,
Desc.hDevice);
}
break;
}
default:
UmfRet = UMF_RESULT_ERROR_INVALID_ARGUMENT;
}

if (UmfRet)
return std::pair<ur_result_t, umf::pool_unique_handle_t>{
umf::umf2urResult(UmfRet), nullptr};

umf::pool_unique_handle_t Pool = nullptr;
std::tie(UmfRet, Pool) =
umf::poolMakeUnique<P, 1>({std::move(MemProvider)}, args...);

return std::pair<ur_result_t, umf::pool_unique_handle_t>{
umf::umf2urResult(UmfRet), std::move(Pool)};
};

ur_result_t ur_context_handle_t_::initialize() {

// Helper lambda to create various USM allocators for a device.
// Note that the CCS devices and their respective subdevices share a
// common ze_device_handle and therefore, also share USM allocators.
auto createUSMAllocators = [this](ur_device_handle_t Device) {
auto MemProvider = umf::memoryProviderMakeUnique<L0DeviceMemoryProvider>(
reinterpret_cast<ur_context_handle_t>(this), Device)
.second;
DeviceMemPools.emplace(
std::piecewise_construct, std::make_tuple(Device->ZeDevice),
std::make_tuple(umf::poolMakeUnique<usm::DisjointPool, 1>(
{std::move(MemProvider)},
DisjointPoolConfigInstance
.Configs[usm::DisjointPoolMemType::Device])
.second));

MemProvider = umf::memoryProviderMakeUnique<L0SharedMemoryProvider>(
reinterpret_cast<ur_context_handle_t>(this), Device)
.second;
SharedMemPools.emplace(
std::piecewise_construct, std::make_tuple(Device->ZeDevice),
std::make_tuple(umf::poolMakeUnique<usm::DisjointPool, 1>(
{std::move(MemProvider)},
DisjointPoolConfigInstance
.Configs[usm::DisjointPoolMemType::Shared])
.second));

MemProvider = umf::memoryProviderMakeUnique<L0SharedReadOnlyMemoryProvider>(
reinterpret_cast<ur_context_handle_t>(this), Device)
.second;
SharedReadOnlyMemPools.emplace(
std::piecewise_construct, std::make_tuple(Device->ZeDevice),
std::make_tuple(
umf::poolMakeUnique<usm::DisjointPool, 1>(
{std::move(MemProvider)},
DisjointPoolConfigInstance
.Configs[usm::DisjointPoolMemType::SharedReadOnly])
.second));

MemProvider = umf::memoryProviderMakeUnique<L0DeviceMemoryProvider>(
reinterpret_cast<ur_context_handle_t>(this), Device)
.second;
DeviceMemProxyPools.emplace(
std::piecewise_construct, std::make_tuple(Device->ZeDevice),
std::make_tuple(
umf::poolMakeUnique<USMProxyPool, 1>({std::move(MemProvider)})
.second));

MemProvider = umf::memoryProviderMakeUnique<L0SharedMemoryProvider>(
reinterpret_cast<ur_context_handle_t>(this), Device)
.second;
SharedMemProxyPools.emplace(
std::piecewise_construct, std::make_tuple(Device->ZeDevice),
std::make_tuple(
umf::poolMakeUnique<USMProxyPool, 1>({std::move(MemProvider)})
.second));

MemProvider = umf::memoryProviderMakeUnique<L0SharedReadOnlyMemoryProvider>(
reinterpret_cast<ur_context_handle_t>(this), Device)
.second;
SharedReadOnlyMemProxyPools.emplace(
std::piecewise_construct, std::make_tuple(Device->ZeDevice),
std::make_tuple(
umf::poolMakeUnique<USMProxyPool, 1>({std::move(MemProvider)})
.second));
};
auto Context = reinterpret_cast<ur_context_handle_t>(this);
ur_result_t Ret;

// Recursive helper to call createUSMAllocators for all sub-devices
std::function<void(ur_device_handle_t)> createUSMAllocatorsRecursive;
createUSMAllocatorsRecursive =
[createUSMAllocators,
&createUSMAllocatorsRecursive](ur_device_handle_t Device) -> void {
createUSMAllocators(Device);
for (auto &SubDevice : Device->SubDevices)
createUSMAllocatorsRecursive(SubDevice);
};
// Initialize pool managers.
std::tie(Ret, PoolManager) =
usm::pool_manager<usm::pool_descriptor>::create();
if (Ret) {
urPrint("urContextCreate: unexpected internal error\n");
return Ret;
kswiecicki marked this conversation as resolved.
Show resolved Hide resolved
}

// Create USM pool for each pair (device, context).
//
for (auto &Device : Devices) {
createUSMAllocatorsRecursive(Device);
std::tie(Ret, ProxyPoolManager) =
usm::pool_manager<usm::pool_descriptor>::create();
if (Ret) {
urPrint("urContextCreate: unexpected internal error\n");
return Ret;
}

std::vector<usm::pool_descriptor> Descs;
// Create pool descriptor for every device and subdevice.
std::tie(Ret, Descs) = usm::pool_descriptor::create(nullptr, Context);
if (Ret) {
urPrint("urContextCreate: unexpected internal error\n");
return Ret;
}
// Create USM pool for host. Device and Shared USM allocations
// are device-specific. Host allocations are not device-dependent therefore
// we don't need a map with device as key.
auto MemProvider = umf::memoryProviderMakeUnique<L0HostMemoryProvider>(
reinterpret_cast<ur_context_handle_t>(this), nullptr)
.second;
HostMemPool =
umf::poolMakeUnique<usm::DisjointPool, 1>(
{std::move(MemProvider)},
DisjointPoolConfigInstance.Configs[usm::DisjointPoolMemType::Host])
.second;

MemProvider = umf::memoryProviderMakeUnique<L0HostMemoryProvider>(
reinterpret_cast<ur_context_handle_t>(this), nullptr)
.second;
HostMemProxyPool =
umf::poolMakeUnique<USMProxyPool, 1>({std::move(MemProvider)}).second;

// We may allocate memory to this root device so create allocators.
if (SingleRootDevice &&
DeviceMemPools.find(SingleRootDevice->ZeDevice) == DeviceMemPools.end()) {
createUSMAllocators(SingleRootDevice);

auto descTypeToDisjointPoolType =
[](usm::pool_descriptor &Desc) -> usm::DisjointPoolMemType {
switch (Desc.type) {
case UR_USM_TYPE_HOST:
return usm::DisjointPoolMemType::Host;
case UR_USM_TYPE_DEVICE:
return usm::DisjointPoolMemType::Device;
case UR_USM_TYPE_SHARED:
return (Desc.deviceReadOnly) ? usm::DisjointPoolMemType::SharedReadOnly
: usm::DisjointPoolMemType::Shared;
default:
// Should not be reached.
ur::unreachable();
}
};

// Create USM pool for each pool descriptor and add it to pool manager.
for (auto &Desc : Descs) {
umf::pool_unique_handle_t Pool = nullptr;
auto PoolType = descTypeToDisjointPoolType(Desc);

std::tie(Ret, Pool) = createUMFPoolForDesc<usm::DisjointPool>(
Desc, DisjointPoolConfigInstance.Configs[PoolType]);
if (Ret) {
urPrint("urContextCreate: unexpected internal error\n");
return Ret;
}

PoolManager.addPool(Desc, Pool);

umf::pool_unique_handle_t ProxyPool = nullptr;
std::tie(Ret, ProxyPool) = createUMFPoolForDesc<USMProxyPool>(Desc);
if (Ret) {
urPrint("urContextCreate: unexpected internal error\n");
return Ret;
}

ProxyPoolManager.addPool(Desc, ProxyPool);
}

// Create the immediate command list to be used for initializations.
Expand Down
17 changes: 8 additions & 9 deletions source/adapters/level_zero/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "queue.hpp"

#include <umf_helpers.hpp>
#include <ur_pool_manager.hpp>

struct ur_context_handle_t_ : _ur_object {
ur_context_handle_t_(ze_context_handle_t ZeContext, uint32_t NumDevices,
Expand Down Expand Up @@ -96,15 +97,8 @@ struct ur_context_handle_t_ : _ur_object {

// Store USM pool for USM shared and device allocations. There is 1 memory
// pool per each pair of (context, device) per each memory type.
std::unordered_map<ze_device_handle_t, umf::pool_unique_handle_t>
DeviceMemPools;
std::unordered_map<ze_device_handle_t, umf::pool_unique_handle_t>
SharedMemPools;
std::unordered_map<ze_device_handle_t, umf::pool_unique_handle_t>
SharedReadOnlyMemPools;

// Store the host memory pool. It does not depend on any device.
umf::pool_unique_handle_t HostMemPool;
usm::pool_manager<usm::pool_descriptor> PoolManager;
usm::pool_manager<usm::pool_descriptor> ProxyPoolManager;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if it wouldn't be better to just have a flag in pool_descriptor to decide if we do any pooling. We could then have only a single pool manager.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But is it fitting to place such an option in the pool_descriptor?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not? I think in the future, we will want to have even more options in pool_descriptor, e.g. related to access characteristics, etc. that would influence pooling.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this looks odd. If you decide to do otherwise, please add a comment.


// Allocation-tracking proxy pools for direct allocations. No pooling used.
std::unordered_map<ze_device_handle_t, umf::pool_unique_handle_t>
Expand Down Expand Up @@ -252,3 +246,8 @@ struct ur_context_handle_t_ : _ur_object {
// mutex guarding the container with contexts because the context can be removed
// from the list of tracked contexts.
ur_result_t ContextReleaseHelper(ur_context_handle_t Context);

// Template helper function for creating USM pools for given pool descriptor.
template <typename P, typename... Args>
std::pair<ur_result_t, umf::pool_unique_handle_t>
createUMFPoolForDesc(usm::pool_descriptor &Desc, Args &&...args);
2 changes: 1 addition & 1 deletion source/adapters/level_zero/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ UR_APIEXPORT ur_result_t UR_APICALL urDeviceGetInfo(
}
}
}
return ReturnValue(std::min(GlobalMemSize, FreeMemory));
return ReturnValue((std::min)(GlobalMemSize, FreeMemory));
}
case UR_DEVICE_INFO_MEMORY_CLOCK_RATE: {
// If there are not any memory modules then return 0.
Expand Down
Loading
Loading