Skip to content

Commit

Permalink
[SYCL] Adding support for buffer::use_pinned_host_memory property (#2080
Browse files Browse the repository at this point in the history
)
  • Loading branch information
romanovvlad authored Jul 22, 2020
1 parent b7ae462 commit e5ea144
Show file tree
Hide file tree
Showing 10 changed files with 192 additions and 37 deletions.
2 changes: 1 addition & 1 deletion sycl/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ include(AddSYCLExecutable)
set(SYCL_MAJOR_VERSION 2)
set(SYCL_MINOR_VERSION 1)
set(SYCL_PATCH_VERSION 0)
set(SYCL_DEV_ABI_VERSION 0)
set(SYCL_DEV_ABI_VERSION 1)
if (SYCL_ADD_DEV_VERSION_POSTFIX)
set(SYCL_VERSION_POSTFIX "-${SYCL_DEV_ABI_VERSION}")
endif()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
= SYCL Proposals: Use Pinned Host Memory Property
Vlad Romanov <vlad[email protected]>
v0.1
:source-highlighter: pygments
:icons: font
== Introduction
This document describes an extension that introduces a +sycl::ext::oneapi::property::buffer::use_pinned_host_memory+ property for the `sycl::buffer`. Some SYCL backends can accelerate copies between host and device by allocating pinned memory. The property can be passed to the `sycl::buffer` constructor in order to enable such an allocation.

== Name Strings

+SYCL_INTEL_use_pinned_host_memory+

== Use Pinned Host Memory Property

.Proposed Buffer Property
[cols="^50,50",options="header"]
|===

|Property |Description
|`syc::ext::oneapi::property::buffer::use_pinned_host_memory`
| The `use_pinned_host_memory` property adds the requirement that the SYCL runtime must allocate host pinned memory for the `sycl::buffer`. The property cannot be used with the `sycl::buffer` constructors that take hostData parameter, an invalid_object_error SYCL exception must be thrown in this case.
|===
43 changes: 42 additions & 1 deletion sycl/include/CL/sycl/detail/buffer_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,39 @@ class __SYCL_EXPORT buffer_impl final : public SYCLMemObjT {
public:
buffer_impl(size_t SizeInBytes, size_t, const property_list &Props,
unique_ptr_class<SYCLMemObjAllocator> Allocator)
: BaseT(SizeInBytes, Props, std::move(Allocator)) {}
: BaseT(SizeInBytes, Props, std::move(Allocator)) {

if (Props.has_property<sycl::property::buffer::use_host_ptr>())
throw sycl::invalid_object_error(
"The use_host_ptr property requires host pointer to be provided",
PI_INVALID_OPERATION);
}

buffer_impl(void *HostData, size_t SizeInBytes, size_t RequiredAlign,
const property_list &Props,
unique_ptr_class<SYCLMemObjAllocator> Allocator)
: BaseT(SizeInBytes, Props, std::move(Allocator)) {

if (Props.has_property<
sycl::ext::oneapi::property::buffer::use_pinned_host_memory>())
throw sycl::invalid_object_error(
"The use_pinned_host_memory cannot be used with host pointer",
PI_INVALID_OPERATION);

BaseT::handleHostData(HostData, RequiredAlign);
}

buffer_impl(const void *HostData, size_t SizeInBytes, size_t RequiredAlign,
const property_list &Props,
unique_ptr_class<SYCLMemObjAllocator> Allocator)
: BaseT(SizeInBytes, Props, std::move(Allocator)) {

if (Props.has_property<
sycl::ext::oneapi::property::buffer::use_pinned_host_memory>())
throw sycl::invalid_object_error(
"The use_pinned_host_memory cannot be used with host pointer",
PI_INVALID_OPERATION);

BaseT::handleHostData(HostData, RequiredAlign);
}

Expand All @@ -66,6 +86,13 @@ class __SYCL_EXPORT buffer_impl final : public SYCLMemObjT {
size_t RequiredAlign, const property_list &Props,
unique_ptr_class<SYCLMemObjAllocator> Allocator)
: BaseT(SizeInBytes, Props, std::move(Allocator)) {

if (Props.has_property<
sycl::ext::oneapi::property::buffer::use_pinned_host_memory>())
throw sycl::invalid_object_error(
"The use_pinned_host_memory cannot be used with host pointer",
PI_INVALID_OPERATION);

BaseT::handleHostData(HostData, RequiredAlign);
}

Expand All @@ -79,6 +106,13 @@ class __SYCL_EXPORT buffer_impl final : public SYCLMemObjT {
const property_list &Props,
unique_ptr_class<SYCLMemObjAllocator> Allocator)
: BaseT(SizeInBytes, Props, std::move(Allocator)) {

if (Props.has_property<sycl::property::buffer::use_host_ptr>())
throw sycl::invalid_object_error(
"Buffer constructor from a pair of iterator values cannot have the "
"use_host_ptr property.",
PI_INVALID_OPERATION);

BaseT::handleHostData(First, Last, RequiredAlign);
}

Expand All @@ -92,6 +126,13 @@ class __SYCL_EXPORT buffer_impl final : public SYCLMemObjT {
const property_list &Props,
unique_ptr_class<SYCLMemObjAllocator> Allocator)
: BaseT(SizeInBytes, Props, std::move(Allocator)) {

if (Props.has_property<sycl::property::buffer::use_host_ptr>())
throw sycl::invalid_object_error(
"Buffer constructor from a pair of iterator values cannot have the "
"use_host_ptr property.",
PI_INVALID_OPERATION);

BaseT::handleHostData(First, Last, RequiredAlign);
}

Expand Down
15 changes: 11 additions & 4 deletions sycl/include/CL/sycl/detail/memory_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <CL/sycl/detail/cl.h>
#include <CL/sycl/detail/export.hpp>
#include <CL/sycl/detail/sycl_mem_obj_i.hpp>
#include <CL/sycl/property_list.hpp>
#include <CL/sycl/range.hpp>

#include <memory>
Expand Down Expand Up @@ -71,6 +72,7 @@ class __SYCL_EXPORT MemoryManager {
bool HostPtrReadOnly, size_t Size,
const EventImplPtr &InteropEvent,
const ContextImplPtr &InteropContext,
const sycl::property_list &PropsList,
RT::PiEvent &OutEventToWait);

// Allocates images in specified context taking into account situations such
Expand All @@ -80,29 +82,34 @@ class __SYCL_EXPORT MemoryManager {
ContextImplPtr TargetContext, SYCLMemObjI *MemObj, void *UserPtr,
bool HostPtrReadOnly, size_t Size, const RT::PiMemImageDesc &Desc,
const RT::PiMemImageFormat &Format, const EventImplPtr &InteropEvent,
const ContextImplPtr &InteropContext, RT::PiEvent &OutEventToWait);
const ContextImplPtr &InteropContext,
const sycl::property_list &PropsList, RT::PiEvent &OutEventToWait);

// Releases memory object(buffer or image). TargetContext should be device
// one(not host).
static void releaseMemObj(ContextImplPtr TargetContext, SYCLMemObjI *MemObj,
void *MemAllocation, void *UserPtr);

static void *allocateHostMemory(SYCLMemObjI *MemObj, void *UserPtr,
bool HostPtrReadOnly, size_t Size);
bool HostPtrReadOnly, size_t Size,
const sycl::property_list &PropsList);

static void *allocateInteropMemObject(ContextImplPtr TargetContext,
void *UserPtr,
const EventImplPtr &InteropEvent,
const ContextImplPtr &InteropContext,
const sycl::property_list &PropsList,
RT::PiEvent &OutEventToWait);

static void *allocateImageObject(ContextImplPtr TargetContext, void *UserPtr,
bool HostPtrReadOnly,
const RT::PiMemImageDesc &Desc,
const RT::PiMemImageFormat &Format);
const RT::PiMemImageFormat &Format,
const sycl::property_list &PropsList);

static void *allocateBufferObject(ContextImplPtr TargetContext, void *UserPtr,
bool HostPtrReadOnly, const size_t Size);
bool HostPtrReadOnly, const size_t Size,
const sycl::property_list &PropsList);

// Copies memory between: host and device, host and host,
// device and device if memory objects bound to the one context.
Expand Down
45 changes: 37 additions & 8 deletions sycl/include/CL/sycl/property_list.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ class noinit;

namespace detail {

// Will be aliased in the sycl::ext::oneapi::property namespace
namespace buffer_ {
class use_pinned_host_memory;
}

// List of all properties' IDs.
enum PropKind {
// Buffer properties
Expand All @@ -66,6 +71,8 @@ enum PropKind {
// Accessor
NoInit,

BufferUsePinnedHostMemory,

PropKindSize
};

Expand Down Expand Up @@ -148,6 +155,8 @@ RegisterProp(PropKind::ImageContextBound, image::context_bound);
RegisterProp(PropKind::BufferUseHostPtr, buffer::use_host_ptr);
RegisterProp(PropKind::BufferUseMutex, buffer::use_mutex);
RegisterProp(PropKind::BufferContextBound, buffer::context_bound);
RegisterProp(PropKind::BufferUsePinnedHostMemory,
buffer_::use_pinned_host_memory);

// Queue
RegisterProp(PropKind::QueueEnableProfiling, queue::enable_profiling);
Expand Down Expand Up @@ -212,8 +221,16 @@ class context_bound
public:
context_bound(cl::sycl::context Context) : ContextBoundBase(Context) {}
};

} // namespace buffer

namespace detail {
namespace buffer_ {
class use_pinned_host_memory
: public detail::Prop<detail::PropKind::BufferUsePinnedHostMemory> {};
} // namespace buffer_
} // namespace detail

namespace queue {
class enable_profiling
: public detail::Prop<detail::PropKind::QueueEnableProfiling> {};
Expand All @@ -225,6 +242,17 @@ class noinit : public detail::Prop<detail::PropKind::NoInit> {};

} // namespace property

namespace ext {
namespace oneapi {
namespace property {
namespace buffer {
using use_pinned_host_memory =
sycl::property::detail::buffer_::use_pinned_host_memory;
} // namespace buffer
} // namespace property
} // namespace oneapi
} // namespace ext

#if __cplusplus > 201402L

inline constexpr property::noinit noinit;
Expand Down Expand Up @@ -280,17 +308,18 @@ class property_list {
}

template <typename propertyT> propertyT get_property() const {
static_assert((int)(propertyT::getKind()) <=
property::detail::PropKind::PropKindSize,
"Invalid option passed.");
const auto &PropHolder = std::get<(int)(propertyT::getKind())>(m_PropsList);
if (PropHolder.isInitialized()) {
return PropHolder.getProp();
if (!has_property<propertyT>()) {
throw sycl::invalid_object_error();
}
throw invalid_object_error();
const auto &PropHolder =
std::get<static_cast<int>(propertyT::getKind())>(m_PropsList);
return PropHolder.getProp();
}

template <typename propertyT> bool has_property() const {
if (static_cast<int>(propertyT::getKind()) >
property::detail::PropKind::PropKindSize)
return false;
return std::get<(int)(propertyT::getKind())>(m_PropsList).isInitialized();
}

Expand All @@ -299,7 +328,7 @@ class property_list {

template <typename... propertyTN, class PropT>
void ctorHelper(PropT &Prop, propertyTN... props) {
std::get<(int)(PropT::getKind())>(m_PropsList).setProp(Prop);
std::get<static_cast<int>(PropT::getKind())>(m_PropsList).setProp(Prop);
ctorHelper(props...);
}

Expand Down
2 changes: 1 addition & 1 deletion sycl/source/detail/buffer_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ void *buffer_impl::allocateMem(ContextImplPtr Context, bool InitFromUserData,

return MemoryManager::allocateMemBuffer(
std::move(Context), this, UserPtr, BaseT::MHostPtrReadOnly,
BaseT::getSize(), BaseT::MInteropEvent, BaseT::MInteropContext,
BaseT::getSize(), BaseT::MInteropEvent, BaseT::MInteropContext, MProps,
OutEventToWait);
}
} // namespace detail
Expand Down
2 changes: 1 addition & 1 deletion sycl/source/detail/image_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ void *image_impl<Dimensions>::allocateMem(ContextImplPtr Context,
return MemoryManager::allocateMemImage(
std::move(Context), this, UserPtr, BaseT::MHostPtrReadOnly,
BaseT::getSize(), Desc, Format, BaseT::MInteropEvent,
BaseT::MInteropContext, OutEventToWait);
BaseT::MInteropContext, MProps, OutEventToWait);
}

template <int Dimensions>
Expand Down
39 changes: 25 additions & 14 deletions sycl/source/detail/memory_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ void *MemoryManager::wrapIntoImageBuffer(ContextImplPtr TargetContext,
}

void *MemoryManager::allocateHostMemory(SYCLMemObjI *MemObj, void *UserPtr,
bool HostPtrReadOnly, size_t Size) {
bool HostPtrReadOnly, size_t Size,
const sycl::property_list &) {
// Can return user pointer directly if it points to writable memory.
if (UserPtr && HostPtrReadOnly == false)
return UserPtr;
Expand All @@ -123,7 +124,7 @@ void *MemoryManager::allocateHostMemory(SYCLMemObjI *MemObj, void *UserPtr,
void *MemoryManager::allocateInteropMemObject(
ContextImplPtr TargetContext, void *UserPtr,
const EventImplPtr &InteropEvent, const ContextImplPtr &InteropContext,
RT::PiEvent &OutEventToWait) {
const sycl::property_list &, RT::PiEvent &OutEventToWait) {
// If memory object is created with interop c'tor.
// Return cl_mem as is if contexts match.
if (TargetContext == InteropContext) {
Expand All @@ -144,7 +145,8 @@ void *MemoryManager::allocateInteropMemObject(
void *MemoryManager::allocateImageObject(ContextImplPtr TargetContext,
void *UserPtr, bool HostPtrReadOnly,
const RT::PiMemImageDesc &Desc,
const RT::PiMemImageFormat &Format) {
const RT::PiMemImageFormat &Format,
const sycl::property_list &) {
// Create read_write mem object by default to handle arbitrary uses.
RT::PiMemFlags CreationFlags = PI_MEM_FLAGS_ACCESS_RW;
if (UserPtr)
Expand All @@ -159,16 +161,20 @@ void *MemoryManager::allocateImageObject(ContextImplPtr TargetContext,
return NewMem;
}

void *MemoryManager::allocateBufferObject(ContextImplPtr TargetContext,
void *UserPtr, bool HostPtrReadOnly,
const size_t Size) {
void *
MemoryManager::allocateBufferObject(ContextImplPtr TargetContext, void *UserPtr,
bool HostPtrReadOnly, const size_t Size,
const sycl::property_list &PropsList) {
// Create read_write mem object by default to handle arbitrary uses.
RT::PiMemFlags CreationFlags = PI_MEM_FLAGS_ACCESS_RW;
if (UserPtr)
CreationFlags |= HostPtrReadOnly ? PI_MEM_FLAGS_HOST_PTR_COPY
: PI_MEM_FLAGS_HOST_PTR_USE;
else if (PropsList.has_property<
sycl::ext::oneapi::property::buffer::use_pinned_host_memory>())
CreationFlags |= PI_MEM_FLAGS_HOST_PTR_ALLOC;

RT::PiMem NewMem;
RT::PiMem NewMem = nullptr;
const detail::plugin &Plugin = TargetContext->getPlugin();
Plugin.call<PiApiKind::piMemBufferCreate>(
TargetContext->getHandleRef(), CreationFlags, Size, UserPtr, &NewMem);
Expand All @@ -180,27 +186,32 @@ void *MemoryManager::allocateMemBuffer(ContextImplPtr TargetContext,
bool HostPtrReadOnly, size_t Size,
const EventImplPtr &InteropEvent,
const ContextImplPtr &InteropContext,
const sycl::property_list &PropsList,
RT::PiEvent &OutEventToWait) {
if (TargetContext->is_host())
return allocateHostMemory(MemObj, UserPtr, HostPtrReadOnly, Size);
return allocateHostMemory(MemObj, UserPtr, HostPtrReadOnly, Size,
PropsList);
if (UserPtr && InteropContext)
return allocateInteropMemObject(TargetContext, UserPtr, InteropEvent,
InteropContext, OutEventToWait);
return allocateBufferObject(TargetContext, UserPtr, HostPtrReadOnly, Size);
InteropContext, PropsList, OutEventToWait);
return allocateBufferObject(TargetContext, UserPtr, HostPtrReadOnly, Size,
PropsList);
}

void *MemoryManager::allocateMemImage(
ContextImplPtr TargetContext, SYCLMemObjI *MemObj, void *UserPtr,
bool HostPtrReadOnly, size_t Size, const RT::PiMemImageDesc &Desc,
const RT::PiMemImageFormat &Format, const EventImplPtr &InteropEvent,
const ContextImplPtr &InteropContext, RT::PiEvent &OutEventToWait) {
const ContextImplPtr &InteropContext, const sycl::property_list &PropsList,
RT::PiEvent &OutEventToWait) {
if (TargetContext->is_host())
return allocateHostMemory(MemObj, UserPtr, HostPtrReadOnly, Size);
return allocateHostMemory(MemObj, UserPtr, HostPtrReadOnly, Size,
PropsList);
if (UserPtr && InteropContext)
return allocateInteropMemObject(TargetContext, UserPtr, InteropEvent,
InteropContext, OutEventToWait);
InteropContext, PropsList, OutEventToWait);
return allocateImageObject(TargetContext, UserPtr, HostPtrReadOnly, Desc,
Format);
Format, PropsList);
}

void *MemoryManager::allocateMemSubBuffer(ContextImplPtr TargetContext,
Expand Down
Loading

0 comments on commit e5ea144

Please sign in to comment.