Skip to content

[SYCL][USM] Add support for pointer query APIs #1016

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

Merged
merged 7 commits into from
Feb 6, 2020
Merged
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
14 changes: 14 additions & 0 deletions sycl/include/CL/sycl/usm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,5 +145,19 @@ T *aligned_alloc(size_t Alignment, size_t Count, const queue &Q,
Kind);
}

// Pointer queries
/// Query the allocation type from a USM pointer
///
/// @param ptr is the USM pointer to query
/// @param ctxt is the sycl context the ptr was allocated in
usm::alloc get_pointer_type(const void *ptr, const context &ctxt);

/// Queries the device against which the pointer was allocated
/// Throws an invalid_object_error if ptr is a host allocation.
///
/// @param ptr is the USM pointer to query
/// @param ctxt is the sycl context the ptr was allocated in
device get_pointer_device(const void *ptr, const context &ctxt);

} // namespace sycl
} // namespace cl
75 changes: 75 additions & 0 deletions sycl/source/detail/usm/usm_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -231,5 +231,80 @@ void *aligned_alloc(size_t Alignment, size_t Size, const queue &Q, alloc Kind) {
return aligned_alloc(Alignment, Size, Q.get_device(), Q.get_context(), Kind);
}

// Pointer queries
/// Query the allocation type from a USM pointer
/// Returns alloc::host for all pointers in a host context.
///
/// @param ptr is the USM pointer to query
/// @param ctxt is the sycl context the ptr was allocated in
alloc get_pointer_type(const void *Ptr, const context &Ctxt) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What if user mixes up context used for USM allocation and context passed to get_pointer_type function?

sycl::context Ctx1(sycl::cpu_selector), Ctx2(sycl::host_selector);
auto *Ptr = sycl::alloc_shared(Ctx1, ...);
auto Type = sycl::get_pointer_type(Ptr, Ctx2); // <- This returns alloc::host.

Can we add some diagnostic to catch such issues?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The PI_CALL should fail if something like that goes wrong.

Copy link
Contributor

Choose a reason for hiding this comment

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

In the example above get_pointer_type doesn't call PI.

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'm not sure I follow.

Copy link
Contributor

Choose a reason for hiding this comment

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

I mean, the code snippet doesn't return any error, while being illegal because Ptr is allocated in the Ctx1 but get_pointer_type is called with Ctx2.

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'm not sure this can really be handled. In this case, I would expect it to just work as shared allocations work on the host and the host doesn't really care what context anything is in. CPU memory is CPU memory.

The trickier case is:

sycl::context Ctx1(sycl::gpu_selector), Ctx2(sycl::host_selector);
auto *Ptr = sycl::alloc_device(Ctx1, ...);
auto Type = sycl::get_pointer_type(Ptr, Ctx2); // <- This returns alloc::host.

Trying to use Ptr on the host ought to crash the program or give garbage values. We would have to keep track of insane amounts of state to try to get a host device context to bail on this.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should invent some diagnostic, probably enabled if some option is set only.
People facing this issue in real code already.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm OK if done as a separate patch.

// Everything on a host device is just system malloc so call it host
if (Ctxt.is_host())
return alloc::host;

std::shared_ptr<detail::context_impl> CtxImpl = detail::getSyclObjImpl(Ctxt);
pi_context PICtx = CtxImpl->getHandleRef();
pi_usm_type AllocTy;

// query type using PI function
PI_CALL(piextUSMGetMemAllocInfo)(PICtx, Ptr, PI_MEM_ALLOC_TYPE,
sizeof(pi_usm_type), &AllocTy, nullptr);

alloc ResultAlloc;
switch (AllocTy) {
case PI_MEM_TYPE_HOST:
ResultAlloc = alloc::host;
break;
case PI_MEM_TYPE_DEVICE:
ResultAlloc = alloc::device;
break;
case PI_MEM_TYPE_SHARED:
ResultAlloc = alloc::shared;
break;
default:
ResultAlloc = alloc::unknown;
break;
}

return ResultAlloc;
}

/// Queries the device against which the pointer was allocated
///
/// @param ptr is the USM pointer to query
/// @param ctxt is the sycl context the ptr was allocated in
device get_pointer_device(const void *Ptr, const context &Ctxt) {
// Just return the host device in the host context
if (Ctxt.is_host())
return Ctxt.get_devices()[0];

std::shared_ptr<detail::context_impl> CtxImpl = detail::getSyclObjImpl(Ctxt);

// Check if ptr is a host allocation
if (get_pointer_type(Ptr, Ctxt) == alloc::host) {
auto Devs = CtxImpl->getDevices();
if (Devs.size() == 0)
throw runtime_error("No devices in passed context!");

// Just return the first device in the context
return Devs[0];
}

pi_context PICtx = CtxImpl->getHandleRef();
pi_device DeviceId;

// query device using PI function
PI_CALL(piextUSMGetMemAllocInfo)(PICtx, Ptr, PI_MEM_ALLOC_DEVICE,
sizeof(pi_device), &DeviceId, nullptr);

for (const device &Dev : CtxImpl->getDevices()) {
// Try to find the real sycl device used in the context
if (detail::getSyclObjImpl(Dev)->getHandleRef() == DeviceId)
return Dev;
}

throw runtime_error("Cannot find device associated with USM allocation!");
}

} // namespace sycl
} // namespace cl
96 changes: 96 additions & 0 deletions sycl/test/usm/pointer_query.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// RUN: %clangxx -fsycl %s -o %t1.out
// RUN: env SYCL_DEVICE_TYPE=HOST %t1.out

//==-------------- pointer_query.cpp - Pointer Query test ------------------==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include <CL/sycl.hpp>

using namespace cl::sycl;

int main() {
int *array = nullptr;
const int N = 4;
queue q;
auto dev = q.get_device();
auto ctxt = q.get_context();

if (!(dev.get_info<info::device::usm_device_allocations>() &&
dev.get_info<info::device::usm_shared_allocations>() &&
dev.get_info<info::device::usm_host_allocations>()))
return 0;

usm::alloc Kind;
device D;

// Test device allocs
array = (int *)malloc_device(N * sizeof(int), q);
if (array == nullptr) {
return 1;
}
Kind = get_pointer_type(array, ctxt);
if (ctxt.is_host()) {
// for now, host device treats all allocations
// as host allocations
if (Kind != usm::alloc::host) {
return 2;
}
} else {
if (Kind != usm::alloc::device) {
return 3;
}
}
D = get_pointer_device(array, ctxt);
if (D != dev) {
return 4;
}
free(array, ctxt);

// Test shared allocs
array = (int *)malloc_shared(N * sizeof(int), q);
if (array == nullptr) {
return 5;
}
Kind = get_pointer_type(array, ctxt);
if (ctxt.is_host()) {
// for now, host device treats all allocations
// as host allocations
if (Kind != usm::alloc::host) {
return 6;
}
} else {
if (Kind != usm::alloc::shared) {
return 7;
}
}
D = get_pointer_device(array, ctxt);
if (D != dev) {
return 8;
}
free(array, ctxt);

// Test host allocs
array = (int *)malloc_host(N * sizeof(int), q);
if (array == nullptr) {
return 9;
}
Kind = get_pointer_type(array, ctxt);
if (Kind != usm::alloc::host) {
return 10;
}
D = get_pointer_device(array, ctxt);
auto Devs = ctxt.get_devices();
auto result = std::find(Devs.begin(), Devs.end(), D);
if (result == Devs.end()) {
// Returned device was not in queried context
return 11;
}
free(array, ctxt);

return 0;
}