Skip to content

Commit 4a4a190

Browse files
committed
[GPU] Enable zero-copy host-backed buffers for Xe2+ iGPUs
#### Details: Description of the issue(symptom, root-cause, how it was resolved) - Add engine::can_bind_host_buffer() to validate iGPU, Xe2+, USM host support, and page alignment - Add engine::bind_host_owned_buffer() to wrap host memory for GPU access without copies - Use seek instead of read for padding in data.hpp load path - Remove obsolete helper methods from binary_buffer.hpp The code and line that caused this issue (if it is not changed directly) src/plugins/intel_gpu/include/intel_gpu/graph/serialization/binary_buffer.hpp src/plugins/intel_gpu/include/intel_gpu/primitives/data.hpp src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp src/plugins/intel_gpu/src/graph/program.cpp src/plugins/intel_gpu/src/runtime/engine.cpp src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp Reproduction step and snapshot (if applicable. Do not attach for customer model) NA Problematic graph NA #### Checklist - [x] Is it a proper fix? Yes (not a workaround) - [x] Did you include test case for this fix, if necessary? NA - [x] Did you review existing test that can be extended to cover this scenario? Which test did you review? NA #### Tickets: - CVS-177982 #### AI Assistance: - AI assistance used: yes - AI was used for code refactoring, API naming, and file editing automation. All changes were manually validated through code review and verification of implementation correctness.
1 parent ee0676e commit 4a4a190

11 files changed

Lines changed: 71 additions & 56 deletions

File tree

src/plugins/intel_gpu/include/intel_gpu/graph/serialization/binary_buffer.hpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,6 @@ class BinaryInputBuffer : public InputBuffer<BinaryInputBuffer> {
117117
std::streambuf* get_streambuf() const {
118118
return _stream.rdbuf();
119119
}
120-
bool has_mmap_tensor() const {
121-
return _tensor_base_ptr != nullptr;
122-
}
123-
124-
bool is_mmap_tensor_4K_aligned() const {
125-
return has_mmap_tensor() && (reinterpret_cast<std::uintptr_t>(_tensor_base_ptr) % CACHE_PAGE_SIZE == 0);
126-
}
127120

128121
const size_t* get_mmap_tensor() const {
129122
return _tensor_base_ptr;

src/plugins/intel_gpu/include/intel_gpu/primitives/data.hpp

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -385,11 +385,11 @@ struct data : public primitive_base<data> {
385385

386386
bool do_weightless_caching = cache_info->save(ob, data_size);
387387
if (!do_weightless_caching) {
388+
if (!ob.is_encrypted() && !ob.is_offset_sub_buffer_aligned()) {
389+
std::vector<uint8_t> pad(ob.get_bytes_to_sub_buffer_boundary(), 0);
390+
ob << make_data(pad.data(), pad.size());
391+
}
388392
if (is_alloc_host_accessible(_allocation_type)) {
389-
if (!ob.is_encrypted() && !ob.is_offset_sub_buffer_aligned()) {
390-
std::vector<uint8_t> pad(ob.get_bytes_to_sub_buffer_boundary(), 0);
391-
ob << make_data(pad.data(), pad.size());
392-
}
393393
ob << make_data(mem->buffer_ptr(), data_size);
394394
} else {
395395
std::vector<uint8_t> _buf;
@@ -417,29 +417,24 @@ struct data : public primitive_base<data> {
417417

418418
bool weightless_caching = false;
419419
ib >> weightless_caching;
420-
421-
bool enable_zero_copy_mode = ib.is_mmap_tensor_4K_aligned() && ib.get_engine().get_device_info().arch >= gpu_arch::xe2 &&
422-
ib.get_engine().get_device_info().dev_type == device_type::integrated_gpu &&
423-
_allocation_type == allocation_type::usm_host && !weightless_caching &&
424-
model_tensor_base != nullptr;
425-
if (!enable_zero_copy_mode) {
420+
bool zero_copy_mode = !weightless_caching && ib.get_engine().can_bind_host_buffer(ib.get_mmap_tensor());
421+
if (!zero_copy_mode) {
426422
mem = ib.get_engine().allocate_memory(output_layout, _allocation_type, false);
427423
}
428-
424+
429425
bool is_weightless_caching = cache_info->load(ib, mem, weights_memory, weightless_caching);
430426

431427
if (!is_weightless_caching) {
432-
if (is_alloc_host_accessible(_allocation_type)) {
433-
if (!ib.is_encrypted() && !ib.is_offset_sub_buffer_aligned()) {
434-
std::vector<uint8_t> pad(ib.get_bytes_to_sub_buffer_boundary(), 0);
435-
ib >> make_data(pad.data(), pad.size());
436-
}
437-
if (enable_zero_copy_mode) {
438-
mem = ib.get_engine().create_subbuffer(*model_tensor_base, output_layout, ib.get_offset());
439-
ib.seek_current_ptr(data_size);
440-
} else {
441-
ib >> make_data(std::move(mem->buffer_ptr()), data_size);
442-
}
428+
// Skip padding bytes (page alignment required for binding host-owned buffers)
429+
if (!ib.is_encrypted() && !ib.is_offset_sub_buffer_aligned()) {
430+
ib.seek_current_ptr(ib.get_bytes_to_sub_buffer_boundary());
431+
}
432+
// Zero-copy: create subbuffer referencing mmap'd cache without host-to-device transfer
433+
if (zero_copy_mode) {
434+
mem = ib.get_engine().create_subbuffer(*model_tensor_base, output_layout, ib.get_offset());
435+
ib.seek_current_ptr(data_size);
436+
} else if (is_alloc_host_accessible(_allocation_type)) {
437+
ib >> make_data(std::move(mem->buffer_ptr()), data_size);
443438
} else {
444439
const size_t DATA_BLOCK_SIZE = 4 * 1024 * 1024;
445440
auto& eng = ib.get_engine();

src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,11 @@ class engine {
6464
/// Created subbuffer memory object from the other @p memory and reinterpred the data using specified @p new_layout
6565
virtual memory_ptr create_subbuffer(const memory& memory, const layout& new_layout, size_t byte_offset) = 0;
6666

67-
/// Created memory object by wrapping a host-allocated, memory-mapped layout region
68-
virtual memory_ptr create_mmap_hostbuffer(const void* mmapped_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) = 0;
67+
/// Check if host-owned buffer can be bound for GPU access (integrated GPU, Xe2+, page-aligned)
68+
bool can_bind_host_buffer(const void* host_address) const;
69+
70+
/// Bind host-owned buffer for zero-copy GPU access (returns nullptr if binding not supported)
71+
virtual memory_ptr bind_host_owned_buffer(const void* host_address, size_t data_size) = 0;
6972

7073
/// Created memory object from the other @p memory and reinterpred the data using specified @p new_layout
7174
virtual memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) = 0;

src/plugins/intel_gpu/src/graph/program.cpp

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2001,15 +2001,9 @@ void program::load(cldnn::BinaryInputBuffer& ib,
20012001
}
20022002
}
20032003

2004-
const bool can_use_mmap_zero_copy = ib.is_mmap_tensor_4K_aligned() && _engine.get_device_info().arch >= gpu_arch::xe2 &&
2005-
_engine.get_device_info().dev_type == device_type::integrated_gpu && !_config.get_enable_weightless();
20062004
memory_ptr model_tensor_base_ptr = nullptr;
2007-
if (can_use_mmap_zero_copy) {
2008-
model_tensor_base_ptr =
2009-
ib.get_engine().create_mmap_hostbuffer(ib.get_mmap_tensor(),
2010-
ib.get_stream_size(),
2011-
allocation_type::usm_host,
2012-
layout({{static_cast<tensor::value_type>(ib.get_stream_size()), 1, 1, 1}, data_types::u8, format::bfyx}));
2005+
if (!_config.get_enable_weightless()) {
2006+
model_tensor_base_ptr = ib.get_engine().bind_host_owned_buffer(ib.get_mmap_tensor(), ib.get_stream_size());
20132007
}
20142008

20152009
size_t num_nodes;
@@ -2198,10 +2192,9 @@ void program::load(cldnn::BinaryInputBuffer& ib,
21982192
state_initializers[variable_id] = initializers;
21992193
}
22002194

2201-
// At the end of load
2195+
// Skip padding bytes (page alignment required for binding host-owned buffers)
22022196
if (!ib.is_encrypted() && !ib.is_offset_page_aligned()) {
2203-
std::vector<uint8_t> pad(ib.get_bytes_to_page_boundary(), 0);
2204-
ib >> make_data(pad.data(), pad.size());
2197+
ib.seek_current_ptr(ib.get_bytes_to_page_boundary());
22052198
}
22062199
}
22072200

src/plugins/intel_gpu/src/runtime/engine.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "intel_gpu/runtime/stream.hpp"
99
#include "intel_gpu/runtime/device_query.hpp"
1010
#include "intel_gpu/runtime/debug_configuration.hpp"
11+
#include "intel_gpu/graph/serialization/binary_buffer.hpp"
1112

1213
#include "ocl/ocl_engine_factory.hpp"
1314
#include "ze/ze_engine_factory.hpp"
@@ -97,6 +98,30 @@ uint64_t engine::get_host_memory_size() const {
9798
return static_cast<uint64_t>(get_cpu_ram_size());
9899
}
99100

101+
bool engine::can_bind_host_buffer(const void* host_address) const {
102+
const auto& info = get_device_info();
103+
104+
// Check hardware capabilities
105+
if (info.dev_type != device_type::integrated_gpu)
106+
return false;
107+
108+
if (info.arch < gpu_arch::xe2)
109+
return false;
110+
111+
if (!supports_allocation(allocation_type::usm_host))
112+
return false;
113+
114+
// Buffer address must be provided and page-aligned
115+
if (host_address == nullptr)
116+
return false;
117+
118+
const auto address = reinterpret_cast<std::uintptr_t>(host_address);
119+
if (address % CACHE_PAGE_SIZE != 0)
120+
return false;
121+
122+
return true;
123+
}
124+
100125
bool engine::supports_allocation(allocation_type type) const {
101126
if (memory_capabilities::is_usm_type(type) && !use_unified_shared_memory())
102127
return false;

src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -238,23 +238,29 @@ memory::ptr ocl_engine::create_subbuffer(const memory& memory, const layout& new
238238
}
239239
}
240240

241-
memory_ptr ocl_engine::create_mmap_hostbuffer(const void* mmapped_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) {
241+
memory_ptr ocl_engine::bind_host_owned_buffer(const void* host_address, size_t data_size) {
242+
// Validate that host-backed memory is supported for this buffer
243+
if (!can_bind_host_buffer(host_address)) {
244+
return nullptr; // Validation failed - return nullptr for graceful fallback
245+
}
246+
247+
// All validation passed - create the host-backed buffer
248+
constexpr allocation_type type = allocation_type::usm_host;
249+
layout buffer_layout({{static_cast<tensor::value_type>(data_size), 1, 1, 1}, data_types::u8, format::bfyx});
250+
242251
auto tracker = std::make_shared<MemoryTracker>(this,
243-
const_cast<void*>(mmapped_address), // Point directly to mmap'd memory
252+
const_cast<void*>(host_address),
244253
data_size,
245-
_allocation_type);
246-
std::uintptr_t mmap_address = reinterpret_cast<std::uintptr_t>(mmapped_address);
247-
std::uintptr_t aligned_addr = mmap_address & ~(static_cast<std::uintptr_t>(cldnn::CACHE_PAGE_SIZE) - 1);
248-
void* mmap_aligned_address = reinterpret_cast<void*>(aligned_addr);
254+
type);
249255

250256
cl_int err = CL_SUCCESS;
251257
cl_mem_flags flags = CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR;
252258
#ifdef CL_MEM_FORCE_HOST_MEMORY_INTEL
253259
flags |= CL_MEM_FORCE_HOST_MEMORY_INTEL;
254260
#endif
255-
cl::Buffer buffer(get_cl_context(), flags, data_size, mmap_aligned_address, &err);
256-
OPENVINO_ASSERT(err == CL_SUCCESS, "clcreatebuffer with CL_MEM_USE_HOST_PTR and CL_MEM_FORCE_HOST_MEMORY_INTEL failed!");
257-
return std::make_shared<ocl::gpu_buffer>(this, output_layout, buffer, tracker);
261+
cl::Buffer buffer(get_cl_context(), flags, data_size, const_cast<void*>(host_address), &err);
262+
OPENVINO_ASSERT(err == CL_SUCCESS, "[GPU] Failed to create host-backed buffer with CL_MEM_USE_HOST_PTR and CL_MEM_FORCE_HOST_MEMORY_INTEL");
263+
return std::make_shared<ocl::gpu_buffer>(this, buffer_layout, buffer, tracker);
258264
}
259265

260266
memory::ptr ocl_engine::reinterpret_buffer(const memory& memory, const layout& new_layout) {

src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ class ocl_engine : public engine {
2626
memory_ptr allocate_memory(const layout& layout, allocation_type type, bool reset = true) override;
2727
memory_ptr reinterpret_handle(const layout& new_layout, shared_mem_params params) override;
2828
memory_ptr create_subbuffer(const memory& memory, const layout& new_layout, size_t offset) override;
29-
memory_ptr create_mmap_hostbuffer(const void* mmapped_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override;
29+
memory_ptr bind_host_owned_buffer(const void* host_address, size_t data_size) override;
3030
memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) override;
3131
memory_ptr import_buffer(const layout&, ov::intel_gpu::os_handle_param external_handle) override;
3232
bool is_the_same_buffer(const memory& mem1, const memory& mem2) override;

src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ memory::ptr sycl_engine::create_subbuffer(const memory& memory, const layout& ne
151151
}
152152
}
153153

154-
memory_ptr sycl_engine::create_mmap_hostbuffer(const void* mmapped_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) {
154+
memory_ptr sycl_engine::bind_host_owned_buffer(const void* host_address, size_t data_size) {
155155
OPENVINO_NOT_IMPLEMENTED;
156156
}
157157

src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class sycl_engine : public engine {
3030
memory_ptr allocate_memory(const layout& layout, allocation_type type, bool reset = true) override;
3131
memory_ptr reinterpret_handle(const layout& new_layout, shared_mem_params params) override;
3232
memory_ptr create_subbuffer(const memory& memory, const layout& new_layout, size_t offset) override;
33-
memory_ptr create_mmap_hostbuffer(const void* mmapped_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override;
33+
memory_ptr bind_host_owned_buffer(const void* host_address, size_t data_size) override;
3434
memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) override;
3535
memory_ptr import_buffer(const layout& layout, ov::intel_gpu::os_handle_param external_handle) override;
3636
bool is_the_same_buffer(const memory& mem1, const memory& mem2) override;

src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ memory_ptr ze_engine::create_subbuffer(const memory& memory, const layout& new_l
171171
memory.get_mem_tracker());
172172
}
173173

174-
memory_ptr ze_engine::create_mmap_hostbuffer(const void* mmapped_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) {
174+
memory_ptr ze_engine::bind_host_owned_buffer(const void* host_address, size_t data_size) {
175175
OPENVINO_NOT_IMPLEMENTED;
176176
}
177177

0 commit comments

Comments
 (0)