From a7986c7b55e0fd09004a739936155cc7eaa1dc85 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:34:42 +0000 Subject: [PATCH 01/84] Add OCL remote tensor creation from file --- .../include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 82dbc0f5c977f7..4d310a73f456cc 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -346,6 +346,18 @@ class ClContext : public RemoteContext { return create_tensor(type, shape, params).as(); } + /** + * @brief This function is used to obtain a remote tensor object from a file. + * @param type Tensor element type + * @param shape Tensor shape + * @param file_path Path to the file containing tensor data + * @return A remote tensor instance + */ + ClBufferTensor create_tensor(const element::Type type, const Shape& shape, const std::filesystem::path& file_path) { + auto data = ov::read_tensor_data(file_path, type, shape); + return create_tensor(type, shape, VirtualAddressMemory{std::as_const(data).data(), static_cast(data.get_byte_size())}); + } + /** * @brief This function is used to obtain remote tensor object from user-supplied USM pointer * @param type Tensor element type From 0b7a358b85554ab7f2667e948b322390313084ee Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:35:14 +0000 Subject: [PATCH 02/84] Retain mapped data in OCL buffer tensor --- src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 4d310a73f456cc..879f09c7cc1033 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -53,6 +53,11 @@ using VirtualAddressMemory = ov::intel_gpu::VirtualAddressMemory; */ class ClBufferTensor : public RemoteTensor { public: + using RemoteTensor::RemoteTensor; + + ClBufferTensor(const RemoteTensor& tensor, std::shared_ptr data) + : RemoteTensor(tensor), m_data(std::move(data)) {} + /** * @brief Checks that type defined runtime parameters are presented in remote object * @param tensor a tensor to check From f28d994ff0d2c943aff6297181f6d093cb2b05d6 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:35:42 +0000 Subject: [PATCH 03/84] Keep file mapping alive for OCL buffer tensor --- src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 879f09c7cc1033..0884e58736be7c 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -95,6 +95,9 @@ class ClBufferTensor : public RemoteTensor { operator cl::Buffer() { return cl::Buffer(get(), true); } + +private: + std::shared_ptr m_data; }; /** From 4acc8a7bde70f23f3dd369dbb233b2b71a87c659 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:36:12 +0000 Subject: [PATCH 04/84] Keep mapped file data alive for OCL tensor --- .../include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 0884e58736be7c..bace223ec4ad45 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -362,8 +362,11 @@ class ClContext : public RemoteContext { * @return A remote tensor instance */ ClBufferTensor create_tensor(const element::Type type, const Shape& shape, const std::filesystem::path& file_path) { - auto data = ov::read_tensor_data(file_path, type, shape); - return create_tensor(type, shape, VirtualAddressMemory{std::as_const(data).data(), static_cast(data.get_byte_size())}); + auto data = std::make_shared(ov::read_tensor_data(file_path, type, shape)); + auto tensor = create_tensor(type, + shape, + VirtualAddressMemory{std::as_const(*data).data(), static_cast(data->get_byte_size())}); + return ClBufferTensor(tensor, std::move(data)); } /** From b6c40783b562e5ad155ec655b97b7f274946a3b5 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:36:30 +0000 Subject: [PATCH 05/84] Include file stream for OCL file tensor test --- .../functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index 82b6e1a33e71d7..e9d112a861d63c 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -5,6 +5,7 @@ #ifdef OV_GPU_WITH_OCL_RT #include +#include #include "openvino/core/preprocess/pre_post_process.hpp" #include "openvino/op/add.hpp" From a9bf0fd45dbbfdbe155e6a4da549f2ad00317d62 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:37:01 +0000 Subject: [PATCH 06/84] Test OCL remote tensor creation from file --- .../ocl_remote_tensor_tests.cpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index e9d112a861d63c..b4b262a97c371e 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -603,6 +603,28 @@ TEST_P(OVRemoteTensorInputBlob_Test, smoke_canInputOutputRemoteTensor) { } } +TEST(OVRemoteTensorTests, smoke_CreateTensorFromFile) { +#if defined(ANDROID) + GTEST_SKIP(); +#endif + const ov::Shape shape{1, 2, 2, 2}; + const std::filesystem::path file_path{"ocl_remote_tensor_file.bin"}; + std::vector values(ov::shape_size(shape), 1.0f); + { + std::ofstream file(file_path, std::ios::binary); + file.write(reinterpret_cast(values.data()), values.size() * sizeof(float)); + } + + auto core = ov::Core(); + auto context = core.get_default_context(ov::test::utils::DEVICE_GPU).as(); + auto tensor = context.create_tensor(ov::element::f32, shape, file_path); + + EXPECT_TRUE(tensor.is()); + EXPECT_NE(tensor.get(), nullptr); + + std::filesystem::remove(file_path); +} + INSTANTIATE_TEST_SUITE_P( smoke_GPU, OVRemoteTensorInputBlob_Test, From 98bf157d59fcda43616129bb57e825b684f19f74 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:37:18 +0000 Subject: [PATCH 07/84] Include filesystem for OCL file tensor API --- src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index bace223ec4ad45..855945a9d943dd 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -10,6 +10,7 @@ */ #pragma once +#include #include #include From 3ead730ea768c31a4ca2c93089b54ce6b707dc3e Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:37:36 +0000 Subject: [PATCH 08/84] Include filesystem for OCL file tensor test --- .../functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index b4b262a97c371e..7647fab2ca9bd8 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -5,6 +5,7 @@ #ifdef OV_GPU_WITH_OCL_RT #include +#include #include #include "openvino/core/preprocess/pre_post_process.hpp" From 419fd3089806ad17ea11f1b6050a56991c105814 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:40:05 +0000 Subject: [PATCH 09/84] Add offset to OCL file tensor creation --- .../include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 855945a9d943dd..694e8c4c5d874a 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -360,10 +360,14 @@ class ClContext : public RemoteContext { * @param type Tensor element type * @param shape Tensor shape * @param file_path Path to the file containing tensor data + * @param offset_in_bytes Offset in bytes from the beginning of the file * @return A remote tensor instance */ - ClBufferTensor create_tensor(const element::Type type, const Shape& shape, const std::filesystem::path& file_path) { - auto data = std::make_shared(ov::read_tensor_data(file_path, type, shape)); + ClBufferTensor create_tensor(const element::Type type, + const Shape& shape, + const std::filesystem::path& file_path, + std::size_t offset_in_bytes = 0) { + auto data = std::make_shared(ov::read_tensor_data(file_path, type, shape, offset_in_bytes)); auto tensor = create_tensor(type, shape, VirtualAddressMemory{std::as_const(*data).data(), static_cast(data->get_byte_size())}); From 7bf2f39c17c17ee60d65ff5c49e2c494b3e65fd3 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:42:46 +0000 Subject: [PATCH 10/84] Keep OCL file tensor API lightweight --- src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 694e8c4c5d874a..1509e657c5835b 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -11,7 +11,6 @@ #pragma once #include -#include #include #include "openvino/runtime/core.hpp" From bf129a3b10d0127721c870926badb56863fc96da Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:43:29 +0000 Subject: [PATCH 11/84] Remove file mapping state from OCL buffer tensor --- src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 1509e657c5835b..6b1c3048e14fdb 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -53,11 +53,6 @@ using VirtualAddressMemory = ov::intel_gpu::VirtualAddressMemory; */ class ClBufferTensor : public RemoteTensor { public: - using RemoteTensor::RemoteTensor; - - ClBufferTensor(const RemoteTensor& tensor, std::shared_ptr data) - : RemoteTensor(tensor), m_data(std::move(data)) {} - /** * @brief Checks that type defined runtime parameters are presented in remote object * @param tensor a tensor to check From 86e180926d732231583f57284c11e569da14a207 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:44:09 +0000 Subject: [PATCH 12/84] Restore OCL buffer tensor wrapper --- src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 6b1c3048e14fdb..dc8c83d1014eae 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -90,9 +90,6 @@ class ClBufferTensor : public RemoteTensor { operator cl::Buffer() { return cl::Buffer(get(), true); } - -private: - std::shared_ptr m_data; }; /** From 1094be6e7240d3e151306c540f051e1955bee465 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:44:42 +0000 Subject: [PATCH 13/84] Use direct mapped data for OCL file tensor --- .../include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index dc8c83d1014eae..839df21b37de56 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -358,11 +358,8 @@ class ClContext : public RemoteContext { const Shape& shape, const std::filesystem::path& file_path, std::size_t offset_in_bytes = 0) { - auto data = std::make_shared(ov::read_tensor_data(file_path, type, shape, offset_in_bytes)); - auto tensor = create_tensor(type, - shape, - VirtualAddressMemory{std::as_const(*data).data(), static_cast(data->get_byte_size())}); - return ClBufferTensor(tensor, std::move(data)); + auto data = ov::read_tensor_data(file_path, type, shape, offset_in_bytes); + return create_tensor(type, shape, VirtualAddressMemory{std::as_const(data).data(), static_cast(data.get_byte_size())}); } /** From 3e1b39591fd5010f6fb5d02c29305d17a8253408 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:45:08 +0000 Subject: [PATCH 14/84] Exercise OCL file tensor offset --- .../functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index 7647fab2ca9bd8..a91a5d913c1bf6 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -611,8 +611,10 @@ TEST(OVRemoteTensorTests, smoke_CreateTensorFromFile) { const ov::Shape shape{1, 2, 2, 2}; const std::filesystem::path file_path{"ocl_remote_tensor_file.bin"}; std::vector values(ov::shape_size(shape), 1.0f); + constexpr std::size_t offset = 4096; { std::ofstream file(file_path, std::ios::binary); + file.seekp(offset); file.write(reinterpret_cast(values.data()), values.size() * sizeof(float)); } From 74595d4fd42e60ebd6aaab492f34994f133f78f1 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:45:31 +0000 Subject: [PATCH 15/84] Create OCL file tensor with offset --- .../functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index a91a5d913c1bf6..7a30f403eb53ba 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -620,7 +620,7 @@ TEST(OVRemoteTensorTests, smoke_CreateTensorFromFile) { auto core = ov::Core(); auto context = core.get_default_context(ov::test::utils::DEVICE_GPU).as(); - auto tensor = context.create_tensor(ov::element::f32, shape, file_path); + auto tensor = context.create_tensor(ov::element::f32, shape, file_path, offset); EXPECT_TRUE(tensor.is()); EXPECT_NE(tensor.get(), nullptr); From 2b75fb4214c38f4c159ee3e3f8bba211d7599e3f Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:51:00 +0000 Subject: [PATCH 16/84] Document OCL remote tensor creation from file --- .../assets/snippets/gpu/remote_objects_creation.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp b/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp index b62af47e3490c6..5ca4355c8830c9 100644 --- a/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp +++ b/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp @@ -69,6 +69,13 @@ int main() { //! [wrap_cpu_pointer] } +{ + //! [wrap_file] + std::filesystem::path file_path{"input.bin"}; + auto remote_tensor = gpu_context.create_tensor(in_element_type, in_shape, file_path); + //! [wrap_file] +} + { //! [wrap_cl_mem] cl_mem shared_buffer = allocate_cl_mem(input_size); From 86f83e2718f5c2dfeca26bb9ab40ed3db9a54a61 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 14:51:24 +0000 Subject: [PATCH 17/84] Document file tensor offset --- .../articles_en/assets/snippets/gpu/remote_objects_creation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp b/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp index 5ca4355c8830c9..c2623d8a3c553a 100644 --- a/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp +++ b/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp @@ -72,7 +72,7 @@ int main() { { //! [wrap_file] std::filesystem::path file_path{"input.bin"}; - auto remote_tensor = gpu_context.create_tensor(in_element_type, in_shape, file_path); + auto remote_tensor = gpu_context.create_tensor(in_element_type, in_shape, file_path, 4096); //! [wrap_file] } From 6011f1bee6d61bce22234e33464fbf52fdf6ddb9 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:00:25 +0000 Subject: [PATCH 18/84] Include filesystem for GPU file descriptor property --- .../include/openvino/runtime/intel_gpu/remote_properties.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp index c6cd21d1140236..3823772e84790c 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp @@ -10,6 +10,8 @@ */ #pragma once +#include + #include "openvino/runtime/properties.hpp" namespace ov { From b8c7729cdf2e6450be60d5613c19fca0fa97f3c2 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:00:54 +0000 Subject: [PATCH 19/84] Add MMAPED_FILE shared mem type for GPU --- .../include/openvino/runtime/intel_gpu/remote_properties.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp index 3823772e84790c..0667b530776551 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp @@ -121,6 +121,7 @@ enum class SharedMemType { BUFFER_FROM_HANDLE = 7, //!< OS-level external memory handle (e.g. DX12 NT handle on Windows, //!< DMA-BUF fd on Linux) imported by the plugin into a cl_mem CPU_VA = 8, //!< Shared mmap-backed/aligned allocated host pointer mapped by plugin + MMAPED_FILE = 9, //!< Memory-mapped file buffer read and wrapped by the plugin }; /** From deeb6421fd684f7b3db09c11722d0e618251a1af Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:01:26 +0000 Subject: [PATCH 20/84] Serialize MMAPED_FILE shared mem type --- .../include/openvino/runtime/intel_gpu/remote_properties.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp index 0667b530776551..2bc7cb43ca5a57 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp @@ -154,6 +154,8 @@ inline std::ostream& operator<<(std::ostream& os, const SharedMemType& share_mem return os << "DX_BUFFER"; case SharedMemType::BUFFER_FROM_HANDLE: return os << "BUFFER_FROM_HANDLE"; + case SharedMemType::MMAPED_FILE: + return os << "MMAPED_FILE"; default: OPENVINO_THROW("Unsupported memory type"); } From 2477fd55dff2c0de0870612cf5a4ba0483200568 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:01:57 +0000 Subject: [PATCH 21/84] Deserialize MMAPED_FILE shared mem type --- .../include/openvino/runtime/intel_gpu/remote_properties.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp index 2bc7cb43ca5a57..4ff494e826468a 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp @@ -182,6 +182,8 @@ inline std::istream& operator>>(std::istream& is, SharedMemType& share_mem_type) share_mem_type = SharedMemType::DX_BUFFER; } else if (str == "BUFFER_FROM_HANDLE") { share_mem_type = SharedMemType::BUFFER_FROM_HANDLE; + } else if (str == "MMAPED_FILE") { + share_mem_type = SharedMemType::MMAPED_FILE; } else { OPENVINO_THROW("Unsupported memory type: ", str); } From acffc73fe7c81fbe8c030d2eb075f42a65807200 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:04:05 +0000 Subject: [PATCH 22/84] Include GPU debug logging in ocl_engine --- src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp index d7c458429f4899..8de7eeef9a6c5c 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp @@ -3,6 +3,7 @@ // #include "ocl_engine.hpp" +#include "intel_gpu/runtime/debug_configuration.hpp" #include "intel_gpu/runtime/utils.hpp" #include "intel_gpu/graph/serialization/binary_buffer.hpp" // For CACHE_PAGE_SIZE #include "openvino/runtime/intel_gpu/remote_properties.hpp" From ade06dc34d9c8f6a86dde787147880e9cdc2319a Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:04:33 +0000 Subject: [PATCH 23/84] Log host buffer creation in ocl_engine --- src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp index 8de7eeef9a6c5c..fb29192ad5bac9 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp @@ -395,6 +395,9 @@ memory_ptr ocl_engine::create_hostbuffer_impl(void* cpu_address, size_t data_siz OPENVINO_ASSERT(err == CL_SUCCESS, "clCreateBuffer with CL_MEM_USE_HOST_PTR failed!"); #endif + GPU_DEBUG_TRACE_DETAIL << "Created host buffer of " << data_size << " bytes from cpu address " << cpu_address + << " (access_flags=" << access_flags << ", allocation type=" << allocation << ")" << std::endl; + std::shared_ptr tracker = nullptr; #ifdef CL_MEM_FORCE_HOST_MEMORY_INTEL tracker = std::make_shared(nullptr, From b698fdd55e32570381645845e066bcaad076b278 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:05:53 +0000 Subject: [PATCH 24/84] Add FileDescriptor and mmap file properties for GPU --- .../runtime/intel_gpu/remote_properties.hpp | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp index 4ff494e826468a..5429d8fd3ff1bb 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp @@ -267,5 +267,36 @@ struct VirtualAddressMemory { void* ptr = nullptr; int64_t size = -1; ///< Buffer size in bytes; -1 means "derive from tensor shape" }; + +/** + * @brief File descriptor for wrapping tensor data memory-mapped from a file as a GPU plugin tensor. + * The plugin memory-maps the file and keeps the mapping alive for the whole tensor lifetime, + * so the file must not be modified until the returned tensor is destroyed. + * @ingroup ov_runtime_ocl_gpu_cpp_api + */ +struct FileDescriptor { + explicit FileDescriptor(const std::filesystem::path& file_path, std::size_t offset_in_bytes = 0) + : path(file_path), + offset(offset_in_bytes) { + OPENVINO_ASSERT(!file_path.empty(), "[GPU] Provided file path is empty."); + } + + std::filesystem::path path; ///< File path + std::size_t offset = 0; ///< Offset in bytes to read from the file +}; + +/** + * @brief This key identifies the file path + * in a memory-mapped tensor parameter map. + * @ingroup ov_runtime_ocl_gpu_cpp_api + */ +static constexpr Property file_path{"FILE_PATH"}; + +/** + * @brief This key identifies the offset in bytes from the beginning of the file + * in a memory-mapped tensor parameter map. + * @ingroup ov_runtime_ocl_gpu_cpp_api + */ +static constexpr Property file_offset{"FILE_OFFSET"}; } // namespace intel_gpu } // namespace ov From 55d73a0252243294e2fe590966b677700691b1cb Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:06:22 +0000 Subject: [PATCH 25/84] Route OCL file tensor through plugin mmap path --- .../include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 839df21b37de56..ec9fe0912f41b0 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -358,8 +358,10 @@ class ClContext : public RemoteContext { const Shape& shape, const std::filesystem::path& file_path, std::size_t offset_in_bytes = 0) { - auto data = ov::read_tensor_data(file_path, type, shape, offset_in_bytes); - return create_tensor(type, shape, VirtualAddressMemory{std::as_const(data).data(), static_cast(data.get_byte_size())}); + AnyMap params = {{ov::intel_gpu::shared_mem_type.name(), ov::intel_gpu::SharedMemType::MMAPED_FILE}, + {ov::intel_gpu::file_path.name(), file_path.string()}, + {ov::intel_gpu::file_offset.name(), offset_in_bytes}}; + return create_tensor(type, shape, params).as(); } /** From 329a3a8e3d7808a06dea2711dd5af5beae9473ef Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:06:54 +0000 Subject: [PATCH 26/84] Include ov::Tensor for mmap keep-alive --- src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp index cdbb460354cbe2..c39898f967e5c6 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp @@ -19,6 +19,7 @@ #endif #include "openvino/runtime/iremote_tensor.hpp" #include "openvino/runtime/intel_gpu/remote_properties.hpp" +#include "openvino/runtime/tensor.hpp" #include "intel_gpu/runtime/memory_caps.hpp" #include "intel_gpu/runtime/memory.hpp" From 1708c0887a61f5a82d7fe3e20bf60239a1b8b196 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:07:14 +0000 Subject: [PATCH 27/84] Accept mmap tensor keep-alive in remote tensor --- .../intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp index c39898f967e5c6..4952bf60b8d339 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp @@ -44,7 +44,8 @@ class RemoteTensorImpl : public ov::IRemoteTensor { cldnn::shared_surface surf = 0, uint32_t plane = 0, ov::intel_gpu::SharedBufferHandle shared_buffer_handle = {}, - ov::intel_gpu::VirtualAddressMemory va_mem = ov::intel_gpu::VirtualAddressMemory(nullptr)); + ov::intel_gpu::VirtualAddressMemory va_mem = ov::intel_gpu::VirtualAddressMemory(nullptr), + ov::Tensor mmap_tensor = {}); ~RemoteTensorImpl() override; const AnyMap& get_properties() const override; From ff78ef5ec6c61d05ad4b2fd3fa982de739259f75 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:07:43 +0000 Subject: [PATCH 28/84] Store mmap tensor keep-alive in remote tensor --- src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp index 4952bf60b8d339..3fdc21df7c8498 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp @@ -90,6 +90,7 @@ class RemoteTensorImpl : public ov::IRemoteTensor { uint32_t m_plane; ov::intel_gpu::SharedBufferHandle m_shared_buffer_handle; ov::intel_gpu::VirtualAddressMemory m_va_mem; + ov::Tensor m_mmap_tensor; // keeps the file mapping alive for the whole tensor lifetime size_t m_hash = 0; bool supports_caching() const; From c257ee569fdb5e0bebd1936fa705347c0de0d1b0 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:08:07 +0000 Subject: [PATCH 29/84] Keep file mapping alive in remote tensor ctor --- src/plugins/intel_gpu/src/plugin/remote_tensor.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp index 8751f2e62130ac..90437866fd0e8e 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp @@ -170,7 +170,8 @@ RemoteTensorImpl::RemoteTensorImpl(RemoteContextImpl::Ptr context, cldnn::shared_surface surf, uint32_t plane, ov::intel_gpu::SharedBufferHandle shared_buffer_handle, - ov::intel_gpu::VirtualAddressMemory va_mem) + ov::intel_gpu::VirtualAddressMemory va_mem, + ov::Tensor mmap_tensor) : m_context(context) , m_element_type(element_type) , m_shape(shape) @@ -180,7 +181,8 @@ RemoteTensorImpl::RemoteTensorImpl(RemoteContextImpl::Ptr context, , m_surf(surf) , m_plane(plane) , m_shared_buffer_handle(shared_buffer_handle) - , m_va_mem(va_mem) { + , m_va_mem(va_mem) + , m_mmap_tensor(std::move(mmap_tensor)) { update_hash(); allocate(); } From f0e0fe423a8c9dd1520518568efcd197110b168d Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:08:49 +0000 Subject: [PATCH 30/84] Include read_tensor_data for GPU mmap tensor --- src/plugins/intel_gpu/src/plugin/remote_context.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/intel_gpu/src/plugin/remote_context.cpp b/src/plugins/intel_gpu/src/plugin/remote_context.cpp index 54b61e449b21ad..ddd50b1e2f76e1 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_context.cpp @@ -4,6 +4,7 @@ #include "openvino/runtime/intel_gpu/remote_properties.hpp" #include "openvino/runtime/make_tensor.hpp" +#include "openvino/runtime/tensor.hpp" #include "intel_gpu/plugin/remote_context.hpp" #include "intel_gpu/plugin/remote_tensor.hpp" #include "intel_gpu/plugin/usm_host_tensor.hpp" From 12b928d214c988c31cb66c8f6b8f38e4d1d7fe73 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:09:20 +0000 Subject: [PATCH 31/84] Handle MMAPED_FILE shared memory type --- src/plugins/intel_gpu/src/plugin/remote_context.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/plugins/intel_gpu/src/plugin/remote_context.cpp b/src/plugins/intel_gpu/src/plugin/remote_context.cpp index ddd50b1e2f76e1..bf648e6c801e0e 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_context.cpp @@ -188,6 +188,10 @@ ov::SoPtr RemoteContextImpl::create_tensor(const ov::element: mem = extract_object(params, ov::intel_gpu::cpu_va); auto size = extract_object(params, ov::intel_gpu::cpu_va_size); return { reuse_memory_from_cpu_va(type, shape, VirtualAddressMemory{mem, size}, tensor_type), nullptr }; + } else if (ov::intel_gpu::SharedMemType::MMAPED_FILE == mem_type) { + const auto path = extract_object(params, ov::intel_gpu::file_path); + const auto offset = extract_object(params, ov::intel_gpu::file_offset); + return { reuse_memory_from_file(type, shape, path, offset), nullptr }; } else if (ov::intel_gpu::SharedMemType::OCL_IMAGE2D == mem_type) { tensor_type = TensorType::BT_IMG_SHARED; mem = extract_object(params, ov::intel_gpu::mem_handle); From 562dc5edefcd9509314757b4b975984acf745924 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:09:54 +0000 Subject: [PATCH 32/84] Add file-mapped remote tensor creation --- .../intel_gpu/src/plugin/remote_context.cpp | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/plugins/intel_gpu/src/plugin/remote_context.cpp b/src/plugins/intel_gpu/src/plugin/remote_context.cpp index bf648e6c801e0e..89e0e5064d27b7 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_context.cpp @@ -278,6 +278,27 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_handle(c return std::make_shared(get_this_shared_ptr(), shape, type, tensor_type, nullptr, 0, 0, handle); } +std::shared_ptr RemoteContextImpl::reuse_memory_from_file(const ov::element::Type type, + const ov::Shape& shape, + const std::string& file_path, + size_t offset) { + // Memory-map the file. The mapping is retained inside the RemoteTensorImpl so it stays + // alive for the whole tensor lifetime (GPU wraps the host pointer via CL_MEM_USE_HOST_PTR). + auto mmap_tensor = ov::read_tensor_data(file_path, type, shape, offset); + void* data_ptr = std::as_const(mmap_tensor).data(); + const auto size = static_cast(mmap_tensor.get_byte_size()); + return std::make_shared(get_this_shared_ptr(), + shape, + type, + TensorType::BT_CPU_VA, + nullptr, + 0, + 0, + ov::intel_gpu::SharedBufferHandle{}, + VirtualAddressMemory{data_ptr, size}, + mmap_tensor); +} + std::shared_ptr RemoteContextImpl::create_buffer(const ov::element::Type type, const ov::Shape& shape) { return std::make_shared(get_this_shared_ptr(), shape, type, TensorType::BT_BUF_INTERNAL); } From 45b0b57d274013ddc551c9f0d9acdd9a76bfd1dd Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:10:22 +0000 Subject: [PATCH 33/84] Declare file-mapped remote tensor helper --- .../intel_gpu/include/intel_gpu/plugin/remote_context.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_context.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_context.hpp index 52062ee4619493..5876736efa8637 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_context.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_context.hpp @@ -85,6 +85,7 @@ class RemoteContextImpl : public ov::IRemoteContext { std::shared_ptr reuse_memory(const ov::element::Type type, const ov::Shape& shape, cldnn::shared_handle mem, TensorType tensor_type); std::shared_ptr reuse_memory_from_cpu_va(const ov::element::Type type, const ov::Shape& shape, VirtualAddressMemory cpu_va, TensorType tensor_type); std::shared_ptr reuse_memory_from_handle(const ov::element::Type type, const ov::Shape& shape, SharedBufferHandle handle, TensorType tensor_type); + std::shared_ptr reuse_memory_from_file(const ov::element::Type type, const ov::Shape& shape, const std::string& file_path, size_t offset); std::shared_ptr create_buffer(const ov::element::Type type, const ov::Shape& shape); std::shared_ptr create_usm(const ov::element::Type type, const ov::Shape& shape, TensorType alloc_type); void check_if_shared() const; From 1dd3990f2b8909184356ac3c57d1f858d45756f8 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:11:21 +0000 Subject: [PATCH 34/84] Expose FileDescriptor alias in ocl namespace --- src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index ec9fe0912f41b0..2c863a72bae98f 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -43,6 +43,7 @@ using gpu_handle_param = void*; using SharedBufferHandle = ov::intel_gpu::SharedBufferHandle; using VirtualAddressMemory = ov::intel_gpu::VirtualAddressMemory; +using FileDescriptor = ov::intel_gpu::FileDescriptor; /** * @brief This class represents an abstraction for GPU plugin remote tensor From d3a4fa00cd9af3306a6cdb9df6c135d13bbbf0b0 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:11:49 +0000 Subject: [PATCH 35/84] Use FileDescriptor for OCL file tensor API --- .../include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 2c863a72bae98f..6352e812b2f78b 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -349,19 +349,17 @@ class ClContext : public RemoteContext { /** * @brief This function is used to obtain a remote tensor object from a file. + * The plugin memory-maps the file and keeps the mapping alive for the whole tensor lifetime, + * so the file must not be modified until the returned tensor is destroyed. * @param type Tensor element type * @param shape Tensor shape - * @param file_path Path to the file containing tensor data - * @param offset_in_bytes Offset in bytes from the beginning of the file + * @param file_descriptor Descriptor with the path and offset of the file containing tensor data * @return A remote tensor instance */ - ClBufferTensor create_tensor(const element::Type type, - const Shape& shape, - const std::filesystem::path& file_path, - std::size_t offset_in_bytes = 0) { + ClBufferTensor create_tensor(const element::Type type, const Shape& shape, const FileDescriptor& file_descriptor) { AnyMap params = {{ov::intel_gpu::shared_mem_type.name(), ov::intel_gpu::SharedMemType::MMAPED_FILE}, - {ov::intel_gpu::file_path.name(), file_path.string()}, - {ov::intel_gpu::file_offset.name(), offset_in_bytes}}; + {ov::intel_gpu::file_path.name(), file_descriptor.path.string()}, + {ov::intel_gpu::file_offset.name(), file_descriptor.offset}}; return create_tensor(type, shape, params).as(); } From b554fd38e991e8c09161d4da13956046427da8e8 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:12:13 +0000 Subject: [PATCH 36/84] Drop unused filesystem include in ocl header --- src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 6352e812b2f78b..a23e9937eaf61e 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -10,7 +10,6 @@ */ #pragma once -#include #include #include "openvino/runtime/core.hpp" From 4228c847cac00cea540103ba3a38d0c0abd2e397 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:12:37 +0000 Subject: [PATCH 37/84] Use FileDescriptor in OCL file tensor test --- .../functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index 7a30f403eb53ba..662ea7c717b970 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -620,7 +620,7 @@ TEST(OVRemoteTensorTests, smoke_CreateTensorFromFile) { auto core = ov::Core(); auto context = core.get_default_context(ov::test::utils::DEVICE_GPU).as(); - auto tensor = context.create_tensor(ov::element::f32, shape, file_path, offset); + auto tensor = context.create_tensor(ov::element::f32, shape, ov::intel_gpu::FileDescriptor{file_path, offset}); EXPECT_TRUE(tensor.is()); EXPECT_NE(tensor.get(), nullptr); From d17f441a1792fdf97e5ff876c71aab457c392666 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:12:58 +0000 Subject: [PATCH 38/84] Document FileDescriptor-based GPU file tensor --- .../assets/snippets/gpu/remote_objects_creation.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp b/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp index c2623d8a3c553a..4d9520efc7e027 100644 --- a/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp +++ b/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp @@ -71,8 +71,10 @@ int main() { { //! [wrap_file] - std::filesystem::path file_path{"input.bin"}; - auto remote_tensor = gpu_context.create_tensor(in_element_type, in_shape, file_path, 4096); + // The plugin memory-maps the file and keeps the mapping alive for the tensor lifetime, + // so the file must not be modified until the returned tensor is destroyed. + ov::intel_gpu::FileDescriptor file_descriptor{"input.bin", /*offset_in_bytes=*/0}; + auto remote_tensor = gpu_context.create_tensor(in_element_type, in_shape, file_descriptor); //! [wrap_file] } From 278e02655e750b649de420999f56867369a91027 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:15:08 +0000 Subject: [PATCH 39/84] Document file-backed remote tensor creation tab --- .../gpu-device/remote-tensor-api-gpu-plugin.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst b/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst index 8a014404459f5f..ff4f3abc078131 100644 --- a/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst +++ b/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst @@ -267,6 +267,17 @@ For more details, see the code snippets below: The ``shape`` and ``element type`` must describe the same memory layout as the external buffer. The handle must remain valid for the whole lifetime of the created remote tensor. + .. tab-item:: file + :sync: file + + Use this overload to wrap tensor data stored in a file. The plugin memory-maps the file + and keeps the mapping alive for the whole lifetime of the created remote tensor, so the + file must not be modified until the tensor is destroyed. + + .. doxygensnippet:: docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp + :language: cpp + :fragment: [wrap_file] + .. tab-item:: biplanar NV12 surface :sync: biplanar-nv12-surface From fea3da040a3688e584b0d7fffef091a9b65539bb Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:17:47 +0000 Subject: [PATCH 40/84] Make OCL file tensor test run inference --- .../ocl_remote_tensor_tests.cpp | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index 662ea7c717b970..57aa20f701600e 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -608,22 +608,48 @@ TEST(OVRemoteTensorTests, smoke_CreateTensorFromFile) { #if defined(ANDROID) GTEST_SKIP(); #endif - const ov::Shape shape{1, 2, 2, 2}; + auto core = ov::Core(); + auto model = ov::test::behavior::getDefaultNGraphFunctionForTheDevice(); + auto compiled_model = core.compile_model(model, ov::test::utils::DEVICE_GPU); + + auto input = model->get_parameters().at(0); + auto output = model->get_results().at(0); + const ov::Shape input_shape{1, 2, 32, 32}; + + // Prepare input data and store it in a file at a page-aligned offset. + auto input_data = ov::test::utils::create_and_fill_tensor(input->get_element_type(), input_shape); const std::filesystem::path file_path{"ocl_remote_tensor_file.bin"}; - std::vector values(ov::shape_size(shape), 1.0f); - constexpr std::size_t offset = 4096; + constexpr std::size_t offset = 4096; // page-aligned (and cache-line aligned) offset { std::ofstream file(file_path, std::ios::binary); file.seekp(offset); - file.write(reinterpret_cast(values.data()), values.size() * sizeof(float)); + file.write(reinterpret_cast(input_data.data()), input_data.get_byte_size()); } - auto core = ov::Core(); - auto context = core.get_default_context(ov::test::utils::DEVICE_GPU).as(); - auto tensor = context.create_tensor(ov::element::f32, shape, ov::intel_gpu::FileDescriptor{file_path, offset}); + // Regular inference to get the reference output. + auto inf_req_regular = compiled_model.create_infer_request(); + inf_req_regular.set_tensor(input, input_data); + inf_req_regular.infer(); + auto output_tensor_regular = inf_req_regular.get_tensor(output); + + // Inference using a remote tensor memory-mapped from the file. + auto context = compiled_model.get_context().as(); + auto remote_tensor = + context.create_tensor(input->get_element_type(), input_shape, ov::intel_gpu::FileDescriptor{file_path, offset}); + ASSERT_TRUE(remote_tensor.is()); + + auto inf_req_shared = compiled_model.create_infer_request(); + inf_req_shared.set_tensor(input, remote_tensor); + inf_req_shared.infer(); + auto output_tensor_shared = inf_req_shared.get_tensor(output); - EXPECT_TRUE(tensor.is()); - EXPECT_NE(tensor.get(), nullptr); + // Compare results. + { + ASSERT_EQ(output_tensor_regular.get_size(), output_tensor_shared.get_size()); + OV_ASSERT_NO_THROW(output_tensor_regular.data()); + OV_ASSERT_NO_THROW(output_tensor_shared.data()); + ov::test::utils::compare(output_tensor_regular, output_tensor_shared); + } std::filesystem::remove(file_path); } From ef652b4cff443ff40ae98c5d9a05a6f8875b2071 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:22:57 +0000 Subject: [PATCH 41/84] Drop unused debug include in ocl_engine --- src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp index fb29192ad5bac9..2e50c6cf7d0b5d 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp @@ -3,7 +3,6 @@ // #include "ocl_engine.hpp" -#include "intel_gpu/runtime/debug_configuration.hpp" #include "intel_gpu/runtime/utils.hpp" #include "intel_gpu/graph/serialization/binary_buffer.hpp" // For CACHE_PAGE_SIZE #include "openvino/runtime/intel_gpu/remote_properties.hpp" From 6a0d59776ee9a8148dc68d966fcf7ff4867ae06a Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:23:17 +0000 Subject: [PATCH 42/84] Remove unnecessary host buffer logging --- src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp index 2e50c6cf7d0b5d..d7c458429f4899 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp @@ -394,9 +394,6 @@ memory_ptr ocl_engine::create_hostbuffer_impl(void* cpu_address, size_t data_siz OPENVINO_ASSERT(err == CL_SUCCESS, "clCreateBuffer with CL_MEM_USE_HOST_PTR failed!"); #endif - GPU_DEBUG_TRACE_DETAIL << "Created host buffer of " << data_size << " bytes from cpu address " << cpu_address - << " (access_flags=" << access_flags << ", allocation type=" << allocation << ")" << std::endl; - std::shared_ptr tracker = nullptr; #ifdef CL_MEM_FORCE_HOST_MEMORY_INTEL tracker = std::make_shared(nullptr, From 73f729f5dd2d2916d5f9d7696f1ef1675d715c77 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:30:11 +0000 Subject: [PATCH 43/84] Log memory when wrapping file-mapped host buffer --- src/plugins/intel_gpu/src/plugin/remote_tensor.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp index 90437866fd0e8e..48298b6be61212 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp @@ -375,8 +375,13 @@ void RemoteTensorImpl::allocate() { break; } case TensorType::BT_CPU_VA: { + const auto buffer_size = m_va_mem.size > -1 ? static_cast(m_va_mem.size) : m_layout.bytes_count(); + if (m_mmap_tensor) { + GPU_DEBUG_TRACE_DETAIL << "Wrapping file-mapped host buffer of " << buffer_size << " bytes at " << m_va_mem.ptr + << " into a RemoteTensor" << std::endl; + } m_memory_object = engine.create_hostbuffer(m_va_mem.ptr, - m_va_mem.size > -1 ? m_va_mem.size : m_layout.bytes_count(), + buffer_size, cldnn::allocation_type::cl_mem, m_layout); break; From 9f4f4be1fcc47e32b38e09b962c7ff01577fd3b8 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:30:39 +0000 Subject: [PATCH 44/84] Hash mmap tensor data pointer in remote tensor --- src/plugins/intel_gpu/src/plugin/remote_tensor.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp index 48298b6be61212..6c9dd349d30aff 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp @@ -439,6 +439,7 @@ void RemoteTensorImpl::update_hash() { m_hash = cldnn::hash_combine(0, m_mem); m_hash = cldnn::hash_combine(m_hash, m_shared_buffer_handle); m_hash = cldnn::hash_combine(m_hash, m_va_mem); + m_hash = cldnn::hash_combine(m_hash, m_mmap_tensor ? std::as_const(m_mmap_tensor).data() : nullptr); m_hash = cldnn::hash_combine(m_hash, m_surf); m_hash = cldnn::hash_combine(m_hash, m_plane); m_hash = cldnn::hash_combine(m_hash, m_shape.size()); From 6bc3ccfdbd86d90c9d9e1761287583ee4185cb02 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:38:15 +0000 Subject: [PATCH 45/84] Remove file-mapped host buffer debug trace --- src/plugins/intel_gpu/src/plugin/remote_tensor.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp index 6c9dd349d30aff..ef08f595624cd1 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp @@ -375,13 +375,8 @@ void RemoteTensorImpl::allocate() { break; } case TensorType::BT_CPU_VA: { - const auto buffer_size = m_va_mem.size > -1 ? static_cast(m_va_mem.size) : m_layout.bytes_count(); - if (m_mmap_tensor) { - GPU_DEBUG_TRACE_DETAIL << "Wrapping file-mapped host buffer of " << buffer_size << " bytes at " << m_va_mem.ptr - << " into a RemoteTensor" << std::endl; - } m_memory_object = engine.create_hostbuffer(m_va_mem.ptr, - buffer_size, + m_va_mem.size > -1 ? m_va_mem.size : m_layout.bytes_count(), cldnn::allocation_type::cl_mem, m_layout); break; From 0742fa71cc3ad39a148f579a1047dc29b04d4ca2 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:41:13 +0000 Subject: [PATCH 46/84] Add track_memory flag to create_hostbuffer --- src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp b/src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp index bb5d83c4f86e40..89f7da1e13e407 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp @@ -66,7 +66,8 @@ class engine { /// Created memory object by wrapping a writable host-allocated layout region. /// Backends that support access flags should use read-write permissions. - virtual memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) = 0; + /// When @p track_memory is true the wrapped host memory is accounted and logged by the engine's memory tracker. + virtual memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) = 0; /// Created memory object by wrapping a read-only host-allocated layout region. /// Backends that support access flags should use read-only permissions. From 7bf3b3d8abaf8fcf214182842111e43729961f07 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:41:36 +0000 Subject: [PATCH 47/84] Add track_memory flag to ocl create_hostbuffer --- src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp index 2d39a5013a594f..768a527fe6a743 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp @@ -26,7 +26,7 @@ class ocl_engine : public engine { memory_ptr allocate_memory(const layout& layout, allocation_type type, bool reset = true) override; memory_ptr reinterpret_handle(const layout& new_layout, shared_mem_params params) override; memory_ptr create_subbuffer(const memory& memory, const layout& new_layout, size_t offset) override; - memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; + memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) override; memory_ptr import_buffer(const layout&, ov::intel_gpu::os_handle_param external_handle) override; From 5407ead46dc84c6e5390545b16bc10dbc9d0bad3 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:41:55 +0000 Subject: [PATCH 48/84] Add track_memory flag to create_hostbuffer_impl --- src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp index 768a527fe6a743..5b8ec6b521e948 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp @@ -56,7 +56,7 @@ class ocl_engine : public engine { static std::shared_ptr create(const device::ptr device, runtime_types runtime_type); private: - memory_ptr create_hostbuffer_impl(void* cpu_address, size_t data_size, allocation_type allocation, const layout& output_layout, cl_mem_flags access_flags); + memory_ptr create_hostbuffer_impl(void* cpu_address, size_t data_size, allocation_type allocation, const layout& output_layout, cl_mem_flags access_flags, bool track_memory = false); std::string _extensions; }; From 1842d16f5d942bc13781ff9666bbc597f79f9914 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:42:14 +0000 Subject: [PATCH 49/84] Pass track_memory through ocl create_hostbuffer --- src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp index d7c458429f4899..c8a0a5b703fb43 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp @@ -244,8 +244,9 @@ memory::ptr ocl_engine::create_subbuffer(const memory& memory, const layout& new memory_ptr ocl_engine::create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, - const layout output_layout) { - return create_hostbuffer_impl(cpu_address, data_size, _allocation_type, output_layout, CL_MEM_READ_WRITE); + const layout output_layout, + bool track_memory) { + return create_hostbuffer_impl(cpu_address, data_size, _allocation_type, output_layout, CL_MEM_READ_WRITE, track_memory); } memory_ptr ocl_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) { From 10d4a695b5a25eb3d930757ff0957c063f1f027a Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:42:33 +0000 Subject: [PATCH 50/84] Accept track_memory in create_hostbuffer_impl --- src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp index c8a0a5b703fb43..138732ba5523e5 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp @@ -375,7 +375,7 @@ std::shared_ptr ocl_engine::create(const device::ptr device, runt return std::make_shared(device, runtime_type); } -memory_ptr ocl_engine::create_hostbuffer_impl(void* cpu_address, size_t data_size, allocation_type allocation, const layout& output_layout, cl_mem_flags access_flags) { +memory_ptr ocl_engine::create_hostbuffer_impl(void* cpu_address, size_t data_size, allocation_type allocation, const layout& output_layout, cl_mem_flags access_flags, bool track_memory) { cl_int err = CL_SUCCESS; cl_mem_flags flags = access_flags | CL_MEM_USE_HOST_PTR; From 72891690cad3da18be2ace58b044fe4ac381e153 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:42:56 +0000 Subject: [PATCH 51/84] Track mmap host memory when requested --- src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp index 138732ba5523e5..15fefe9c2609de 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp @@ -397,7 +397,10 @@ memory_ptr ocl_engine::create_hostbuffer_impl(void* cpu_address, size_t data_siz std::shared_ptr tracker = nullptr; #ifdef CL_MEM_FORCE_HOST_MEMORY_INTEL - tracker = std::make_shared(nullptr, + // Host memory imported via CL_MEM_FORCE_HOST_MEMORY_INTEL is owned by the caller, so it is not + // accounted by default. When track_memory is set (e.g. memory the plugin mmap-ed itself) the + // engine is passed so the allocation is tracked and logged. + tracker = std::make_shared(track_memory ? this : nullptr, cpu_address, data_size, allocation); From 98fe4e72e51489235e04a049786e766e9cd579f6 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:43:17 +0000 Subject: [PATCH 52/84] Match create_hostbuffer signature in ze_engine --- src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp b/src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp index 51c2067b10c766..608c73bd3eefc6 100644 --- a/src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp +++ b/src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp @@ -27,7 +27,7 @@ class ze_engine : public engine { memory_ptr import_buffer(const layout& layout, ov::intel_gpu::os_handle_param external_handle) override; memory_ptr reinterpret_handle(const layout& new_layout, shared_mem_params params) override; memory_ptr create_subbuffer(const memory& memory, const layout& new_layout, size_t byte_offset) override; - memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; + memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) override; bool is_the_same_buffer(const memory& mem1, const memory& mem2) override; From 2ca6957805f96f680a9279bcc80c467ef01ce1d7 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:43:34 +0000 Subject: [PATCH 53/84] Match create_hostbuffer signature in ze_engine --- src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp b/src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp index 3047d90298278a..a1345ef96c950e 100644 --- a/src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp @@ -171,7 +171,7 @@ memory_ptr ze_engine::create_subbuffer(const memory& memory, const layout& new_l memory.get_mem_tracker()); } -memory_ptr ze_engine::create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) { +memory_ptr ze_engine::create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory) { OPENVINO_NOT_IMPLEMENTED; } From 77b5549ef80dbb2632cb1b193acced167619e7d1 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:43:54 +0000 Subject: [PATCH 54/84] Match create_hostbuffer signature in sycl_engine --- src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp b/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp index a440d4a7c6a382..bddfd0750fdec2 100644 --- a/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp +++ b/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp @@ -30,7 +30,7 @@ class sycl_engine : public engine { memory_ptr allocate_memory(const layout& layout, allocation_type type, bool reset = true) override; memory_ptr reinterpret_handle(const layout& new_layout, shared_mem_params params) override; memory_ptr create_subbuffer(const memory& memory, const layout& new_layout, size_t offset) override; - memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; + memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) override; memory_ptr import_buffer(const layout& layout, ov::intel_gpu::os_handle_param external_handle) override; From 4a3176ca6c05e2d7a626326099e6faf86493f864 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:44:13 +0000 Subject: [PATCH 55/84] Match create_hostbuffer signature in sycl_engine --- src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp b/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp index d9ef60fe7971c9..7be247d4fed4ff 100644 --- a/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp @@ -151,7 +151,7 @@ memory::ptr sycl_engine::create_subbuffer(const memory& memory, const layout& ne } } -memory_ptr sycl_engine::create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) { +memory_ptr sycl_engine::create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory) { OPENVINO_NOT_IMPLEMENTED; } From 11bc1ad622b9321dc5659a272dc3c59f363653ab Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:44:34 +0000 Subject: [PATCH 56/84] Track file-mapped host memory in remote tensor --- src/plugins/intel_gpu/src/plugin/remote_tensor.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp index ef08f595624cd1..c919a26e4f8555 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp @@ -375,10 +375,13 @@ void RemoteTensorImpl::allocate() { break; } case TensorType::BT_CPU_VA: { + // Track (account and log) the host memory only when the plugin owns the mapping (file-mmap case). + const bool track_memory = static_cast(m_mmap_tensor); m_memory_object = engine.create_hostbuffer(m_va_mem.ptr, m_va_mem.size > -1 ? m_va_mem.size : m_layout.bytes_count(), cldnn::allocation_type::cl_mem, - m_layout); + m_layout, + track_memory); break; } #ifdef _WIN32 From 37e8f2efaafe5bef0109395a0e8fdbd0bfa219df Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:47:33 +0000 Subject: [PATCH 57/84] Move file tensor test next to CPU-memory test --- .../ocl_remote_tensor_tests.cpp | 50 ------------------- 1 file changed, 50 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index 57aa20f701600e..73cd8decb8785d 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -604,56 +604,6 @@ TEST_P(OVRemoteTensorInputBlob_Test, smoke_canInputOutputRemoteTensor) { } } -TEST(OVRemoteTensorTests, smoke_CreateTensorFromFile) { -#if defined(ANDROID) - GTEST_SKIP(); -#endif - auto core = ov::Core(); - auto model = ov::test::behavior::getDefaultNGraphFunctionForTheDevice(); - auto compiled_model = core.compile_model(model, ov::test::utils::DEVICE_GPU); - - auto input = model->get_parameters().at(0); - auto output = model->get_results().at(0); - const ov::Shape input_shape{1, 2, 32, 32}; - - // Prepare input data and store it in a file at a page-aligned offset. - auto input_data = ov::test::utils::create_and_fill_tensor(input->get_element_type(), input_shape); - const std::filesystem::path file_path{"ocl_remote_tensor_file.bin"}; - constexpr std::size_t offset = 4096; // page-aligned (and cache-line aligned) offset - { - std::ofstream file(file_path, std::ios::binary); - file.seekp(offset); - file.write(reinterpret_cast(input_data.data()), input_data.get_byte_size()); - } - - // Regular inference to get the reference output. - auto inf_req_regular = compiled_model.create_infer_request(); - inf_req_regular.set_tensor(input, input_data); - inf_req_regular.infer(); - auto output_tensor_regular = inf_req_regular.get_tensor(output); - - // Inference using a remote tensor memory-mapped from the file. - auto context = compiled_model.get_context().as(); - auto remote_tensor = - context.create_tensor(input->get_element_type(), input_shape, ov::intel_gpu::FileDescriptor{file_path, offset}); - ASSERT_TRUE(remote_tensor.is()); - - auto inf_req_shared = compiled_model.create_infer_request(); - inf_req_shared.set_tensor(input, remote_tensor); - inf_req_shared.infer(); - auto output_tensor_shared = inf_req_shared.get_tensor(output); - - // Compare results. - { - ASSERT_EQ(output_tensor_regular.get_size(), output_tensor_shared.get_size()); - OV_ASSERT_NO_THROW(output_tensor_regular.data()); - OV_ASSERT_NO_THROW(output_tensor_shared.data()); - ov::test::utils::compare(output_tensor_regular, output_tensor_shared); - } - - std::filesystem::remove(file_path); -} - INSTANTIATE_TEST_SUITE_P( smoke_GPU, OVRemoteTensorInputBlob_Test, From 43a0a0b058a4b733b835c1ba478faa8d477b47b3 Mon Sep 17 00:00:00 2001 From: Artemis Git Integration Date: Thu, 30 Jul 2026 15:47:59 +0000 Subject: [PATCH 58/84] Add OCL-only file-mmap remote tensor inference test --- .../ocl_remote_tensor_tests.cpp | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index 73cd8decb8785d..8bee5de9ecc77c 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -2993,4 +2993,49 @@ TEST(GpuRemoteTensorFromCpu, smoke_allocAlignedCPUMemory) { ov::util::aligned_free(output_ptr); } +TEST(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { + ov::Core core; + std::string target_device = ov::test::utils::DEVICE_GPU; + const size_t float_size = sizeof(float); + const ov::Shape shape{16}; + const size_t element_count = ov::shape_size(shape); + const size_t byte_size = element_count * float_size; + auto ctx = core.get_default_context(target_device).as(); + + // Store input data in a file at a page-aligned offset. + const std::filesystem::path file_path{"gpu_remote_tensor_from_file.bin"}; + constexpr std::size_t offset = 4096; // page-aligned (and cache-line aligned) offset + { + std::vector values(element_count, 2.0f); + std::ofstream file(file_path, std::ios::binary); + file.seekp(offset); + file.write(reinterpret_cast(values.data()), byte_size); + } + + void* output_ptr = ov::util::aligned_alloc(byte_size, core.get_property(target_device, ov::intel_gpu::cacheline_size)); + std::fill_n(static_cast(output_ptr), element_count, 0.0f); + + { + auto remote_input_tensor = + ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::FileDescriptor{file_path, offset}); + ASSERT_TRUE(remote_input_tensor.is()); + auto remote_output_tensor = + ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::VirtualAddressMemory(output_ptr)); + + auto model = make_copy_model(shape); + auto compiled = core.compile_model(model, ctx); + auto infer_req = compiled.create_infer_request(); + infer_req.set_tensor(compiled.input(), remote_input_tensor); + infer_req.set_tensor(compiled.output(), remote_output_tensor); + infer_req.infer(); + + for (size_t i = 0; i < element_count; ++i) { + EXPECT_FLOAT_EQ(static_cast(output_ptr)[i], 2.0f) << "Mismatch at index " << i; + } + } + + ov::util::aligned_free(output_ptr); + std::filesystem::remove(file_path); +} + #endif // OV_GPU_WITH_OCL_RT From d7be9ad80f83b6dc8d211f1617c0da087063d931 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Fri, 31 Jul 2026 11:13:28 +0200 Subject: [PATCH 59/84] still some problem with tests --- .../openvino/runtime/intel_gpu/ocl/ocl.hpp | 3 +-- .../runtime/intel_gpu/remote_properties.hpp | 17 +++++------------ .../intel_gpu/src/plugin/remote_context.cpp | 9 ++++----- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index a23e9937eaf61e..686c50601aaaab 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -357,8 +357,7 @@ class ClContext : public RemoteContext { */ ClBufferTensor create_tensor(const element::Type type, const Shape& shape, const FileDescriptor& file_descriptor) { AnyMap params = {{ov::intel_gpu::shared_mem_type.name(), ov::intel_gpu::SharedMemType::MMAPED_FILE}, - {ov::intel_gpu::file_path.name(), file_descriptor.path.string()}, - {ov::intel_gpu::file_offset.name(), file_descriptor.offset}}; + {ov::intel_gpu::file_descriptor.name(), file_descriptor}}; return create_tensor(type, shape, params).as(); } diff --git a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp index 5429d8fd3ff1bb..dedfee0e4fa891 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp @@ -274,29 +274,22 @@ struct VirtualAddressMemory { * so the file must not be modified until the returned tensor is destroyed. * @ingroup ov_runtime_ocl_gpu_cpp_api */ -struct FileDescriptor { +struct FileDescriptor { //need to be merged with ov::intel_npu::FileDescriptor in future explicit FileDescriptor(const std::filesystem::path& file_path, std::size_t offset_in_bytes = 0) : path(file_path), offset(offset_in_bytes) { OPENVINO_ASSERT(!file_path.empty(), "[GPU] Provided file path is empty."); } - std::filesystem::path path; ///< File path - std::size_t offset = 0; ///< Offset in bytes to read from the file + std::filesystem::path path; ///< File path + std::size_t offset = 0; ///< Offset in bytes to read from the file }; /** - * @brief This key identifies the file path + * @brief This key identifies the file descriptor * in a memory-mapped tensor parameter map. * @ingroup ov_runtime_ocl_gpu_cpp_api */ -static constexpr Property file_path{"FILE_PATH"}; - -/** - * @brief This key identifies the offset in bytes from the beginning of the file - * in a memory-mapped tensor parameter map. - * @ingroup ov_runtime_ocl_gpu_cpp_api - */ -static constexpr Property file_offset{"FILE_OFFSET"}; +static constexpr Property file_descriptor{"FILE_DESCRIPTOR"}; } // namespace intel_gpu } // namespace ov diff --git a/src/plugins/intel_gpu/src/plugin/remote_context.cpp b/src/plugins/intel_gpu/src/plugin/remote_context.cpp index 89e0e5064d27b7..2e7bbc87a77ce1 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_context.cpp @@ -189,9 +189,8 @@ ov::SoPtr RemoteContextImpl::create_tensor(const ov::element: auto size = extract_object(params, ov::intel_gpu::cpu_va_size); return { reuse_memory_from_cpu_va(type, shape, VirtualAddressMemory{mem, size}, tensor_type), nullptr }; } else if (ov::intel_gpu::SharedMemType::MMAPED_FILE == mem_type) { - const auto path = extract_object(params, ov::intel_gpu::file_path); - const auto offset = extract_object(params, ov::intel_gpu::file_offset); - return { reuse_memory_from_file(type, shape, path, offset), nullptr }; + const auto fd = extract_object(params, ov::intel_gpu::file_descriptor); + return { reuse_memory_from_file(type, shape, fd.path, fd.offset), nullptr }; } else if (ov::intel_gpu::SharedMemType::OCL_IMAGE2D == mem_type) { tensor_type = TensorType::BT_IMG_SHARED; mem = extract_object(params, ov::intel_gpu::mem_handle); @@ -285,7 +284,7 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_file(con // Memory-map the file. The mapping is retained inside the RemoteTensorImpl so it stays // alive for the whole tensor lifetime (GPU wraps the host pointer via CL_MEM_USE_HOST_PTR). auto mmap_tensor = ov::read_tensor_data(file_path, type, shape, offset); - void* data_ptr = std::as_const(mmap_tensor).data(); + const void* data_ptr = mmap_tensor.data(); const auto size = static_cast(mmap_tensor.get_byte_size()); return std::make_shared(get_this_shared_ptr(), shape, @@ -295,7 +294,7 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_file(con 0, 0, ov::intel_gpu::SharedBufferHandle{}, - VirtualAddressMemory{data_ptr, size}, + VirtualAddressMemory{const_cast(data_ptr), size}, mmap_tensor); } From 57a7348ae5b63eb6b3a29463ea47e1c73262e7df Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Fri, 31 Jul 2026 12:11:37 +0200 Subject: [PATCH 60/84] fix clang --- .../include/openvino/runtime/intel_gpu/remote_properties.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp index dedfee0e4fa891..508fd39dadc75e 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp @@ -274,7 +274,7 @@ struct VirtualAddressMemory { * so the file must not be modified until the returned tensor is destroyed. * @ingroup ov_runtime_ocl_gpu_cpp_api */ -struct FileDescriptor { //need to be merged with ov::intel_npu::FileDescriptor in future +struct FileDescriptor { //need to be merged with ov::intel_npu::FileDescriptor in future explicit FileDescriptor(const std::filesystem::path& file_path, std::size_t offset_in_bytes = 0) : path(file_path), offset(offset_in_bytes) { From 0ace9491d3448445fd844662c959cc2ae6900352 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Fri, 31 Jul 2026 17:38:03 +0200 Subject: [PATCH 61/84] fix clang --- .../include/openvino/runtime/intel_gpu/remote_properties.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp index 508fd39dadc75e..ba44261e50ed4b 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp @@ -274,7 +274,7 @@ struct VirtualAddressMemory { * so the file must not be modified until the returned tensor is destroyed. * @ingroup ov_runtime_ocl_gpu_cpp_api */ -struct FileDescriptor { //need to be merged with ov::intel_npu::FileDescriptor in future +struct FileDescriptor { // need to be merged with ov::intel_npu::FileDescriptor in future explicit FileDescriptor(const std::filesystem::path& file_path, std::size_t offset_in_bytes = 0) : path(file_path), offset(offset_in_bytes) { From d76930f2440dec20ddacbcea92c84f396670c960 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Wed, 5 Aug 2026 14:05:42 +0200 Subject: [PATCH 62/84] fix test --- .../include/intel_gpu/plugin/remote_tensor.hpp | 4 ++-- .../intel_gpu/include/intel_gpu/runtime/engine.hpp | 2 +- src/plugins/intel_gpu/src/plugin/remote_context.cpp | 2 +- src/plugins/intel_gpu/src/plugin/remote_tensor.cpp | 10 +++++----- src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp | 4 ++-- src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp | 2 +- src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp | 2 +- src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp | 2 +- src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp | 2 +- src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp | 2 +- 10 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp index 3fdc21df7c8498..3e50a3f64487a9 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp @@ -45,7 +45,7 @@ class RemoteTensorImpl : public ov::IRemoteTensor { uint32_t plane = 0, ov::intel_gpu::SharedBufferHandle shared_buffer_handle = {}, ov::intel_gpu::VirtualAddressMemory va_mem = ov::intel_gpu::VirtualAddressMemory(nullptr), - ov::Tensor mmap_tensor = {}); + const ov::Tensor mmap_tensor = {}); ~RemoteTensorImpl() override; const AnyMap& get_properties() const override; @@ -90,7 +90,7 @@ class RemoteTensorImpl : public ov::IRemoteTensor { uint32_t m_plane; ov::intel_gpu::SharedBufferHandle m_shared_buffer_handle; ov::intel_gpu::VirtualAddressMemory m_va_mem; - ov::Tensor m_mmap_tensor; // keeps the file mapping alive for the whole tensor lifetime + const ov::Tensor m_mmap_tensor; // keeps the file mapping alive for the whole tensor lifetime size_t m_hash = 0; bool supports_caching() const; diff --git a/src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp b/src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp index 89f7da1e13e407..ef1fc74390b771 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp @@ -71,7 +71,7 @@ class engine { /// Created memory object by wrapping a read-only host-allocated layout region. /// Backends that support access flags should use read-only permissions. - virtual memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) = 0; + virtual memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) = 0; /// Created memory object from the other @p memory and reinterpred the data using specified @p new_layout virtual memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) = 0; diff --git a/src/plugins/intel_gpu/src/plugin/remote_context.cpp b/src/plugins/intel_gpu/src/plugin/remote_context.cpp index 2e7bbc87a77ce1..8dd7e1362e6043 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_context.cpp @@ -283,7 +283,7 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_file(con size_t offset) { // Memory-map the file. The mapping is retained inside the RemoteTensorImpl so it stays // alive for the whole tensor lifetime (GPU wraps the host pointer via CL_MEM_USE_HOST_PTR). - auto mmap_tensor = ov::read_tensor_data(file_path, type, shape, offset); + const auto mmap_tensor = ov::read_tensor_data(file_path, type, shape, offset); const void* data_ptr = mmap_tensor.data(); const auto size = static_cast(mmap_tensor.get_byte_size()); return std::make_shared(get_this_shared_ptr(), diff --git a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp index c919a26e4f8555..90197078a10623 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp @@ -171,7 +171,7 @@ RemoteTensorImpl::RemoteTensorImpl(RemoteContextImpl::Ptr context, uint32_t plane, ov::intel_gpu::SharedBufferHandle shared_buffer_handle, ov::intel_gpu::VirtualAddressMemory va_mem, - ov::Tensor mmap_tensor) + const ov::Tensor mmap_tensor) : m_context(context) , m_element_type(element_type) , m_shape(shape) @@ -182,7 +182,7 @@ RemoteTensorImpl::RemoteTensorImpl(RemoteContextImpl::Ptr context, , m_plane(plane) , m_shared_buffer_handle(shared_buffer_handle) , m_va_mem(va_mem) - , m_mmap_tensor(std::move(mmap_tensor)) { + , m_mmap_tensor(mmap_tensor) { update_hash(); allocate(); } @@ -376,12 +376,12 @@ void RemoteTensorImpl::allocate() { } case TensorType::BT_CPU_VA: { // Track (account and log) the host memory only when the plugin owns the mapping (file-mmap case). - const bool track_memory = static_cast(m_mmap_tensor); - m_memory_object = engine.create_hostbuffer(m_va_mem.ptr, + const bool tensor_initialized = static_cast(m_mmap_tensor); + m_memory_object = engine.create_hostbuffer(tensor_initialized ? const_cast(m_va_mem.ptr) : m_va_mem.ptr, m_va_mem.size > -1 ? m_va_mem.size : m_layout.bytes_count(), cldnn::allocation_type::cl_mem, m_layout, - track_memory); + tensor_initialized); break; } #ifdef _WIN32 diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp index 15fefe9c2609de..08ec3818bf5186 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp @@ -249,8 +249,8 @@ memory_ptr ocl_engine::create_hostbuffer(void* cpu_address, return create_hostbuffer_impl(cpu_address, data_size, _allocation_type, output_layout, CL_MEM_READ_WRITE, track_memory); } -memory_ptr ocl_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) { - return create_hostbuffer_impl(const_cast(cpu_address), data_size, _allocation_type, output_layout, CL_MEM_READ_ONLY); +memory_ptr ocl_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory) { + return create_hostbuffer_impl(const_cast(cpu_address), data_size, _allocation_type, output_layout, CL_MEM_READ_ONLY | CL_MEM_HOST_READ_ONLY, track_memory); } memory::ptr ocl_engine::reinterpret_buffer(const memory& memory, const layout& new_layout) { diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp index 5b8ec6b521e948..a01eaedec47d9e 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp @@ -27,7 +27,7 @@ class ocl_engine : public engine { memory_ptr reinterpret_handle(const layout& new_layout, shared_mem_params params) override; memory_ptr create_subbuffer(const memory& memory, const layout& new_layout, size_t offset) override; memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; - memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; + memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) override; memory_ptr import_buffer(const layout&, ov::intel_gpu::os_handle_param external_handle) override; bool is_the_same_buffer(const memory& mem1, const memory& mem2) override; diff --git a/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp b/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp index 7be247d4fed4ff..8b2be0ddb7fdf7 100644 --- a/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp @@ -155,7 +155,7 @@ memory_ptr sycl_engine::create_hostbuffer(void* cpu_address, size_t data_size, a OPENVINO_NOT_IMPLEMENTED; } -memory_ptr sycl_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) { +memory_ptr sycl_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory) { OPENVINO_NOT_IMPLEMENTED; } diff --git a/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp b/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp index bddfd0750fdec2..64b089474db2b7 100644 --- a/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp +++ b/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp @@ -31,7 +31,7 @@ class sycl_engine : public engine { memory_ptr reinterpret_handle(const layout& new_layout, shared_mem_params params) override; memory_ptr create_subbuffer(const memory& memory, const layout& new_layout, size_t offset) override; memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; - memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; + memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) override; memory_ptr import_buffer(const layout& layout, ov::intel_gpu::os_handle_param external_handle) override; bool is_the_same_buffer(const memory& mem1, const memory& mem2) override; diff --git a/src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp b/src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp index a1345ef96c950e..0be14b4e6a376b 100644 --- a/src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp @@ -175,7 +175,7 @@ memory_ptr ze_engine::create_hostbuffer(void* cpu_address, size_t data_size, all OPENVINO_NOT_IMPLEMENTED; } -memory_ptr ze_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) { +memory_ptr ze_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory) { OPENVINO_NOT_IMPLEMENTED; } diff --git a/src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp b/src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp index 608c73bd3eefc6..11f575caee4cef 100644 --- a/src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp +++ b/src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp @@ -28,7 +28,7 @@ class ze_engine : public engine { memory_ptr reinterpret_handle(const layout& new_layout, shared_mem_params params) override; memory_ptr create_subbuffer(const memory& memory, const layout& new_layout, size_t byte_offset) override; memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; - memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; + memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) override; bool is_the_same_buffer(const memory& mem1, const memory& mem2) override; From e0f388da8b71ffd633b34d65f6e44c7e34c3d491 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Wed, 5 Aug 2026 23:41:48 +0200 Subject: [PATCH 63/84] wip --- .../intel_gpu/plugin/remote_context.hpp | 3 +- .../intel_gpu/plugin/remote_tensor.hpp | 5 ++-- .../include/intel_gpu/runtime/engine.hpp | 5 ++-- .../intel_gpu/src/plugin/remote_context.cpp | 15 ++++++---- .../intel_gpu/src/plugin/remote_tensor.cpp | 29 ++++++++++++------- .../intel_gpu/src/runtime/ocl/ocl_engine.cpp | 13 ++++----- .../intel_gpu/src/runtime/ocl/ocl_engine.hpp | 6 ++-- .../src/runtime/sycl/sycl_engine.cpp | 4 +-- .../src/runtime/sycl/sycl_engine.hpp | 4 +-- .../intel_gpu/src/runtime/ze/ze_engine.cpp | 4 +-- .../intel_gpu/src/runtime/ze/ze_engine.hpp | 4 +-- 11 files changed, 51 insertions(+), 41 deletions(-) diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_context.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_context.hpp index 5876736efa8637..a16b5924142bd6 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_context.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_context.hpp @@ -18,6 +18,7 @@ #include "intel_gpu/plugin/common_utils.hpp" #include +#include #include #include @@ -85,7 +86,7 @@ class RemoteContextImpl : public ov::IRemoteContext { std::shared_ptr reuse_memory(const ov::element::Type type, const ov::Shape& shape, cldnn::shared_handle mem, TensorType tensor_type); std::shared_ptr reuse_memory_from_cpu_va(const ov::element::Type type, const ov::Shape& shape, VirtualAddressMemory cpu_va, TensorType tensor_type); std::shared_ptr reuse_memory_from_handle(const ov::element::Type type, const ov::Shape& shape, SharedBufferHandle handle, TensorType tensor_type); - std::shared_ptr reuse_memory_from_file(const ov::element::Type type, const ov::Shape& shape, const std::string& file_path, size_t offset); + std::shared_ptr reuse_memory_from_file(const ov::element::Type type, const ov::Shape& shape, const std::filesystem::path& file_path, size_t offset); std::shared_ptr create_buffer(const ov::element::Type type, const ov::Shape& shape); std::shared_ptr create_usm(const ov::element::Type type, const ov::Shape& shape, TensorType alloc_type); void check_if_shared() const; diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp index 3e50a3f64487a9..d66c62b9005316 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp @@ -20,6 +20,7 @@ #include "openvino/runtime/iremote_tensor.hpp" #include "openvino/runtime/intel_gpu/remote_properties.hpp" #include "openvino/runtime/tensor.hpp" +#include "openvino/util/mmap_object.hpp" #include "intel_gpu/runtime/memory_caps.hpp" #include "intel_gpu/runtime/memory.hpp" @@ -45,7 +46,7 @@ class RemoteTensorImpl : public ov::IRemoteTensor { uint32_t plane = 0, ov::intel_gpu::SharedBufferHandle shared_buffer_handle = {}, ov::intel_gpu::VirtualAddressMemory va_mem = ov::intel_gpu::VirtualAddressMemory(nullptr), - const ov::Tensor mmap_tensor = {}); + std::shared_ptr mapped_memory = nullptr); ~RemoteTensorImpl() override; const AnyMap& get_properties() const override; @@ -90,7 +91,7 @@ class RemoteTensorImpl : public ov::IRemoteTensor { uint32_t m_plane; ov::intel_gpu::SharedBufferHandle m_shared_buffer_handle; ov::intel_gpu::VirtualAddressMemory m_va_mem; - const ov::Tensor m_mmap_tensor; // keeps the file mapping alive for the whole tensor lifetime + std::shared_ptr m_mapped_memory; // keeps the file mapping alive for the whole tensor lifetime size_t m_hash = 0; bool supports_caching() const; diff --git a/src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp b/src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp index ef1fc74390b771..bb5d83c4f86e40 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/runtime/engine.hpp @@ -66,12 +66,11 @@ class engine { /// Created memory object by wrapping a writable host-allocated layout region. /// Backends that support access flags should use read-write permissions. - /// When @p track_memory is true the wrapped host memory is accounted and logged by the engine's memory tracker. - virtual memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) = 0; + virtual memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) = 0; /// Created memory object by wrapping a read-only host-allocated layout region. /// Backends that support access flags should use read-only permissions. - virtual memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) = 0; + virtual memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) = 0; /// Created memory object from the other @p memory and reinterpred the data using specified @p new_layout virtual memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) = 0; diff --git a/src/plugins/intel_gpu/src/plugin/remote_context.cpp b/src/plugins/intel_gpu/src/plugin/remote_context.cpp index 8dd7e1362e6043..196f94b7e62188 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_context.cpp @@ -5,6 +5,8 @@ #include "openvino/runtime/intel_gpu/remote_properties.hpp" #include "openvino/runtime/make_tensor.hpp" #include "openvino/runtime/tensor.hpp" +#include "openvino/core/memory_util.hpp" +#include "openvino/util/mmap_object.hpp" #include "intel_gpu/plugin/remote_context.hpp" #include "intel_gpu/plugin/remote_tensor.hpp" #include "intel_gpu/plugin/usm_host_tensor.hpp" @@ -279,13 +281,14 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_handle(c std::shared_ptr RemoteContextImpl::reuse_memory_from_file(const ov::element::Type type, const ov::Shape& shape, - const std::string& file_path, + const std::filesystem::path& file_path, size_t offset) { + const auto byte_size = ov::util::get_memory_size_safe(type, shape); + OPENVINO_ASSERT(byte_size, "[GPU] Cannot calculate memory size for element type ", type, " and shape ", shape); + // Memory-map the file. The mapping is retained inside the RemoteTensorImpl so it stays // alive for the whole tensor lifetime (GPU wraps the host pointer via CL_MEM_USE_HOST_PTR). - const auto mmap_tensor = ov::read_tensor_data(file_path, type, shape, offset); - const void* data_ptr = mmap_tensor.data(); - const auto size = static_cast(mmap_tensor.get_byte_size()); + auto mapped_memory = ov::load_mmap_object(file_path, offset, *byte_size); return std::make_shared(get_this_shared_ptr(), shape, type, @@ -294,8 +297,8 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_file(con 0, 0, ov::intel_gpu::SharedBufferHandle{}, - VirtualAddressMemory{const_cast(data_ptr), size}, - mmap_tensor); + VirtualAddressMemory{mapped_memory->data(), static_cast(*byte_size)}, + mapped_memory); } std::shared_ptr RemoteContextImpl::create_buffer(const ov::element::Type type, const ov::Shape& shape) { diff --git a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp index 90197078a10623..7d5a6d0a093cf9 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp @@ -171,7 +171,7 @@ RemoteTensorImpl::RemoteTensorImpl(RemoteContextImpl::Ptr context, uint32_t plane, ov::intel_gpu::SharedBufferHandle shared_buffer_handle, ov::intel_gpu::VirtualAddressMemory va_mem, - const ov::Tensor mmap_tensor) + std::shared_ptr mapped_memory) : m_context(context) , m_element_type(element_type) , m_shape(shape) @@ -182,7 +182,7 @@ RemoteTensorImpl::RemoteTensorImpl(RemoteContextImpl::Ptr context, , m_plane(plane) , m_shared_buffer_handle(shared_buffer_handle) , m_va_mem(va_mem) - , m_mmap_tensor(mmap_tensor) { + , m_mapped_memory(std::move(mapped_memory)) { update_hash(); allocate(); } @@ -375,13 +375,19 @@ void RemoteTensorImpl::allocate() { break; } case TensorType::BT_CPU_VA: { - // Track (account and log) the host memory only when the plugin owns the mapping (file-mmap case). - const bool tensor_initialized = static_cast(m_mmap_tensor); - m_memory_object = engine.create_hostbuffer(tensor_initialized ? const_cast(m_va_mem.ptr) : m_va_mem.ptr, - m_va_mem.size > -1 ? m_va_mem.size : m_layout.bytes_count(), - cldnn::allocation_type::cl_mem, - m_layout, - tensor_initialized); + const auto buffer_size = m_va_mem.size > -1 ? m_va_mem.size : m_layout.bytes_count(); + if (m_mapped_memory) { + // The plugin owns the mapping (file-mmap case), so the buffer is imported as read-only. + m_memory_object = engine.create_hostbuffer(static_cast(m_va_mem.ptr), + buffer_size, + cldnn::allocation_type::cl_mem, + m_layout); + } else { + m_memory_object = engine.create_hostbuffer(m_va_mem.ptr, + buffer_size, + cldnn::allocation_type::cl_mem, + m_layout); + } break; } #ifdef _WIN32 @@ -429,7 +435,9 @@ bool RemoteTensorImpl::is_shared() const noexcept { } bool RemoteTensorImpl::supports_caching() const { - return is_shared(); + // Memory mapped by the plugin is released together with this tensor, so the cached memory object + // (created with CL_MEM_USE_HOST_PTR) would outlive the host pointer it wraps. + return is_shared() && !m_mapped_memory; } void RemoteTensorImpl::update_hash() { @@ -437,7 +445,6 @@ void RemoteTensorImpl::update_hash() { m_hash = cldnn::hash_combine(0, m_mem); m_hash = cldnn::hash_combine(m_hash, m_shared_buffer_handle); m_hash = cldnn::hash_combine(m_hash, m_va_mem); - m_hash = cldnn::hash_combine(m_hash, m_mmap_tensor ? std::as_const(m_mmap_tensor).data() : nullptr); m_hash = cldnn::hash_combine(m_hash, m_surf); m_hash = cldnn::hash_combine(m_hash, m_plane); m_hash = cldnn::hash_combine(m_hash, m_shape.size()); diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp index 08ec3818bf5186..2dd3097a43e7ca 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp @@ -244,13 +244,12 @@ memory::ptr ocl_engine::create_subbuffer(const memory& memory, const layout& new memory_ptr ocl_engine::create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, - const layout output_layout, - bool track_memory) { - return create_hostbuffer_impl(cpu_address, data_size, _allocation_type, output_layout, CL_MEM_READ_WRITE, track_memory); + const layout output_layout) { + return create_hostbuffer_impl(cpu_address, data_size, _allocation_type, output_layout, CL_MEM_READ_WRITE); } -memory_ptr ocl_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory) { - return create_hostbuffer_impl(const_cast(cpu_address), data_size, _allocation_type, output_layout, CL_MEM_READ_ONLY | CL_MEM_HOST_READ_ONLY, track_memory); +memory_ptr ocl_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) { + return create_hostbuffer_impl(const_cast(cpu_address), data_size, _allocation_type, output_layout, CL_MEM_READ_ONLY | CL_MEM_HOST_READ_ONLY); } memory::ptr ocl_engine::reinterpret_buffer(const memory& memory, const layout& new_layout) { @@ -375,7 +374,7 @@ std::shared_ptr ocl_engine::create(const device::ptr device, runt return std::make_shared(device, runtime_type); } -memory_ptr ocl_engine::create_hostbuffer_impl(void* cpu_address, size_t data_size, allocation_type allocation, const layout& output_layout, cl_mem_flags access_flags, bool track_memory) { +memory_ptr ocl_engine::create_hostbuffer_impl(void* cpu_address, size_t data_size, allocation_type allocation, const layout& output_layout, cl_mem_flags access_flags) { cl_int err = CL_SUCCESS; cl_mem_flags flags = access_flags | CL_MEM_USE_HOST_PTR; @@ -400,7 +399,7 @@ memory_ptr ocl_engine::create_hostbuffer_impl(void* cpu_address, size_t data_siz // Host memory imported via CL_MEM_FORCE_HOST_MEMORY_INTEL is owned by the caller, so it is not // accounted by default. When track_memory is set (e.g. memory the plugin mmap-ed itself) the // engine is passed so the allocation is tracked and logged. - tracker = std::make_shared(track_memory ? this : nullptr, + tracker = std::make_shared(nullptr, cpu_address, data_size, allocation); diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp index a01eaedec47d9e..2d39a5013a594f 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.hpp @@ -26,8 +26,8 @@ class ocl_engine : public engine { memory_ptr allocate_memory(const layout& layout, allocation_type type, bool reset = true) override; memory_ptr reinterpret_handle(const layout& new_layout, shared_mem_params params) override; memory_ptr create_subbuffer(const memory& memory, const layout& new_layout, size_t offset) override; - memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; - memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; + memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; + memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) override; memory_ptr import_buffer(const layout&, ov::intel_gpu::os_handle_param external_handle) override; bool is_the_same_buffer(const memory& mem1, const memory& mem2) override; @@ -56,7 +56,7 @@ class ocl_engine : public engine { static std::shared_ptr create(const device::ptr device, runtime_types runtime_type); private: - memory_ptr create_hostbuffer_impl(void* cpu_address, size_t data_size, allocation_type allocation, const layout& output_layout, cl_mem_flags access_flags, bool track_memory = false); + memory_ptr create_hostbuffer_impl(void* cpu_address, size_t data_size, allocation_type allocation, const layout& output_layout, cl_mem_flags access_flags); std::string _extensions; }; diff --git a/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp b/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp index 8b2be0ddb7fdf7..d9ef60fe7971c9 100644 --- a/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.cpp @@ -151,11 +151,11 @@ memory::ptr sycl_engine::create_subbuffer(const memory& memory, const layout& ne } } -memory_ptr sycl_engine::create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory) { +memory_ptr sycl_engine::create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) { OPENVINO_NOT_IMPLEMENTED; } -memory_ptr sycl_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory) { +memory_ptr sycl_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) { OPENVINO_NOT_IMPLEMENTED; } diff --git a/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp b/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp index 64b089474db2b7..a440d4a7c6a382 100644 --- a/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp +++ b/src/plugins/intel_gpu/src/runtime/sycl/sycl_engine.hpp @@ -30,8 +30,8 @@ class sycl_engine : public engine { memory_ptr allocate_memory(const layout& layout, allocation_type type, bool reset = true) override; memory_ptr reinterpret_handle(const layout& new_layout, shared_mem_params params) override; memory_ptr create_subbuffer(const memory& memory, const layout& new_layout, size_t offset) override; - memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; - memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; + memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; + memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) override; memory_ptr import_buffer(const layout& layout, ov::intel_gpu::os_handle_param external_handle) override; bool is_the_same_buffer(const memory& mem1, const memory& mem2) override; diff --git a/src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp b/src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp index 0be14b4e6a376b..3047d90298278a 100644 --- a/src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ze/ze_engine.cpp @@ -171,11 +171,11 @@ memory_ptr ze_engine::create_subbuffer(const memory& memory, const layout& new_l memory.get_mem_tracker()); } -memory_ptr ze_engine::create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory) { +memory_ptr ze_engine::create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) { OPENVINO_NOT_IMPLEMENTED; } -memory_ptr ze_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory) { +memory_ptr ze_engine::create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) { OPENVINO_NOT_IMPLEMENTED; } diff --git a/src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp b/src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp index 11f575caee4cef..51c2067b10c766 100644 --- a/src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp +++ b/src/plugins/intel_gpu/src/runtime/ze/ze_engine.hpp @@ -27,8 +27,8 @@ class ze_engine : public engine { memory_ptr import_buffer(const layout& layout, ov::intel_gpu::os_handle_param external_handle) override; memory_ptr reinterpret_handle(const layout& new_layout, shared_mem_params params) override; memory_ptr create_subbuffer(const memory& memory, const layout& new_layout, size_t byte_offset) override; - memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; - memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout, bool track_memory = false) override; + memory_ptr create_hostbuffer(void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; + memory_ptr create_hostbuffer(const void* cpu_address, size_t data_size, allocation_type _allocation_type, const layout output_layout) override; memory_ptr reinterpret_buffer(const memory& memory, const layout& new_layout) override; bool is_the_same_buffer(const memory& mem1, const memory& mem2) override; From 4b8a4099c56479f9db95c2ddb394b21b91bede00 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Thu, 6 Aug 2026 11:34:18 +0200 Subject: [PATCH 64/84] added more requirements, delete one comment --- src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 2 +- src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 686c50601aaaab..5ac8df91c9556b 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -352,7 +352,7 @@ class ClContext : public RemoteContext { * so the file must not be modified until the returned tensor is destroyed. * @param type Tensor element type * @param shape Tensor shape - * @param file_descriptor Descriptor with the path and offset of the file containing tensor data + * @param file_descriptor Descriptor with the path and offset(which must be multiply of page size) of the file containing tensor data * @return A remote tensor instance */ ClBufferTensor create_tensor(const element::Type type, const Shape& shape, const FileDescriptor& file_descriptor) { diff --git a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp index 2dd3097a43e7ca..802804f048990d 100644 --- a/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp +++ b/src/plugins/intel_gpu/src/runtime/ocl/ocl_engine.cpp @@ -396,9 +396,6 @@ memory_ptr ocl_engine::create_hostbuffer_impl(void* cpu_address, size_t data_siz std::shared_ptr tracker = nullptr; #ifdef CL_MEM_FORCE_HOST_MEMORY_INTEL - // Host memory imported via CL_MEM_FORCE_HOST_MEMORY_INTEL is owned by the caller, so it is not - // accounted by default. When track_memory is set (e.g. memory the plugin mmap-ed itself) the - // engine is passed so the allocation is tracked and logged. tracker = std::make_shared(nullptr, cpu_address, data_size, From 529f35e025aa75972687a7d92a432bdced3cbfaa Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Thu, 6 Aug 2026 11:38:20 +0200 Subject: [PATCH 65/84] wip --- src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp | 2 +- .../functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index 5ac8df91c9556b..e5415f84780483 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -352,7 +352,7 @@ class ClContext : public RemoteContext { * so the file must not be modified until the returned tensor is destroyed. * @param type Tensor element type * @param shape Tensor shape - * @param file_descriptor Descriptor with the path and offset(which must be multiply of page size) of the file containing tensor data + * @param file_descriptor Descriptor with the path and offset(which must be multiple of page size) of the file containing tensor data * @return A remote tensor instance */ ClBufferTensor create_tensor(const element::Type type, const Shape& shape, const FileDescriptor& file_descriptor) { diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index 8bee5de9ecc77c..f2d7f34e2d6fe4 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -2997,7 +2997,7 @@ TEST(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { ov::Core core; std::string target_device = ov::test::utils::DEVICE_GPU; const size_t float_size = sizeof(float); - const ov::Shape shape{16}; + const ov::Shape shape{4096}; const size_t element_count = ov::shape_size(shape); const size_t byte_size = element_count * float_size; auto ctx = core.get_default_context(target_device).as(); From 6a6024746bc606d43f710c0f4ca793b0ef745bbb Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Thu, 6 Aug 2026 12:20:40 +0200 Subject: [PATCH 66/84] wip --- .../openvino/runtime/intel_gpu/ocl/ocl.hpp | 4 +++- .../intel_gpu/src/plugin/remote_context.cpp | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index e5415f84780483..bb4d86ed5f5c93 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -352,7 +352,9 @@ class ClContext : public RemoteContext { * so the file must not be modified until the returned tensor is destroyed. * @param type Tensor element type * @param shape Tensor shape - * @param file_descriptor Descriptor with the path and offset(which must be multiple of page size) of the file containing tensor data + * @param file_descriptor Descriptor with the path and offset of the file containing tensor data. + * The offset must be a multiple of the system memory mapping alignment: the page size on Linux + * (typically 4 KiB) and the allocation granularity on Windows (typically 64 KiB). * @return A remote tensor instance */ ClBufferTensor create_tensor(const element::Type type, const Shape& shape, const FileDescriptor& file_descriptor) { diff --git a/src/plugins/intel_gpu/src/plugin/remote_context.cpp b/src/plugins/intel_gpu/src/plugin/remote_context.cpp index 196f94b7e62188..b67f1f28dc02d5 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_context.cpp @@ -14,6 +14,13 @@ #include "intel_gpu/runtime/device_query.hpp" #include +#ifdef _WIN32 +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include +#endif + namespace ov::intel_gpu { namespace { @@ -26,6 +33,17 @@ Type extract_object(const ov::AnyMap& params, const ov::Property& p) { return res.as(); } +// Alignment required for a memory mapping offset: allocation granularity on Windows, page size elsewhere. +size_t get_mmap_offset_alignment() { +#ifdef _WIN32 + SYSTEM_INFO sys_info; + GetSystemInfo(&sys_info); + return static_cast(sys_info.dwAllocationGranularity); +#else + return static_cast(ov::util::get_system_page_size()); +#endif +} + ContextType get_default_context_type() { #ifdef OV_GPU_WITH_ZE_RT return ContextType::ZE; @@ -286,6 +304,12 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_file(con const auto byte_size = ov::util::get_memory_size_safe(type, shape); OPENVINO_ASSERT(byte_size, "[GPU] Cannot calculate memory size for element type ", type, " and shape ", shape); + const auto alignment = get_mmap_offset_alignment(); + OPENVINO_ASSERT(alignment != 0 && offset % alignment == 0, + "[GPU] Offset ", + offset, + " must be a multiple of ", + alignment); // Memory-map the file. The mapping is retained inside the RemoteTensorImpl so it stays // alive for the whole tensor lifetime (GPU wraps the host pointer via CL_MEM_USE_HOST_PTR). auto mapped_memory = ov::load_mmap_object(file_path, offset, *byte_size); From c84072128fd2b6f10f43893e17c0b66f435d139c Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Thu, 6 Aug 2026 16:07:49 +0200 Subject: [PATCH 67/84] more test cases --- .../ocl_remote_tensor_tests.cpp | 54 ++++++++++++++++--- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index f2d7f34e2d6fe4..c4050a0d8cb839 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -2993,24 +2993,40 @@ TEST(GpuRemoteTensorFromCpu, smoke_allocAlignedCPUMemory) { ov::util::aligned_free(output_ptr); } -TEST(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { +// +using MmapFileMemoryParams = std::tuple; + +class GpuRemoteTensorFromFile : public ::testing::TestWithParam { +public: + static std::string getTestCaseName(const testing::TestParamInfo& obj) { + const auto& [offset, byte_size] = obj.param; + return "offset_" + std::to_string(offset) + "_bytes_" + std::to_string(byte_size); + } +}; + +TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { + const auto& [offset, byte_size] = GetParam(); + ov::Core core; std::string target_device = ov::test::utils::DEVICE_GPU; const size_t float_size = sizeof(float); - const ov::Shape shape{4096}; + const ov::Shape shape{byte_size / float_size}; const size_t element_count = ov::shape_size(shape); - const size_t byte_size = element_count * float_size; auto ctx = core.get_default_context(target_device).as(); - // Store input data in a file at a page-aligned offset. - const std::filesystem::path file_path{"gpu_remote_tensor_from_file.bin"}; - constexpr std::size_t offset = 4096; // page-aligned (and cache-line aligned) offset + // Store input data in a file at a page-aligned offset, so the resulting file size is offset + byte_size. + const std::filesystem::path file_path{"gpu_remote_tensor_from_file_" + std::to_string(offset) + "_" + + std::to_string(byte_size) + ".bin"}; { std::vector values(element_count, 2.0f); std::ofstream file(file_path, std::ios::binary); - file.seekp(offset); + if (offset > 0) { + const std::vector padding(offset, 0); + file.write(padding.data(), padding.size()); + } file.write(reinterpret_cast(values.data()), byte_size); } + ASSERT_EQ(std::filesystem::file_size(file_path), offset + byte_size); void* output_ptr = ov::util::aligned_alloc(byte_size, core.get_property(target_device, ov::intel_gpu::cacheline_size)); std::fill_n(static_cast(output_ptr), element_count, 0.0f); @@ -3038,4 +3054,28 @@ TEST(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { std::filesystem::remove(file_path); } +INSTANTIATE_TEST_SUITE_P(smoke_mmapFileMemory, + GpuRemoteTensorFromFile, +#ifdef _WIN32 + // Windows maps file views with 64K allocation granularity + ::testing::Values(MmapFileMemoryParams{0, 256}, + MmapFileMemoryParams{0, 4 * 65536}, + MmapFileMemoryParams{65536, 256}, + MmapFileMemoryParams{2 * 65536, 256}, + MmapFileMemoryParams{16 * 65536, 256}, + MmapFileMemoryParams{65536, 65536}, + MmapFileMemoryParams{3 * 65536, 4 * 65536}, + MmapFileMemoryParams{8 * 65536, 16 * 65536}), +#else + ::testing::Values(MmapFileMemoryParams{0, 256}, + MmapFileMemoryParams{0, 4 * 4096}, + MmapFileMemoryParams{4096, 256}, + MmapFileMemoryParams{2 * 4096, 256}, + MmapFileMemoryParams{16 * 4096, 256}, + MmapFileMemoryParams{4096, 4096}, + MmapFileMemoryParams{3 * 4096, 4 * 4096}, + MmapFileMemoryParams{8 * 4096, 16 * 4096}), +#endif + GpuRemoteTensorFromFile::getTestCaseName); + #endif // OV_GPU_WITH_OCL_RT From 2ce299325748edcb07d23ec1dcc0bda38305c2b6 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Mon, 10 Aug 2026 11:59:16 +0200 Subject: [PATCH 68/84] fix test on windows --- .../ocl_remote_tensor_tests.cpp | 37 +++++++++---------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index c4050a0d8cb839..46c9ae9981de3d 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -2993,7 +2993,9 @@ TEST(GpuRemoteTensorFromCpu, smoke_allocAlignedCPUMemory) { ov::util::aligned_free(output_ptr); } -// +// : the tensor data starts at `offset` bytes into the file and spans `byte_size` bytes, +// so the file is `offset + byte_size` long. Both values are independent - `offset` only controls how much +// padding precedes the data and is varied to cover mmap allocation granularity boundaries. using MmapFileMemoryParams = std::tuple; class GpuRemoteTensorFromFile : public ::testing::TestWithParam { @@ -3054,28 +3056,23 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { std::filesystem::remove(file_path); } -INSTANTIATE_TEST_SUITE_P(smoke_mmapFileMemory, - GpuRemoteTensorFromFile, #ifdef _WIN32 - // Windows maps file views with 64K allocation granularity - ::testing::Values(MmapFileMemoryParams{0, 256}, - MmapFileMemoryParams{0, 4 * 65536}, - MmapFileMemoryParams{65536, 256}, - MmapFileMemoryParams{2 * 65536, 256}, - MmapFileMemoryParams{16 * 65536, 256}, - MmapFileMemoryParams{65536, 65536}, - MmapFileMemoryParams{3 * 65536, 4 * 65536}, - MmapFileMemoryParams{8 * 65536, 16 * 65536}), +// Windows maps file views with 64K allocation granularity +constexpr std::size_t mmap_granularity = 65536; #else - ::testing::Values(MmapFileMemoryParams{0, 256}, - MmapFileMemoryParams{0, 4 * 4096}, - MmapFileMemoryParams{4096, 256}, - MmapFileMemoryParams{2 * 4096, 256}, - MmapFileMemoryParams{16 * 4096, 256}, - MmapFileMemoryParams{4096, 4096}, - MmapFileMemoryParams{3 * 4096, 4 * 4096}, - MmapFileMemoryParams{8 * 4096, 16 * 4096}), +constexpr std::size_t mmap_granularity = 4096; #endif + +INSTANTIATE_TEST_SUITE_P(smoke_mmapFileMemory, + GpuRemoteTensorFromFile, + ::testing::Values(MmapFileMemoryParams{0, 256}, + MmapFileMemoryParams{0, 4 * mmap_granularity}, + MmapFileMemoryParams{mmap_granularity, 256}, + MmapFileMemoryParams{2 * mmap_granularity, 256}, + MmapFileMemoryParams{16 * mmap_granularity, 256}, + MmapFileMemoryParams{mmap_granularity, mmap_granularity}, + MmapFileMemoryParams{3 * mmap_granularity, 4 * mmap_granularity}, + MmapFileMemoryParams{8 * mmap_granularity, 16 * mmap_granularity}), GpuRemoteTensorFromFile::getTestCaseName); #endif // OV_GPU_WITH_OCL_RT From ad8bd7c020f4f5402fa7f88d1a9ee328785a2ab9 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Mon, 10 Aug 2026 13:40:45 +0200 Subject: [PATCH 69/84] added rw to mmap --- .../include/openvino/util/mmap_object.hpp | 18 +++- .../util/src/os/lin/lin_mmap_object.cpp | 25 +++--- .../util/src/os/win/win_mmap_object.cpp | 46 ++++++---- .../intel_gpu/plugin/remote_tensor.hpp | 4 +- .../intel_gpu/src/plugin/remote_context.cpp | 21 ++++- .../intel_gpu/src/plugin/remote_tensor.cpp | 10 ++- .../ocl_remote_tensor_tests.cpp | 84 +++++++++++++++---- 7 files changed, 156 insertions(+), 52 deletions(-) diff --git a/src/common/util/include/openvino/util/mmap_object.hpp b/src/common/util/include/openvino/util/mmap_object.hpp index 56e0567ac7fe61..ee4f29341f3fdf 100644 --- a/src/common/util/include/openvino/util/mmap_object.hpp +++ b/src/common/util/include/openvino/util/mmap_object.hpp @@ -28,6 +28,19 @@ int64_t get_system_page_size(); */ inline constexpr auto auto_size = std::numeric_limits::max(); +/** + * @brief Access mode of a memory mapping. + */ +enum class MmapMode { + read, //!< Read-only mapping. + read_write //!< Read-write mapping, modifications are written back to the file. +}; + +/** + * @brief Id reported by mappings whose content is mutable, so they must never be substituted for one another. + */ +inline constexpr uint64_t no_mapping_id = 0; + /** * @brief This class represents a mapped memory. * Instead of reading files, we can map the memory via mmap for Linux or MapViewOfFile for Windows. @@ -61,12 +74,15 @@ class MappedMemory { * @param no_placeholder When true, skip the Windows 10+ placeholder/VEH mechanism and use the legacy * single-call MapViewOfFile path instead. This guarantees a uniform AllocationBase * across the whole mapping, required for NPU zero-copy blob import. On Linux ignored. + * @param mode Access mode of the mapping. A read_write mapping requires the file to be writable and reports + * no_mapping_id, because get_id() marks an immutable data source that consumers may share. * @return MappedMemory shared ptr object which keep mmaped memory and control the lifetime. */ std::shared_ptr load_mmap_object(const std::filesystem::path& path, size_t offset = 0, size_t size = auto_size, - bool no_placeholder = false); + bool no_placeholder = false, + MmapMode mode = MmapMode::read); /** * @brief Returns mapped memory for a file from provided file handle (cross-platform). diff --git a/src/common/util/src/os/lin/lin_mmap_object.cpp b/src/common/util/src/os/lin/lin_mmap_object.cpp index 763c01175fbab1..3a0c2194d276f2 100644 --- a/src/common/util/src/os/lin/lin_mmap_object.cpp +++ b/src/common/util/src/os/lin/lin_mmap_object.cpp @@ -107,18 +107,18 @@ class MapHolder final : public MappedMemory { public: MapHolder() = default; - void set(const std::filesystem::path& path, const size_t offset, const size_t size) { - int mode = O_RDONLY; + void set(const std::filesystem::path& path, const size_t offset, const size_t size, const MmapMode mmap_mode) { + int mode = (mmap_mode == MmapMode::read_write) ? O_RDWR : O_RDONLY; int fd = open(path.c_str(), mode); if (fd == -1) { throw std::runtime_error("Can not open file " + util::path_to_string(path) + " for mapping. Ensure that file exists and has appropriate permissions."); } - set_from_fd(fd, offset, size); - m_id = util::get_id_for_file(path, offset, size); + set_from_fd(fd, offset, size, mmap_mode); + m_id = (mmap_mode == MmapMode::read_write) ? no_mapping_id : util::get_id_for_file(path, offset, size); } - void set_from_fd(const int fd, const size_t offset, const size_t size) { + void set_from_fd(const int fd, const size_t offset, const size_t size, const MmapMode mmap_mode = MmapMode::read) { m_handle = HandleHolder(fd); struct stat sb = {}; @@ -132,17 +132,21 @@ class MapHolder final : public MappedMemory { } if (m_size > 0) { + const auto prot = (mmap_mode == MmapMode::read_write) ? (PROT_READ | PROT_WRITE) : PROT_READ; const auto& [aligned_offset, length, gap] = util::make_mmap_region(offset, m_size); m_mapped_view_size = length; - m_mapped_view = mmap(nullptr, length, PROT_READ, MAP_SHARED, fd, aligned_offset); + m_mapped_view = mmap(nullptr, length, prot, MAP_SHARED, fd, aligned_offset); if (m_mapped_view == MAP_FAILED) { throw std::runtime_error("Can not create file mapping for " + std::to_string(fd) + ", err=" + std::strerror(errno)); } m_data = static_cast(m_mapped_view) + gap; } - m_id = - util::u64_hash_combine(static_cast(sb.st_ino), {static_cast(sb.st_dev), offset, size}); + // A read-write mapping is not an immutable data source, so it must not be shared through id-based caches. + m_id = (mmap_mode == MmapMode::read_write) + ? no_mapping_id + : util::u64_hash_combine(static_cast(sb.st_ino), + {static_cast(sb.st_dev), offset, size}); } uint64_t get_id() const noexcept override { @@ -185,9 +189,10 @@ class MapHolder final : public MappedMemory { std::shared_ptr load_mmap_object(const std::filesystem::path& path, size_t offset, size_t size, - bool /* no_placeholder */) { + bool /* no_placeholder */, + MmapMode mode) { auto holder = std::make_shared(); - holder->set(path, offset, size); + holder->set(path, offset, size, mode); return holder; } diff --git a/src/common/util/src/os/win/win_mmap_object.cpp b/src/common/util/src/os/win/win_mmap_object.cpp index c1f637968aa145..f1c77bb3291144 100644 --- a/src/common/util/src/os/win/win_mmap_object.cpp +++ b/src/common/util/src/os/win/win_mmap_object.cpp @@ -285,7 +285,11 @@ class MapHolder : public ov::MappedMemory { MapHolder() = default; ~MapHolder() override; - void set(const std::filesystem::path& path, size_t offset, size_t size, bool no_placeholder = false); + void set(const std::filesystem::path& path, + size_t offset, + size_t size, + bool no_placeholder = false, + MmapMode mode = MmapMode::read); void set_from_handle(FileHandle handle, size_t offset, size_t size); bool try_remap_slot(uintptr_t fault_addr); @@ -319,7 +323,7 @@ class MapHolder : public ov::MappedMemory { void set_id(HANDLE h, size_t offset, size_t size); /** @brief Core setup shared by set() and set_from_handle(). */ - void setup(HANDLE file_handle, size_t offset, size_t size, bool no_placeholder); + void setup(HANDLE file_handle, size_t offset, size_t size, bool no_placeholder, MmapMode mode = MmapMode::read); /** @brief Try to establish the placeholder mapping. * Returns true on success; caller falls back to legacy path on false. @@ -327,7 +331,7 @@ class MapHolder : public ov::MappedMemory { bool try_placeholder_setup(size_t aligned_offset, size_t head_pad, size_t total_va_size, size_t file_size); /** @brief Legacy single-call MapViewOfFile path (no partial-release support). */ - void legacy_setup(size_t aligned_offset, size_t head_pad, size_t size); + void legacy_setup(size_t aligned_offset, size_t head_pad, size_t size, MmapMode mode = MmapMode::read); /** * @brief Computes the clamped, gran-aligned VA range to evict. @@ -591,9 +595,10 @@ bool MapHolder::try_placeholder_setup(size_t aligned_offset, size_t head_pad, si return true; } -void MapHolder::legacy_setup(size_t aligned_offset, size_t head_pad, size_t size) { +void MapHolder::legacy_setup(size_t aligned_offset, size_t head_pad, size_t size, MmapMode mode) { + const DWORD access = (mode == MmapMode::read_write) ? (FILE_MAP_READ | FILE_MAP_WRITE) : FILE_MAP_READ; if (auto view = ::MapViewOfFile(m_handle.get(), - FILE_MAP_READ, + access, static_cast(aligned_offset >> 32), static_cast(aligned_offset & 0xFFFFFFFF), head_pad + size)) { @@ -604,7 +609,7 @@ void MapHolder::legacy_setup(size_t aligned_offset, size_t head_pad, size_t size } } -void MapHolder::setup(HANDLE file_handle, size_t offset, size_t size, bool no_placeholder) { +void MapHolder::setup(HANDLE file_handle, size_t offset, size_t size, bool no_placeholder, MmapMode mode) { LARGE_INTEGER file_size_li{}; if (!::GetFileSizeEx(file_handle, &file_size_li)) { throw std::runtime_error{"GetFileSizeEx failed: " + std::to_string(::GetLastError())}; @@ -623,28 +628,36 @@ void MapHolder::setup(HANDLE file_handle, size_t offset, size_t size, bool no_pl const size_t total_va_size = util::align_size_up(r_length, gran); set_id(file_handle, offset, size); + if (mode == MmapMode::read_write) { + // A read-write mapping is not an immutable data source, so it must not be shared through id-based caches. + m_id = no_mapping_id; + } if (m_size == 0) { return; } - // Create a read-only file-mapping object for the whole file. - m_handle = HandleHolder{::CreateFileMappingW(file_handle, nullptr, PAGE_READONLY, 0, 0, nullptr)}; + const DWORD protect = (mode == MmapMode::read_write) ? PAGE_READWRITE : PAGE_READONLY; + m_handle = HandleHolder{::CreateFileMappingW(file_handle, nullptr, protect, 0, 0, nullptr)}; if (!m_handle.valid()) { throw std::runtime_error{"CreateFileMappingW failed: " + std::to_string(::GetLastError())}; } // When no_placeholder is set, skip the placeholder/VEH path to guarantee a single uniform AllocationBase // (required for NPU zero-copy blob import). Otherwise prefer placeholder for RSS reduction. - if (no_placeholder || !try_placeholder_setup(m_aligned_offset, head_pad, total_va_size, file_size)) { - legacy_setup(m_aligned_offset, head_pad, m_size); + // Read-write mappings always take the legacy path: the VEH only remaps on read faults, so an evicted + // granule hit by a write would fault indefinitely. + if (no_placeholder || mode == MmapMode::read_write || + !try_placeholder_setup(m_aligned_offset, head_pad, total_va_size, file_size)) { + legacy_setup(m_aligned_offset, head_pad, m_size, mode); } } -void MapHolder::set(const std::filesystem::path& path, size_t offset, size_t size, bool no_placeholder) { +void MapHolder::set(const std::filesystem::path& path, size_t offset, size_t size, bool no_placeholder, MmapMode mode) { + const bool writable = mode == MmapMode::read_write; auto fh = ::CreateFileW(path.c_str(), - GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_DELETE, + writable ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_READ, + writable ? (FILE_SHARE_READ | FILE_SHARE_WRITE) : (FILE_SHARE_READ | FILE_SHARE_DELETE), nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, @@ -655,7 +668,7 @@ void MapHolder::set(const std::filesystem::path& path, size_t offset, size_t siz } HandleHolder fh_holder{fh}; - setup(fh, offset, size, no_placeholder); + setup(fh, offset, size, no_placeholder, mode); // Keep the file handle alive so the section object can always resolve page faults // back to the original file data, even if the caller deletes or renames the file. // FILE_SHARE_DELETE allows std::filesystem::remove() to succeed while the mapping is alive. @@ -888,9 +901,10 @@ void MapHolder::hint_evict(size_t offset, size_t size) noexcept { std::shared_ptr load_mmap_object(const std::filesystem::path& path, size_t offset, size_t size, - bool no_placeholder) { + bool no_placeholder, + MmapMode mode) { auto holder = std::make_shared(); - holder->set(path, offset, size, no_placeholder); + holder->set(path, offset, size, no_placeholder, mode); return holder; } diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp index d66c62b9005316..69eb00247b64ee 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_tensor.hpp @@ -46,7 +46,8 @@ class RemoteTensorImpl : public ov::IRemoteTensor { uint32_t plane = 0, ov::intel_gpu::SharedBufferHandle shared_buffer_handle = {}, ov::intel_gpu::VirtualAddressMemory va_mem = ov::intel_gpu::VirtualAddressMemory(nullptr), - std::shared_ptr mapped_memory = nullptr); + std::shared_ptr mapped_memory = nullptr, + bool mapped_memory_read_only = true); ~RemoteTensorImpl() override; const AnyMap& get_properties() const override; @@ -92,6 +93,7 @@ class RemoteTensorImpl : public ov::IRemoteTensor { ov::intel_gpu::SharedBufferHandle m_shared_buffer_handle; ov::intel_gpu::VirtualAddressMemory m_va_mem; std::shared_ptr m_mapped_memory; // keeps the file mapping alive for the whole tensor lifetime + bool m_mapped_memory_read_only = true; size_t m_hash = 0; bool supports_caching() const; diff --git a/src/plugins/intel_gpu/src/plugin/remote_context.cpp b/src/plugins/intel_gpu/src/plugin/remote_context.cpp index b67f1f28dc02d5..4ee2dfb919b42c 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_context.cpp @@ -44,6 +44,16 @@ size_t get_mmap_offset_alignment() { #endif } +bool is_file_writable(const std::filesystem::path& path) { + std::error_code ec; + const auto perms = std::filesystem::status(path, ec).permissions(); + if (ec) { + return false; + } + return (perms & (std::filesystem::perms::owner_write | std::filesystem::perms::group_write | + std::filesystem::perms::others_write)) != std::filesystem::perms::none; +} + ContextType get_default_context_type() { #ifdef OV_GPU_WITH_ZE_RT return ContextType::ZE; @@ -312,7 +322,13 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_file(con alignment); // Memory-map the file. The mapping is retained inside the RemoteTensorImpl so it stays // alive for the whole tensor lifetime (GPU wraps the host pointer via CL_MEM_USE_HOST_PTR). - auto mapped_memory = ov::load_mmap_object(file_path, offset, *byte_size); + // Writable files get a read-write mapping so the GPU can also write back through it. + const bool read_only = !is_file_writable(file_path); + auto mapped_memory = ov::load_mmap_object(file_path, + offset, + *byte_size, + /*no_placeholder=*/false, + read_only ? ov::MmapMode::read : ov::MmapMode::read_write); return std::make_shared(get_this_shared_ptr(), shape, type, @@ -322,7 +338,8 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_file(con 0, ov::intel_gpu::SharedBufferHandle{}, VirtualAddressMemory{mapped_memory->data(), static_cast(*byte_size)}, - mapped_memory); + mapped_memory, + read_only); } std::shared_ptr RemoteContextImpl::create_buffer(const ov::element::Type type, const ov::Shape& shape) { diff --git a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp index 7d5a6d0a093cf9..b3dc7db1f8ff9f 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_tensor.cpp @@ -171,7 +171,8 @@ RemoteTensorImpl::RemoteTensorImpl(RemoteContextImpl::Ptr context, uint32_t plane, ov::intel_gpu::SharedBufferHandle shared_buffer_handle, ov::intel_gpu::VirtualAddressMemory va_mem, - std::shared_ptr mapped_memory) + std::shared_ptr mapped_memory, + bool mapped_memory_read_only) : m_context(context) , m_element_type(element_type) , m_shape(shape) @@ -182,7 +183,8 @@ RemoteTensorImpl::RemoteTensorImpl(RemoteContextImpl::Ptr context, , m_plane(plane) , m_shared_buffer_handle(shared_buffer_handle) , m_va_mem(va_mem) - , m_mapped_memory(std::move(mapped_memory)) { + , m_mapped_memory(std::move(mapped_memory)) + , m_mapped_memory_read_only(mapped_memory_read_only) { update_hash(); allocate(); } @@ -376,8 +378,8 @@ void RemoteTensorImpl::allocate() { } case TensorType::BT_CPU_VA: { const auto buffer_size = m_va_mem.size > -1 ? m_va_mem.size : m_layout.bytes_count(); - if (m_mapped_memory) { - // The plugin owns the mapping (file-mmap case), so the buffer is imported as read-only. + if (m_mapped_memory && m_mapped_memory_read_only) { + // The plugin owns a read-only mapping (file-mmap case), so the buffer is imported as read-only. m_memory_object = engine.create_hostbuffer(static_cast(m_va_mem.ptr), buffer_size, cldnn::allocation_type::cl_mem, diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index 46c9ae9981de3d..d916ad9fa6674f 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -2993,21 +2993,23 @@ TEST(GpuRemoteTensorFromCpu, smoke_allocAlignedCPUMemory) { ov::util::aligned_free(output_ptr); } -// : the tensor data starts at `offset` bytes into the file and spans `byte_size` bytes, -// so the file is `offset + byte_size` long. Both values are independent - `offset` only controls how much -// padding precedes the data and is varied to cover mmap allocation granularity boundaries. -using MmapFileMemoryParams = std::tuple; +// : the tensor data starts at `offset` bytes into the file and spans `byte_size` +// bytes, so the file is `offset + byte_size` long. Both values are independent - `offset` only controls how much +// padding precedes the data and is varied to cover mmap allocation granularity boundaries. `writable` controls +// the file permissions, which decide whether the plugin creates a read-only or a read-write mapping. +using MmapFileMemoryParams = std::tuple; class GpuRemoteTensorFromFile : public ::testing::TestWithParam { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { - const auto& [offset, byte_size] = obj.param; - return "offset_" + std::to_string(offset) + "_bytes_" + std::to_string(byte_size); + const auto& [offset, byte_size, writable] = obj.param; + return "offset_" + std::to_string(offset) + "_bytes_" + std::to_string(byte_size) + + (writable ? "_rw" : "_ro"); } }; TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { - const auto& [offset, byte_size] = GetParam(); + const auto& [offset, byte_size, writable] = GetParam(); ov::Core core; std::string target_device = ov::test::utils::DEVICE_GPU; @@ -3018,9 +3020,9 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { // Store input data in a file at a page-aligned offset, so the resulting file size is offset + byte_size. const std::filesystem::path file_path{"gpu_remote_tensor_from_file_" + std::to_string(offset) + "_" + - std::to_string(byte_size) + ".bin"}; + std::to_string(byte_size) + (writable ? "_rw" : "_ro") + ".bin"}; { - std::vector values(element_count, 2.0f); + std::vector values(element_count, 1.0f); std::ofstream file(file_path, std::ios::binary); if (offset > 0) { const std::vector padding(offset, 0); @@ -3030,6 +3032,13 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { } ASSERT_EQ(std::filesystem::file_size(file_path), offset + byte_size); + // The plugin picks a read-write mapping only for files it is allowed to write to. + const auto write_perms = std::filesystem::perms::owner_write | std::filesystem::perms::group_write | + std::filesystem::perms::others_write; + std::filesystem::permissions(file_path, + write_perms, + writable ? std::filesystem::perm_options::add : std::filesystem::perm_options::remove); + void* output_ptr = ov::util::aligned_alloc(byte_size, core.get_property(target_device, ov::intel_gpu::cacheline_size)); std::fill_n(static_cast(output_ptr), element_count, 0.0f); @@ -3048,11 +3057,37 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { infer_req.infer(); for (size_t i = 0; i < element_count; ++i) { - EXPECT_FLOAT_EQ(static_cast(output_ptr)[i], 2.0f) << "Mismatch at index " << i; + EXPECT_FLOAT_EQ(static_cast(output_ptr)[i], 1.0f) << "Mismatch at index " << i; + } + } + + // A writable file gets a read-write mapping, so it can also be used as an inference output. + if (writable) { + constexpr float written_value = 3.0f; + std::vector input_values(element_count, written_value); + { + auto remote_output_tensor = + ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::FileDescriptor{file_path, offset}); + + auto model = make_copy_model(shape); + auto compiled = core.compile_model(model, ctx); + auto infer_req = compiled.create_infer_request(); + infer_req.set_tensor(compiled.input(), ov::Tensor(ov::element::f32, shape, input_values.data())); + infer_req.set_tensor(compiled.output(), remote_output_tensor); + infer_req.infer(); + } + + std::vector file_content(element_count, 0.0f); + std::ifstream file(file_path, std::ios::binary); + file.seekg(offset); + file.read(reinterpret_cast(file_content.data()), byte_size); + for (size_t i = 0; i < element_count; ++i) { + EXPECT_FLOAT_EQ(file_content[i], written_value) << "Mismatch in file at index " << i; } } ov::util::aligned_free(output_ptr); + std::filesystem::permissions(file_path, write_perms, std::filesystem::perm_options::add); std::filesystem::remove(file_path); } @@ -3063,16 +3098,29 @@ constexpr std::size_t mmap_granularity = 65536; constexpr std::size_t mmap_granularity = 4096; #endif +std::vector generate_mmap_file_memory_params() { + const std::vector> layouts{ + {0, 256}, + {0, 4 * mmap_granularity}, + {mmap_granularity, 256}, + {2 * mmap_granularity, 256}, + {16 * mmap_granularity, 256}, + {mmap_granularity, mmap_granularity}, + {3 * mmap_granularity, 4 * mmap_granularity}, + {8 * mmap_granularity, 16 * mmap_granularity}}; + + std::vector params; + params.reserve(layouts.size() * 2); + for (const auto& [offset, byte_size] : layouts) { + params.emplace_back(offset, byte_size, false); + params.emplace_back(offset, byte_size, true); + } + return params; +} + INSTANTIATE_TEST_SUITE_P(smoke_mmapFileMemory, GpuRemoteTensorFromFile, - ::testing::Values(MmapFileMemoryParams{0, 256}, - MmapFileMemoryParams{0, 4 * mmap_granularity}, - MmapFileMemoryParams{mmap_granularity, 256}, - MmapFileMemoryParams{2 * mmap_granularity, 256}, - MmapFileMemoryParams{16 * mmap_granularity, 256}, - MmapFileMemoryParams{mmap_granularity, mmap_granularity}, - MmapFileMemoryParams{3 * mmap_granularity, 4 * mmap_granularity}, - MmapFileMemoryParams{8 * mmap_granularity, 16 * mmap_granularity}), + ::testing::ValuesIn(generate_mmap_file_memory_params()), GpuRemoteTensorFromFile::getTestCaseName); #endif // OV_GPU_WITH_OCL_RT From b519c40b17eae3c2c13091174ea90482ee4e237b Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Mon, 10 Aug 2026 14:37:34 +0200 Subject: [PATCH 70/84] tests rw --- .../ocl_remote_tensor_tests.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index d916ad9fa6674f..ab764becaffd4d 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -3022,7 +3022,10 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { const std::filesystem::path file_path{"gpu_remote_tensor_from_file_" + std::to_string(offset) + "_" + std::to_string(byte_size) + (writable ? "_rw" : "_ro") + ".bin"}; { - std::vector values(element_count, 1.0f); + std::vector values(element_count); + for (size_t i = 0; i < element_count; ++i) { + values[i] = static_cast(i + 1); + } std::ofstream file(file_path, std::ios::binary); if (offset > 0) { const std::vector padding(offset, 0); @@ -3057,14 +3060,16 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { infer_req.infer(); for (size_t i = 0; i < element_count; ++i) { - EXPECT_FLOAT_EQ(static_cast(output_ptr)[i], 1.0f) << "Mismatch at index " << i; + EXPECT_FLOAT_EQ(static_cast(output_ptr)[i], static_cast(i + 1)) << "Mismatch at index " << i; } } // A writable file gets a read-write mapping, so it can also be used as an inference output. if (writable) { - constexpr float written_value = 3.0f; - std::vector input_values(element_count, written_value); + std::vector input_values(element_count); + for (size_t i = 0; i < element_count; ++i) { + input_values[i] = static_cast(element_count - i); + } { auto remote_output_tensor = ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::FileDescriptor{file_path, offset}); @@ -3082,7 +3087,7 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { file.seekg(offset); file.read(reinterpret_cast(file_content.data()), byte_size); for (size_t i = 0; i < element_count; ++i) { - EXPECT_FLOAT_EQ(file_content[i], written_value) << "Mismatch in file at index " << i; + EXPECT_FLOAT_EQ(file_content[i], input_values[i]) << "Mismatch in file at index " << i; } } From 849eba88eb903c829df030cbe8ff1c6459d83004 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Mon, 10 Aug 2026 15:15:50 +0200 Subject: [PATCH 71/84] doc update Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../gpu-device/remote-tensor-api-gpu-plugin.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst b/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst index ff4f3abc078131..94c68cccdee6a0 100644 --- a/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst +++ b/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst @@ -270,9 +270,11 @@ For more details, see the code snippets below: .. tab-item:: file :sync: file - Use this overload to wrap tensor data stored in a file. The plugin memory-maps the file - and keeps the mapping alive for the whole lifetime of the created remote tensor, so the - file must not be modified until the tensor is destroyed. + Use this overload to wrap tensor data stored in a file. A writable file is mapped read-write, + may be used as an inference output, and receives changes made through the tensor. A non-writable + file is mapped read-only and may only be used as an inference input. The plugin keeps the mapping + alive for the whole lifetime of the created remote tensor, so the file must not otherwise be + modified until the tensor is destroyed. .. doxygensnippet:: docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp :language: cpp From 0e45da90599237e18ccf40e680bd9d103ea17fd8 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Mon, 10 Aug 2026 16:04:10 +0200 Subject: [PATCH 72/84] apply copilot insights and more --- .../include/openvino/util/mmap_object.hpp | 6 +- .../util/src/os/lin/lin_mmap_object.cpp | 10 +- .../util/src/os/win/win_mmap_object.cpp | 16 +- src/core/tests/mmap_object.cpp | 58 ++++++ .../intel_gpu/src/plugin/remote_context.cpp | 14 +- .../ocl_remote_tensor_tests.cpp | 171 +++++++++++------- 6 files changed, 181 insertions(+), 94 deletions(-) diff --git a/src/common/util/include/openvino/util/mmap_object.hpp b/src/common/util/include/openvino/util/mmap_object.hpp index ee4f29341f3fdf..068073a807f887 100644 --- a/src/common/util/include/openvino/util/mmap_object.hpp +++ b/src/common/util/include/openvino/util/mmap_object.hpp @@ -32,8 +32,8 @@ inline constexpr auto auto_size = std::numeric_limits::max(); * @brief Access mode of a memory mapping. */ enum class MmapMode { - read, //!< Read-only mapping. - read_write //!< Read-write mapping, modifications are written back to the file. + READ, //!< Read-only mapping. + READ_WRITE //!< Read-write mapping, modifications are written back to the file. }; /** @@ -82,7 +82,7 @@ std::shared_ptr load_mmap_object(const std::filesystem::path& size_t offset = 0, size_t size = auto_size, bool no_placeholder = false, - MmapMode mode = MmapMode::read); + MmapMode mode = MmapMode::READ); /** * @brief Returns mapped memory for a file from provided file handle (cross-platform). diff --git a/src/common/util/src/os/lin/lin_mmap_object.cpp b/src/common/util/src/os/lin/lin_mmap_object.cpp index 3a0c2194d276f2..ef9f0315e0b6d9 100644 --- a/src/common/util/src/os/lin/lin_mmap_object.cpp +++ b/src/common/util/src/os/lin/lin_mmap_object.cpp @@ -108,17 +108,17 @@ class MapHolder final : public MappedMemory { MapHolder() = default; void set(const std::filesystem::path& path, const size_t offset, const size_t size, const MmapMode mmap_mode) { - int mode = (mmap_mode == MmapMode::read_write) ? O_RDWR : O_RDONLY; + int mode = (mmap_mode == MmapMode::READ_WRITE) ? O_RDWR : O_RDONLY; int fd = open(path.c_str(), mode); if (fd == -1) { throw std::runtime_error("Can not open file " + util::path_to_string(path) + " for mapping. Ensure that file exists and has appropriate permissions."); } set_from_fd(fd, offset, size, mmap_mode); - m_id = (mmap_mode == MmapMode::read_write) ? no_mapping_id : util::get_id_for_file(path, offset, size); + m_id = (mmap_mode == MmapMode::READ_WRITE) ? no_mapping_id : util::get_id_for_file(path, offset, size); } - void set_from_fd(const int fd, const size_t offset, const size_t size, const MmapMode mmap_mode = MmapMode::read) { + void set_from_fd(const int fd, const size_t offset, const size_t size, const MmapMode mmap_mode = MmapMode::READ) { m_handle = HandleHolder(fd); struct stat sb = {}; @@ -132,7 +132,7 @@ class MapHolder final : public MappedMemory { } if (m_size > 0) { - const auto prot = (mmap_mode == MmapMode::read_write) ? (PROT_READ | PROT_WRITE) : PROT_READ; + const auto prot = (mmap_mode == MmapMode::READ_WRITE) ? (PROT_READ | PROT_WRITE) : PROT_READ; const auto& [aligned_offset, length, gap] = util::make_mmap_region(offset, m_size); m_mapped_view_size = length; m_mapped_view = mmap(nullptr, length, prot, MAP_SHARED, fd, aligned_offset); @@ -143,7 +143,7 @@ class MapHolder final : public MappedMemory { m_data = static_cast(m_mapped_view) + gap; } // A read-write mapping is not an immutable data source, so it must not be shared through id-based caches. - m_id = (mmap_mode == MmapMode::read_write) + m_id = (mmap_mode == MmapMode::READ_WRITE) ? no_mapping_id : util::u64_hash_combine(static_cast(sb.st_ino), {static_cast(sb.st_dev), offset, size}); diff --git a/src/common/util/src/os/win/win_mmap_object.cpp b/src/common/util/src/os/win/win_mmap_object.cpp index f1c77bb3291144..d1a120274443e5 100644 --- a/src/common/util/src/os/win/win_mmap_object.cpp +++ b/src/common/util/src/os/win/win_mmap_object.cpp @@ -289,7 +289,7 @@ class MapHolder : public ov::MappedMemory { size_t offset, size_t size, bool no_placeholder = false, - MmapMode mode = MmapMode::read); + MmapMode mode = MmapMode::READ); void set_from_handle(FileHandle handle, size_t offset, size_t size); bool try_remap_slot(uintptr_t fault_addr); @@ -323,7 +323,7 @@ class MapHolder : public ov::MappedMemory { void set_id(HANDLE h, size_t offset, size_t size); /** @brief Core setup shared by set() and set_from_handle(). */ - void setup(HANDLE file_handle, size_t offset, size_t size, bool no_placeholder, MmapMode mode = MmapMode::read); + void setup(HANDLE file_handle, size_t offset, size_t size, bool no_placeholder, MmapMode mode = MmapMode::READ); /** @brief Try to establish the placeholder mapping. * Returns true on success; caller falls back to legacy path on false. @@ -331,7 +331,7 @@ class MapHolder : public ov::MappedMemory { bool try_placeholder_setup(size_t aligned_offset, size_t head_pad, size_t total_va_size, size_t file_size); /** @brief Legacy single-call MapViewOfFile path (no partial-release support). */ - void legacy_setup(size_t aligned_offset, size_t head_pad, size_t size, MmapMode mode = MmapMode::read); + void legacy_setup(size_t aligned_offset, size_t head_pad, size_t size, MmapMode mode = MmapMode::READ); /** * @brief Computes the clamped, gran-aligned VA range to evict. @@ -596,7 +596,7 @@ bool MapHolder::try_placeholder_setup(size_t aligned_offset, size_t head_pad, si } void MapHolder::legacy_setup(size_t aligned_offset, size_t head_pad, size_t size, MmapMode mode) { - const DWORD access = (mode == MmapMode::read_write) ? (FILE_MAP_READ | FILE_MAP_WRITE) : FILE_MAP_READ; + const DWORD access = (mode == MmapMode::READ_WRITE) ? (FILE_MAP_READ | FILE_MAP_WRITE) : FILE_MAP_READ; if (auto view = ::MapViewOfFile(m_handle.get(), access, static_cast(aligned_offset >> 32), @@ -628,7 +628,7 @@ void MapHolder::setup(HANDLE file_handle, size_t offset, size_t size, bool no_pl const size_t total_va_size = util::align_size_up(r_length, gran); set_id(file_handle, offset, size); - if (mode == MmapMode::read_write) { + if (mode == MmapMode::READ_WRITE) { // A read-write mapping is not an immutable data source, so it must not be shared through id-based caches. m_id = no_mapping_id; } @@ -637,7 +637,7 @@ void MapHolder::setup(HANDLE file_handle, size_t offset, size_t size, bool no_pl return; } - const DWORD protect = (mode == MmapMode::read_write) ? PAGE_READWRITE : PAGE_READONLY; + const DWORD protect = (mode == MmapMode::READ_WRITE) ? PAGE_READWRITE : PAGE_READONLY; m_handle = HandleHolder{::CreateFileMappingW(file_handle, nullptr, protect, 0, 0, nullptr)}; if (!m_handle.valid()) { throw std::runtime_error{"CreateFileMappingW failed: " + std::to_string(::GetLastError())}; @@ -647,14 +647,14 @@ void MapHolder::setup(HANDLE file_handle, size_t offset, size_t size, bool no_pl // (required for NPU zero-copy blob import). Otherwise prefer placeholder for RSS reduction. // Read-write mappings always take the legacy path: the VEH only remaps on read faults, so an evicted // granule hit by a write would fault indefinitely. - if (no_placeholder || mode == MmapMode::read_write || + if (no_placeholder || mode == MmapMode::READ_WRITE || !try_placeholder_setup(m_aligned_offset, head_pad, total_va_size, file_size)) { legacy_setup(m_aligned_offset, head_pad, m_size, mode); } } void MapHolder::set(const std::filesystem::path& path, size_t offset, size_t size, bool no_placeholder, MmapMode mode) { - const bool writable = mode == MmapMode::read_write; + const bool writable = mode == MmapMode::READ_WRITE; auto fh = ::CreateFileW(path.c_str(), writable ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_READ, writable ? (FILE_SHARE_READ | FILE_SHARE_WRITE) : (FILE_SHARE_READ | FILE_SHARE_DELETE), diff --git a/src/core/tests/mmap_object.cpp b/src/core/tests/mmap_object.cpp index d5974e39e80051..5beba68273e7d8 100644 --- a/src/core/tests/mmap_object.cpp +++ b/src/core/tests/mmap_object.cpp @@ -236,6 +236,64 @@ INSTANTIATE_TEST_SUITE_P(MappedMemory, ::testing::ValuesIn(std::vector{true, false})), RangedMappingTest::test_name); +class ReadWriteMappingTest : public ::testing::Test { +protected: + std::filesystem::path m_file_path; + std::vector m_content; + static constexpr size_t k_file_size = 128 * 1024; + + void SetUp() override { + m_content = utils::make_modulo_sequence_pattern(k_file_size); + m_file_path = utils::generateTestFilePrefix() + "_rw_mapping"; + ov::util::save_binary(m_file_path, m_content.data(), m_content.size()); + } + + void TearDown() override { + std::filesystem::remove(m_file_path); + } + + std::vector read_file() const { + std::vector data(static_cast(std::filesystem::file_size(m_file_path))); + std::ifstream is(m_file_path, std::ios::binary); + is.read(reinterpret_cast(data.data()), static_cast(data.size())); + return data; + } +}; + +TEST_F(ReadWriteMappingTest, writes_at_offset_leave_other_bytes_intact) { + constexpr size_t k_offset = 64 * 1024; + constexpr size_t k_size = 512; + + auto expected = m_content; + std::fill_n(expected.begin() + k_offset, k_size, uint8_t{0x5A}); + + { + auto mm = load_mmap_object(m_file_path, k_offset, k_size, false, MmapMode::READ_WRITE); + ASSERT_NE(mm, nullptr); + ASSERT_EQ(mm->size(), k_size); + ASSERT_THAT(std::vector(m_content.begin() + k_offset, m_content.begin() + k_offset + k_size), + ElementsAreArray(reinterpret_cast(mm->data()), mm->size())); + + std::fill_n(reinterpret_cast(mm->data()), mm->size(), uint8_t{0x5A}); + } + + EXPECT_THAT(read_file(), ElementsAreArray(expected)); +} + +TEST_F(ReadWriteMappingTest, read_write_mappings_report_no_mapping_id) { + auto rw_whole = load_mmap_object(m_file_path, 0, auto_size, false, MmapMode::READ_WRITE); + auto rw_part = load_mmap_object(m_file_path, 128, 256, false, MmapMode::READ_WRITE); + auto ro = load_mmap_object(m_file_path); + + ASSERT_NE(rw_whole, nullptr); + ASSERT_NE(rw_part, nullptr); + ASSERT_NE(ro, nullptr); + + EXPECT_EQ(rw_whole->get_id(), no_mapping_id); + EXPECT_EQ(rw_part->get_id(), no_mapping_id); + EXPECT_NE(ro->get_id(), no_mapping_id); +} + class HintEvictTest : public ::testing::Test { protected: std::filesystem::path m_file_path; diff --git a/src/plugins/intel_gpu/src/plugin/remote_context.cpp b/src/plugins/intel_gpu/src/plugin/remote_context.cpp index 4ee2dfb919b42c..3d8d9ae9e7d98d 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_context.cpp @@ -12,6 +12,7 @@ #include "intel_gpu/plugin/usm_host_tensor.hpp" #include "intel_gpu/runtime/itt.hpp" #include "intel_gpu/runtime/device_query.hpp" +#include #include #ifdef _WIN32 @@ -44,14 +45,11 @@ size_t get_mmap_offset_alignment() { #endif } +// Permission bits (and Windows ACLs) describe what some account may do, not what this process may do, +// so writability is probed by opening the file for update with the current credentials. bool is_file_writable(const std::filesystem::path& path) { - std::error_code ec; - const auto perms = std::filesystem::status(path, ec).permissions(); - if (ec) { - return false; - } - return (perms & (std::filesystem::perms::owner_write | std::filesystem::perms::group_write | - std::filesystem::perms::others_write)) != std::filesystem::perms::none; + std::fstream file(path, std::ios::in | std::ios::out | std::ios::binary); + return file.is_open(); } ContextType get_default_context_type() { @@ -328,7 +326,7 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_file(con offset, *byte_size, /*no_placeholder=*/false, - read_only ? ov::MmapMode::read : ov::MmapMode::read_write); + read_only ? ov::MmapMode::READ : ov::MmapMode::READ_WRITE); return std::make_shared(get_this_shared_ptr(), shape, type, diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index ab764becaffd4d..a0f5db024b3624 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -17,6 +17,7 @@ #include "openvino/runtime/intel_gpu/properties.hpp" #include "openvino/runtime/remote_tensor.hpp" #include "openvino/util/memory.hpp" +#include "openvino/util/mmap_object.hpp" #include "remote_tensor_tests/helpers.hpp" #include "common_test_utils/ov_tensor_utils.hpp" @@ -2993,61 +2994,84 @@ TEST(GpuRemoteTensorFromCpu, smoke_allocAlignedCPUMemory) { ov::util::aligned_free(output_ptr); } -// : the tensor data starts at `offset` bytes into the file and spans `byte_size` -// bytes, so the file is `offset + byte_size` long. Both values are independent - `offset` only controls how much -// padding precedes the data and is varied to cover mmap allocation granularity boundaries. `writable` controls -// the file permissions, which decide whether the plugin creates a read-only or a read-write mapping. -using MmapFileMemoryParams = std::tuple; +// : the tensor data starts at `offset` bytes into the file and spans `byte_size` bytes, so the +// file is `offset + byte_size` long. Both values are independent - `offset` only controls how much padding precedes +// the data and is varied to cover mmap allocation granularity boundaries. +using MmapFileMemoryParams = std::tuple; + +constexpr auto file_write_perms = std::filesystem::perms::owner_write | std::filesystem::perms::group_write | + std::filesystem::perms::others_write; class GpuRemoteTensorFromFile : public ::testing::TestWithParam { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { - const auto& [offset, byte_size, writable] = obj.param; - return "offset_" + std::to_string(offset) + "_bytes_" + std::to_string(byte_size) + - (writable ? "_rw" : "_ro"); + const auto& [offset, byte_size] = obj.param; + return "offset_" + std::to_string(offset) + "_bytes_" + std::to_string(byte_size); } -}; -TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { - const auto& [offset, byte_size, writable] = GetParam(); +protected: + std::filesystem::path m_file_path; - ov::Core core; - std::string target_device = ov::test::utils::DEVICE_GPU; - const size_t float_size = sizeof(float); - const ov::Shape shape{byte_size / float_size}; - const size_t element_count = ov::shape_size(shape); - auto ctx = core.get_default_context(target_device).as(); + void SetUp() override { + m_file_path = ov::test::utils::generateTestFilePrefix() + ".bin"; + } - // Store input data in a file at a page-aligned offset, so the resulting file size is offset + byte_size. - const std::filesystem::path file_path{"gpu_remote_tensor_from_file_" + std::to_string(offset) + "_" + - std::to_string(byte_size) + (writable ? "_rw" : "_ro") + ".bin"}; - { - std::vector values(element_count); - for (size_t i = 0; i < element_count; ++i) { - values[i] = static_cast(i + 1); - } - std::ofstream file(file_path, std::ios::binary); + void TearDown() override { + std::error_code ec; + std::filesystem::permissions(m_file_path, file_write_perms, std::filesystem::perm_options::add, ec); + std::filesystem::remove(m_file_path, ec); + } + + static void write_data_at_offset(const std::filesystem::path& path, + std::size_t offset, + const std::vector& values) { + std::ofstream file(path, std::ios::binary); if (offset > 0) { const std::vector padding(offset, 0); file.write(padding.data(), padding.size()); } - file.write(reinterpret_cast(values.data()), byte_size); + file.write(reinterpret_cast(values.data()), values.size() * sizeof(float)); } - ASSERT_EQ(std::filesystem::file_size(file_path), offset + byte_size); // The plugin picks a read-write mapping only for files it is allowed to write to. - const auto write_perms = std::filesystem::perms::owner_write | std::filesystem::perms::group_write | - std::filesystem::perms::others_write; - std::filesystem::permissions(file_path, - write_perms, - writable ? std::filesystem::perm_options::add : std::filesystem::perm_options::remove); + static void set_file_writable(const std::filesystem::path& path, bool writable) { + std::filesystem::permissions( + path, + file_write_perms, + writable ? std::filesystem::perm_options::add : std::filesystem::perm_options::remove); + } + + static std::vector make_values(std::size_t element_count) { + std::vector values(element_count); + for (std::size_t i = 0; i < element_count; ++i) { + values[i] = static_cast(i + 1); + } + return values; + } +}; + +TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemoryAsInput) { + const auto& [offset, byte_size] = GetParam(); + + ov::Core core; + std::string target_device = ov::test::utils::DEVICE_GPU; + const ov::Shape shape{byte_size / sizeof(float)}; + const size_t element_count = ov::shape_size(shape); + auto ctx = core.get_default_context(target_device).as(); + + // Store input data in a file at a page-aligned offset, so the resulting file size is offset + byte_size. + const auto input_values = make_values(element_count); + write_data_at_offset(m_file_path, offset, input_values); + ASSERT_EQ(std::filesystem::file_size(m_file_path), offset + byte_size); + // A read-only file exercises the read-only mapping path. + set_file_writable(m_file_path, false); void* output_ptr = ov::util::aligned_alloc(byte_size, core.get_property(target_device, ov::intel_gpu::cacheline_size)); std::fill_n(static_cast(output_ptr), element_count, 0.0f); { auto remote_input_tensor = - ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::FileDescriptor{file_path, offset}); + ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::FileDescriptor{m_file_path, offset}); ASSERT_TRUE(remote_input_tensor.is()); auto remote_output_tensor = ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::VirtualAddressMemory(output_ptr)); @@ -3060,50 +3084,58 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemory) { infer_req.infer(); for (size_t i = 0; i < element_count; ++i) { - EXPECT_FLOAT_EQ(static_cast(output_ptr)[i], static_cast(i + 1)) << "Mismatch at index " << i; + EXPECT_FLOAT_EQ(static_cast(output_ptr)[i], input_values[i]) << "Mismatch at index " << i; } } - // A writable file gets a read-write mapping, so it can also be used as an inference output. - if (writable) { - std::vector input_values(element_count); - for (size_t i = 0; i < element_count; ++i) { - input_values[i] = static_cast(element_count - i); - } - { - auto remote_output_tensor = - ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::FileDescriptor{file_path, offset}); - - auto model = make_copy_model(shape); - auto compiled = core.compile_model(model, ctx); - auto infer_req = compiled.create_infer_request(); - infer_req.set_tensor(compiled.input(), ov::Tensor(ov::element::f32, shape, input_values.data())); - infer_req.set_tensor(compiled.output(), remote_output_tensor); - infer_req.infer(); - } + ov::util::aligned_free(output_ptr); +} - std::vector file_content(element_count, 0.0f); - std::ifstream file(file_path, std::ios::binary); - file.seekg(offset); - file.read(reinterpret_cast(file_content.data()), byte_size); - for (size_t i = 0; i < element_count; ++i) { - EXPECT_FLOAT_EQ(file_content[i], input_values[i]) << "Mismatch in file at index " << i; - } +TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemoryAsOutput) { + const auto& [offset, byte_size] = GetParam(); + + ov::Core core; + std::string target_device = ov::test::utils::DEVICE_GPU; + const ov::Shape shape{byte_size / sizeof(float)}; + const size_t element_count = ov::shape_size(shape); + auto ctx = core.get_default_context(target_device).as(); + + const auto input_values = make_values(element_count); + write_data_at_offset(m_file_path, offset, std::vector(element_count, 0.0f)); + ASSERT_EQ(std::filesystem::file_size(m_file_path), offset + byte_size); + // A writable file is mapped read-write, so the GPU can write inference results back through the mapping. + set_file_writable(m_file_path, true); + + { + auto remote_output_tensor = + ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::FileDescriptor{m_file_path, offset}); + + auto model = make_copy_model(shape); + auto compiled = core.compile_model(model, ctx); + auto infer_req = compiled.create_infer_request(); + infer_req.set_tensor(compiled.input(), + ov::Tensor(ov::element::f32, shape, const_cast(input_values.data()))); + infer_req.set_tensor(compiled.output(), remote_output_tensor); + infer_req.infer(); } - ov::util::aligned_free(output_ptr); - std::filesystem::permissions(file_path, write_perms, std::filesystem::perm_options::add); - std::filesystem::remove(file_path); + std::vector file_content(element_count, 0.0f); + std::ifstream file(m_file_path, std::ios::binary); + file.seekg(offset); + file.read(reinterpret_cast(file_content.data()), byte_size); + for (size_t i = 0; i < element_count; ++i) { + EXPECT_FLOAT_EQ(file_content[i], input_values[i]) << "Mismatch in file at index " << i; + } } +std::vector generate_mmap_file_memory_params() { #ifdef _WIN32 -// Windows maps file views with 64K allocation granularity -constexpr std::size_t mmap_granularity = 65536; + // Windows maps file views with 64K allocation granularity + const std::size_t mmap_granularity = 65536; #else -constexpr std::size_t mmap_granularity = 4096; + // Page size varies per platform: 4K on x86-64, 16K or 64K on some ARM64 Linux distributions. + const auto mmap_granularity = static_cast(ov::util::get_system_page_size()); #endif - -std::vector generate_mmap_file_memory_params() { const std::vector> layouts{ {0, 256}, {0, 4 * mmap_granularity}, @@ -3115,10 +3147,9 @@ std::vector generate_mmap_file_memory_params() { {8 * mmap_granularity, 16 * mmap_granularity}}; std::vector params; - params.reserve(layouts.size() * 2); + params.reserve(layouts.size()); for (const auto& [offset, byte_size] : layouts) { - params.emplace_back(offset, byte_size, false); - params.emplace_back(offset, byte_size, true); + params.emplace_back(offset, byte_size); } return params; } From b388ab7b8ccca44d178d09fa9876859e618ab8d1 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Mon, 10 Aug 2026 16:56:36 +0200 Subject: [PATCH 73/84] fix comments of copilot --- .../intel_gpu/src/plugin/remote_context.cpp | 10 +++++++++- .../ocl_remote_tensor_tests.cpp | 14 +++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/plugins/intel_gpu/src/plugin/remote_context.cpp b/src/plugins/intel_gpu/src/plugin/remote_context.cpp index d246663cb0e290..b9cfdd105e295e 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_context.cpp @@ -12,6 +12,7 @@ #include "intel_gpu/plugin/usm_host_tensor.hpp" #include "intel_gpu/runtime/itt.hpp" #include "intel_gpu/runtime/device_query.hpp" +#include "intel_gpu/runtime/utils.hpp" #include #include @@ -327,6 +328,13 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_file(con *byte_size, /*no_placeholder=*/false, read_only ? ov::MmapMode::READ : ov::MmapMode::READ_WRITE); + + auto import_size = *byte_size; + const auto cacheline_size = static_cast(get_engine().get_device_info().cacheline_size.value_or(0)); + if (cacheline_size > 0 && alignment % cacheline_size == 0) { + import_size = cldnn::align_to(import_size, cacheline_size); + } + return std::make_shared(get_this_shared_ptr(), shape, type, @@ -335,7 +343,7 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_file(con 0, 0, ov::intel_gpu::SharedBufferHandle{}, - VirtualAddressMemory{mapped_memory->data(), static_cast(*byte_size)}, + VirtualAddressMemory{mapped_memory->data(), static_cast(import_size)}, mapped_memory, read_only); } diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index d37ea152d9a9a3..cda3c9bf3fbf66 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -3179,15 +3179,20 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemoryAsInput) { // A read-only file exercises the read-only mapping path. set_file_writable(m_file_path, false); - void* output_ptr = ov::util::aligned_alloc(byte_size, core.get_property(target_device, ov::intel_gpu::cacheline_size)); - std::fill_n(static_cast(output_ptr), element_count, 0.0f); + // Host pointers are imported in whole cachelines, so the destination buffer is padded accordingly. + const std::size_t cacheline = core.get_property(target_device, ov::intel_gpu::cacheline_size); + const std::size_t output_buffer_size = ((byte_size + cacheline - 1) / cacheline) * cacheline; + void* output_ptr = ov::util::aligned_alloc(output_buffer_size, cacheline); + std::fill_n(static_cast(output_ptr), output_buffer_size, 0); { auto remote_input_tensor = ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::FileDescriptor{m_file_path, offset}); ASSERT_TRUE(remote_input_tensor.is()); auto remote_output_tensor = - ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::VirtualAddressMemory(output_ptr)); + ctx.create_tensor(ov::element::f32, + shape, + ov::intel_gpu::VirtualAddressMemory(output_ptr, static_cast(output_buffer_size))); auto model = make_copy_model(shape); auto compiled = core.compile_model(model, ctx); @@ -3250,6 +3255,9 @@ std::vector generate_mmap_file_memory_params() { const auto mmap_granularity = static_cast(ov::util::get_system_page_size()); #endif const std::vector> layouts{ + // Sizes smaller than / not divisible by the device cacheline size, e.g. f32 with shape {1}. + {0, sizeof(float)}, + {mmap_granularity, 3 * sizeof(float)}, {0, 256}, {0, 4 * mmap_granularity}, {mmap_granularity, 256}, From 49ecdf3b6c5e82b03251ec3583a738857f2e2fba Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Mon, 10 Aug 2026 17:51:32 +0200 Subject: [PATCH 74/84] apply some of reviewer comments --- .../util/include/openvino/util/mmap_object.hpp | 8 +------- src/common/util/src/os/lin/lin_mmap_object.cpp | 9 +++------ src/common/util/src/os/win/win_mmap_object.cpp | 4 ---- src/core/tests/mmap_object.cpp | 15 +-------------- 4 files changed, 5 insertions(+), 31 deletions(-) diff --git a/src/common/util/include/openvino/util/mmap_object.hpp b/src/common/util/include/openvino/util/mmap_object.hpp index f162a56cd6a6fc..9db9931cf3e7fc 100644 --- a/src/common/util/include/openvino/util/mmap_object.hpp +++ b/src/common/util/include/openvino/util/mmap_object.hpp @@ -37,11 +37,6 @@ enum class MmapMode { READ_WRITE //!< Read-write mapping, modifications are written back to the file. }; -/** - * @brief Id reported by mappings whose content is mutable, so they must never be substituted for one another. - */ -inline constexpr uint64_t no_mapping_id = 0; - /** * @brief This class represents a mapped memory. * Instead of reading files, we can map the memory via mmap for Linux or MapViewOfFile for Windows. @@ -86,8 +81,7 @@ class MappedMemory { * @param no_placeholder When true, skip the Windows 10+ placeholder/VEH mechanism and use the legacy * single-call MapViewOfFile path instead. This guarantees a uniform AllocationBase * across the whole mapping, required for NPU zero-copy blob import. On Linux ignored. - * @param mode Access mode of the mapping. A read_write mapping requires the file to be writable and reports - * no_mapping_id, because get_id() marks an immutable data source that consumers may share. + * @param mode Access mode of the mapping. A read-write mapping requires the file to be writable. * @return MappedMemory shared ptr object which keep mmaped memory and control the lifetime. */ std::shared_ptr load_mmap_object(const std::filesystem::path& path, diff --git a/src/common/util/src/os/lin/lin_mmap_object.cpp b/src/common/util/src/os/lin/lin_mmap_object.cpp index b534e298bbd896..decae78a54800a 100644 --- a/src/common/util/src/os/lin/lin_mmap_object.cpp +++ b/src/common/util/src/os/lin/lin_mmap_object.cpp @@ -149,7 +149,7 @@ class MapHolder final : public MappedMemory { " for mapping. Ensure that file exists and has appropriate permissions."); } set_from_fd(fd, offset, size, mmap_mode); - m_id = (mmap_mode == MmapMode::READ_WRITE) ? no_mapping_id : util::get_id_for_file(path, offset, size); + m_id = util::get_id_for_file(path, offset, size); } void set_from_fd(const int fd, const size_t offset, const size_t size, const MmapMode mmap_mode = MmapMode::READ) { @@ -176,11 +176,8 @@ class MapHolder final : public MappedMemory { } m_data = static_cast(m_mapped_view) + gap; } - // A read-write mapping is not an immutable data source, so it must not be shared through id-based caches. - m_id = (mmap_mode == MmapMode::READ_WRITE) - ? no_mapping_id - : util::u64_hash_combine(static_cast(sb.st_ino), - {static_cast(sb.st_dev), offset, size}); + m_id = + util::u64_hash_combine(static_cast(sb.st_ino), {static_cast(sb.st_dev), offset, size}); } uint64_t get_id() const noexcept override { diff --git a/src/common/util/src/os/win/win_mmap_object.cpp b/src/common/util/src/os/win/win_mmap_object.cpp index 63e812e1135231..d87702b774b0f7 100644 --- a/src/common/util/src/os/win/win_mmap_object.cpp +++ b/src/common/util/src/os/win/win_mmap_object.cpp @@ -651,10 +651,6 @@ void MapHolder::setup(HANDLE file_handle, size_t offset, size_t size, bool no_pl const size_t total_va_size = util::align_size_up(r_length, gran); set_id(file_handle, offset, size); - if (mode == MmapMode::READ_WRITE) { - // A read-write mapping is not an immutable data source, so it must not be shared through id-based caches. - m_id = no_mapping_id; - } if (m_size == 0) { return; diff --git a/src/core/tests/mmap_object.cpp b/src/core/tests/mmap_object.cpp index 9c03b87c32310e..e4f129206b3012 100644 --- a/src/core/tests/mmap_object.cpp +++ b/src/core/tests/mmap_object.cpp @@ -270,6 +270,7 @@ TEST_F(ReadWriteMappingTest, writes_at_offset_leave_other_bytes_intact) { auto expected = m_content; std::fill_n(expected.begin() + k_offset, k_size, uint8_t{0x5A}); + ASSERT_NE(expected, m_content); { auto mm = load_mmap_object(m_file_path, k_offset, k_size, false, MmapMode::READ_WRITE); @@ -284,20 +285,6 @@ TEST_F(ReadWriteMappingTest, writes_at_offset_leave_other_bytes_intact) { EXPECT_THAT(read_file(), ElementsAreArray(expected)); } -TEST_F(ReadWriteMappingTest, read_write_mappings_report_no_mapping_id) { - auto rw_whole = load_mmap_object(m_file_path, 0, auto_size, false, MmapMode::READ_WRITE); - auto rw_part = load_mmap_object(m_file_path, 128, 256, false, MmapMode::READ_WRITE); - auto ro = load_mmap_object(m_file_path); - - ASSERT_NE(rw_whole, nullptr); - ASSERT_NE(rw_part, nullptr); - ASSERT_NE(ro, nullptr); - - EXPECT_EQ(rw_whole->get_id(), no_mapping_id); - EXPECT_EQ(rw_part->get_id(), no_mapping_id); - EXPECT_NE(ro->get_id(), no_mapping_id); -} - class HintEvictTest : public ::testing::Test { protected: std::filesystem::path m_file_path; From f12d420c7bef45e5f4e9fe2ae9877ecd04c6871c Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Tue, 11 Aug 2026 11:18:27 +0200 Subject: [PATCH 75/84] change api --- .../snippets/gpu/remote_objects_creation.cpp | 4 +- .../remote-tensor-api-gpu-plugin.rst | 11 +-- .../openvino/runtime/intel_gpu/ocl/ocl.hpp | 4 +- .../runtime/intel_gpu/remote_properties.hpp | 21 ++++- .../intel_gpu/plugin/remote_context.hpp | 2 +- .../intel_gpu/src/plugin/remote_context.cpp | 83 +++++++++---------- .../ocl_remote_tensor_tests.cpp | 28 ++----- 7 files changed, 76 insertions(+), 77 deletions(-) diff --git a/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp b/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp index 4d9520efc7e027..eb7fbc36b6f7b1 100644 --- a/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp +++ b/docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp @@ -73,7 +73,9 @@ int main() { //! [wrap_file] // The plugin memory-maps the file and keeps the mapping alive for the tensor lifetime, // so the file must not be modified until the returned tensor is destroyed. - ov::intel_gpu::FileDescriptor file_descriptor{"input.bin", /*offset_in_bytes=*/0}; + ov::intel_gpu::FileDescriptor file_descriptor{"input.bin", + /*offset_in_bytes=*/0, + ov::intel_gpu::FileAccess::READ}; auto remote_tensor = gpu_context.create_tensor(in_element_type, in_shape, file_descriptor); //! [wrap_file] } diff --git a/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst b/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst index 94c68cccdee6a0..e42171a4f0325e 100644 --- a/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst +++ b/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst @@ -270,11 +270,12 @@ For more details, see the code snippets below: .. tab-item:: file :sync: file - Use this overload to wrap tensor data stored in a file. A writable file is mapped read-write, - may be used as an inference output, and receives changes made through the tensor. A non-writable - file is mapped read-only and may only be used as an inference input. The plugin keeps the mapping - alive for the whole lifetime of the created remote tensor, so the file must not otherwise be - modified until the tensor is destroyed. + Use this overload to wrap tensor data stored in a file. The access mode is declared in the + descriptor: ``FileAccess::READ`` maps the file read-only and may only be used as an inference + input, while ``FileAccess::READ_WRITE`` requires a writable file, may be used as an inference + output, and makes changes done through the tensor visible in the file. The plugin keeps the + mapping alive for the whole lifetime of the created remote tensor, so the file must not + otherwise be modified until the tensor is destroyed. .. doxygensnippet:: docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp :language: cpp diff --git a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp index bb4d86ed5f5c93..4f99b035b345ce 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/ocl/ocl.hpp @@ -352,9 +352,11 @@ class ClContext : public RemoteContext { * so the file must not be modified until the returned tensor is destroyed. * @param type Tensor element type * @param shape Tensor shape - * @param file_descriptor Descriptor with the path and offset of the file containing tensor data. + * @param file_descriptor Descriptor with the path, offset and access mode of the file containing tensor data. * The offset must be a multiple of the system memory mapping alignment: the page size on Linux * (typically 4 KiB) and the allocation granularity on Windows (typically 64 KiB). + * FileAccess::READ_WRITE additionally requires the file to be writable by the calling process + * and makes the tensor writes visible in the file. * @return A remote tensor instance */ ClBufferTensor create_tensor(const element::Type type, const Shape& shape, const FileDescriptor& file_descriptor) { diff --git a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp index ba44261e50ed4b..048d02845769d5 100644 --- a/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp +++ b/src/inference/include/openvino/runtime/intel_gpu/remote_properties.hpp @@ -268,6 +268,15 @@ struct VirtualAddressMemory { int64_t size = -1; ///< Buffer size in bytes; -1 means "derive from tensor shape" }; +/** + * @brief Enum to define how a memory-mapped file is accessed by the plugin + * @ingroup ov_runtime_ocl_gpu_cpp_api + */ +enum class FileAccess { + READ = 0, //!< Tensor data is only read from the file + READ_WRITE = 1 //!< Tensor data is also written back to the file; requires a writable file +}; + /** * @brief File descriptor for wrapping tensor data memory-mapped from a file as a GPU plugin tensor. * The plugin memory-maps the file and keeps the mapping alive for the whole tensor lifetime, @@ -275,14 +284,18 @@ struct VirtualAddressMemory { * @ingroup ov_runtime_ocl_gpu_cpp_api */ struct FileDescriptor { // need to be merged with ov::intel_npu::FileDescriptor in future - explicit FileDescriptor(const std::filesystem::path& file_path, std::size_t offset_in_bytes = 0) + explicit FileDescriptor(const std::filesystem::path& file_path, + std::size_t offset_in_bytes = 0, + FileAccess file_access = FileAccess::READ) : path(file_path), - offset(offset_in_bytes) { + offset(offset_in_bytes), + access(file_access) { OPENVINO_ASSERT(!file_path.empty(), "[GPU] Provided file path is empty."); } - std::filesystem::path path; ///< File path - std::size_t offset = 0; ///< Offset in bytes to read from the file + std::filesystem::path path; ///< File path + std::size_t offset = 0; ///< Offset in bytes to read from the file + FileAccess access = FileAccess::READ; ///< Access mode of the mapping }; /** diff --git a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_context.hpp b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_context.hpp index 548cddf589dd16..1a1b9cf3403a98 100644 --- a/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_context.hpp +++ b/src/plugins/intel_gpu/include/intel_gpu/plugin/remote_context.hpp @@ -86,7 +86,7 @@ class RemoteContextImpl : public ov::IRemoteContext { std::shared_ptr reuse_memory(const ov::element::Type type, const ov::Shape& shape, cldnn::shared_handle mem, TensorType tensor_type); std::shared_ptr reuse_memory_from_cpu_va(const ov::element::Type type, const ov::Shape& shape, VirtualAddressMemory cpu_va, TensorType tensor_type); std::shared_ptr reuse_memory_from_handle(const ov::element::Type type, const ov::Shape& shape, SharedBufferHandle handle, TensorType tensor_type); - std::shared_ptr reuse_memory_from_file(const ov::element::Type type, const ov::Shape& shape, const std::filesystem::path& file_path, size_t offset); + std::shared_ptr reuse_memory_from_file(const ov::element::Type type, const ov::Shape& shape, const std::filesystem::path& file_path, size_t offset, ov::intel_gpu::FileAccess access); std::shared_ptr create_buffer(const ov::element::Type type, const ov::Shape& shape); std::shared_ptr create_usm(const ov::element::Type type, const ov::Shape& shape, TensorType alloc_type); void check_if_shared() const; diff --git a/src/plugins/intel_gpu/src/plugin/remote_context.cpp b/src/plugins/intel_gpu/src/plugin/remote_context.cpp index a4fc74c2a71d72..fdf2e9f1044151 100644 --- a/src/plugins/intel_gpu/src/plugin/remote_context.cpp +++ b/src/plugins/intel_gpu/src/plugin/remote_context.cpp @@ -13,7 +13,6 @@ #include "intel_gpu/runtime/itt.hpp" #include "intel_gpu/runtime/device_query.hpp" #include "intel_gpu/runtime/utils.hpp" -#include #include #ifdef _WIN32 @@ -46,13 +45,6 @@ size_t get_mmap_offset_alignment() { #endif } -// Permission bits (and Windows ACLs) describe what some account may do, not what this process may do, -// so writability is probed by opening the file for update with the current credentials. -bool is_file_writable(const std::filesystem::path& path) { - std::fstream file(path, std::ios::in | std::ios::out | std::ios::binary); - return file.is_open(); -} - ContextType get_default_context_type() { #ifdef OV_GPU_WITH_ZE_RT return ContextType::ZE; @@ -202,43 +194,44 @@ ov::SoPtr RemoteContextImpl::create_tensor(const ov::element: } if (ov::intel_gpu::SharedMemType::USM_DEVICE_BUFFER == mem_type) { return { create_usm(type, shape, TensorType::BT_USM_DEVICE_INTERNAL), nullptr }; - } else { - TensorType tensor_type; - cldnn::shared_handle mem = nullptr; - - if (ov::intel_gpu::SharedMemType::OCL_BUFFER == mem_type) { - tensor_type = TensorType::BT_BUF_SHARED; - mem = extract_object(params, ov::intel_gpu::mem_handle); - } else if (ov::intel_gpu::SharedMemType::USM_USER_BUFFER == mem_type) { - tensor_type = TensorType::BT_USM_SHARED; - mem = extract_object(params, ov::intel_gpu::mem_handle); - } else if (ov::intel_gpu::SharedMemType::CPU_VA == mem_type) { - tensor_type = TensorType::BT_CPU_VA; - mem = extract_object(params, ov::intel_gpu::cpu_va); - auto size = extract_object(params, ov::intel_gpu::cpu_va_size); - return { reuse_memory_from_cpu_va(type, shape, VirtualAddressMemory{mem, size}, tensor_type), nullptr }; - } else if (ov::intel_gpu::SharedMemType::MMAPED_FILE == mem_type) { - const auto fd = extract_object(params, ov::intel_gpu::file_descriptor); - return { reuse_memory_from_file(type, shape, fd.path, fd.offset), nullptr }; - } else if (ov::intel_gpu::SharedMemType::OCL_IMAGE2D == mem_type) { - tensor_type = TensorType::BT_IMG_SHARED; - mem = extract_object(params, ov::intel_gpu::mem_handle); + } + + TensorType tensor_type; + cldnn::shared_handle mem = nullptr; + + if (ov::intel_gpu::SharedMemType::OCL_BUFFER == mem_type) { + tensor_type = TensorType::BT_BUF_SHARED; + mem = extract_object(params, ov::intel_gpu::mem_handle); + } else if (ov::intel_gpu::SharedMemType::USM_USER_BUFFER == mem_type) { + tensor_type = TensorType::BT_USM_SHARED; + mem = extract_object(params, ov::intel_gpu::mem_handle); + } else if (ov::intel_gpu::SharedMemType::CPU_VA == mem_type) { + tensor_type = TensorType::BT_CPU_VA; + mem = extract_object(params, ov::intel_gpu::cpu_va); + auto size = extract_object(params, ov::intel_gpu::cpu_va_size); + return { reuse_memory_from_cpu_va(type, shape, VirtualAddressMemory{mem, size}, tensor_type), nullptr }; + } else if (ov::intel_gpu::SharedMemType::MMAPED_FILE == mem_type) { + const auto fd = extract_object(params, ov::intel_gpu::file_descriptor); + return { reuse_memory_from_file(type, shape, fd.path, fd.offset, fd.access), nullptr }; + } else if (ov::intel_gpu::SharedMemType::OCL_IMAGE2D == mem_type) { + tensor_type = TensorType::BT_IMG_SHARED; + mem = extract_object(params, ov::intel_gpu::mem_handle); #ifdef _WIN32 - } else if (ov::intel_gpu::SharedMemType::DX_BUFFER == mem_type) { - tensor_type = TensorType::BT_DX_BUF_SHARED; - mem = extract_object(params, ov::intel_gpu::dev_object_handle); - check_if_shared(); + } else if (ov::intel_gpu::SharedMemType::DX_BUFFER == mem_type) { + tensor_type = TensorType::BT_DX_BUF_SHARED; + mem = extract_object(params, ov::intel_gpu::dev_object_handle); + check_if_shared(); #endif - } else if (ov::intel_gpu::SharedMemType::BUFFER_FROM_HANDLE == mem_type) { - tensor_type = TensorType::BT_BUF_SHARED_FROM_HANDLE; - const auto os_handle = extract_object(params, ov::intel_gpu::os_handle); - SharedBufferHandle handle{os_handle}; - return { reuse_memory_from_handle(type, shape, handle, tensor_type), nullptr }; - } else { - OPENVINO_THROW("[GPU] Unsupported shared object type ", mem_type); - } + } else if (ov::intel_gpu::SharedMemType::BUFFER_FROM_HANDLE == mem_type) { + tensor_type = TensorType::BT_BUF_SHARED_FROM_HANDLE; + const auto os_handle = extract_object(params, ov::intel_gpu::os_handle); + SharedBufferHandle handle{os_handle}; + return { reuse_memory_from_handle(type, shape, handle, tensor_type), nullptr }; + } else { + OPENVINO_THROW("[GPU] Unsupported shared object type ", mem_type); + } - return { reuse_memory(type, shape, mem, tensor_type), nullptr }; + return { reuse_memory(type, shape, mem, tensor_type), nullptr }; } // For external contexts we try to match underlying handles with default contexts created by plugin to find device name @@ -307,7 +300,8 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_handle(c std::shared_ptr RemoteContextImpl::reuse_memory_from_file(const ov::element::Type type, const ov::Shape& shape, const std::filesystem::path& file_path, - size_t offset) { + size_t offset, + ov::intel_gpu::FileAccess access) { const auto byte_size = ov::util::get_memory_size_safe(type, shape); OPENVINO_ASSERT(byte_size, "[GPU] Cannot calculate memory size for element type ", type, " and shape ", shape); @@ -319,8 +313,7 @@ std::shared_ptr RemoteContextImpl::reuse_memory_from_file(con alignment); // Memory-map the file. The mapping is retained inside the RemoteTensorImpl so it stays // alive for the whole tensor lifetime (GPU wraps the host pointer via CL_MEM_USE_HOST_PTR). - // Writable files get a read-write mapping so the GPU can also write back through it. - const bool read_only = !is_file_writable(file_path); + const bool read_only = access == ov::intel_gpu::FileAccess::READ; auto mapped_memory = ov::load_mmap_object(file_path, offset, *byte_size, diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index cda3c9bf3fbf66..260c20246e182c 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -3112,9 +3112,6 @@ TEST(GpuRemoteTensorFromCpu, smoke_allocAlignedCPUMemory) { // the data and is varied to cover mmap allocation granularity boundaries. using MmapFileMemoryParams = std::tuple; -constexpr auto file_write_perms = std::filesystem::perms::owner_write | std::filesystem::perms::group_write | - std::filesystem::perms::others_write; - class GpuRemoteTensorFromFile : public ::testing::TestWithParam { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { @@ -3131,7 +3128,6 @@ class GpuRemoteTensorFromFile : public ::testing::TestWithParam(values.data()), values.size() * sizeof(float)); } - // The plugin picks a read-write mapping only for files it is allowed to write to. - static void set_file_writable(const std::filesystem::path& path, bool writable) { - std::filesystem::permissions( - path, - file_write_perms, - writable ? std::filesystem::perm_options::add : std::filesystem::perm_options::remove); - } - static std::vector make_values(std::size_t element_count) { std::vector values(element_count); for (std::size_t i = 0; i < element_count; ++i) { @@ -3176,8 +3164,6 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemoryAsInput) { const auto input_values = make_values(element_count); write_data_at_offset(m_file_path, offset, input_values); ASSERT_EQ(std::filesystem::file_size(m_file_path), offset + byte_size); - // A read-only file exercises the read-only mapping path. - set_file_writable(m_file_path, false); // Host pointers are imported in whole cachelines, so the destination buffer is padded accordingly. const std::size_t cacheline = core.get_property(target_device, ov::intel_gpu::cacheline_size); @@ -3186,8 +3172,10 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemoryAsInput) { std::fill_n(static_cast(output_ptr), output_buffer_size, 0); { - auto remote_input_tensor = - ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::FileDescriptor{m_file_path, offset}); + auto remote_input_tensor = ctx.create_tensor( + ov::element::f32, + shape, + ov::intel_gpu::FileDescriptor{m_file_path, offset, ov::intel_gpu::FileAccess::READ}); ASSERT_TRUE(remote_input_tensor.is()); auto remote_output_tensor = ctx.create_tensor(ov::element::f32, @@ -3221,12 +3209,12 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemoryAsOutput) { const auto input_values = make_values(element_count); write_data_at_offset(m_file_path, offset, std::vector(element_count, 0.0f)); ASSERT_EQ(std::filesystem::file_size(m_file_path), offset + byte_size); - // A writable file is mapped read-write, so the GPU can write inference results back through the mapping. - set_file_writable(m_file_path, true); { - auto remote_output_tensor = - ctx.create_tensor(ov::element::f32, shape, ov::intel_gpu::FileDescriptor{m_file_path, offset}); + auto remote_output_tensor = ctx.create_tensor( + ov::element::f32, + shape, + ov::intel_gpu::FileDescriptor{m_file_path, offset, ov::intel_gpu::FileAccess::READ_WRITE}); auto model = make_copy_model(shape); auto compiled = core.compile_model(model, ctx); From ff2d11a71bcccc2fcaade56e8c61200fab9926e2 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Tue, 11 Aug 2026 15:03:40 +0200 Subject: [PATCH 76/84] fix compilation --- .../functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index 260c20246e182c..f7886780f2044d 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -3234,7 +3234,7 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemoryAsOutput) { } } -std::vector generate_mmap_file_memory_params() { +static std::vector generate_mmap_file_memory_params() { #ifdef _WIN32 // Windows maps file views with 64K allocation granularity const std::size_t mmap_granularity = 65536; From aa3bc56963fd4624bd28851ee6fa4d7101df8927 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Tue, 11 Aug 2026 15:13:20 +0200 Subject: [PATCH 77/84] Revert "apply some of reviewer comments" This reverts commit 49ecdf3b6c5e82b03251ec3583a738857f2e2fba. --- .../util/include/openvino/util/mmap_object.hpp | 8 +++++++- src/common/util/src/os/lin/lin_mmap_object.cpp | 9 ++++++--- src/common/util/src/os/win/win_mmap_object.cpp | 4 ++++ src/core/tests/mmap_object.cpp | 15 ++++++++++++++- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/src/common/util/include/openvino/util/mmap_object.hpp b/src/common/util/include/openvino/util/mmap_object.hpp index 9db9931cf3e7fc..f162a56cd6a6fc 100644 --- a/src/common/util/include/openvino/util/mmap_object.hpp +++ b/src/common/util/include/openvino/util/mmap_object.hpp @@ -37,6 +37,11 @@ enum class MmapMode { READ_WRITE //!< Read-write mapping, modifications are written back to the file. }; +/** + * @brief Id reported by mappings whose content is mutable, so they must never be substituted for one another. + */ +inline constexpr uint64_t no_mapping_id = 0; + /** * @brief This class represents a mapped memory. * Instead of reading files, we can map the memory via mmap for Linux or MapViewOfFile for Windows. @@ -81,7 +86,8 @@ class MappedMemory { * @param no_placeholder When true, skip the Windows 10+ placeholder/VEH mechanism and use the legacy * single-call MapViewOfFile path instead. This guarantees a uniform AllocationBase * across the whole mapping, required for NPU zero-copy blob import. On Linux ignored. - * @param mode Access mode of the mapping. A read-write mapping requires the file to be writable. + * @param mode Access mode of the mapping. A read_write mapping requires the file to be writable and reports + * no_mapping_id, because get_id() marks an immutable data source that consumers may share. * @return MappedMemory shared ptr object which keep mmaped memory and control the lifetime. */ std::shared_ptr load_mmap_object(const std::filesystem::path& path, diff --git a/src/common/util/src/os/lin/lin_mmap_object.cpp b/src/common/util/src/os/lin/lin_mmap_object.cpp index decae78a54800a..b534e298bbd896 100644 --- a/src/common/util/src/os/lin/lin_mmap_object.cpp +++ b/src/common/util/src/os/lin/lin_mmap_object.cpp @@ -149,7 +149,7 @@ class MapHolder final : public MappedMemory { " for mapping. Ensure that file exists and has appropriate permissions."); } set_from_fd(fd, offset, size, mmap_mode); - m_id = util::get_id_for_file(path, offset, size); + m_id = (mmap_mode == MmapMode::READ_WRITE) ? no_mapping_id : util::get_id_for_file(path, offset, size); } void set_from_fd(const int fd, const size_t offset, const size_t size, const MmapMode mmap_mode = MmapMode::READ) { @@ -176,8 +176,11 @@ class MapHolder final : public MappedMemory { } m_data = static_cast(m_mapped_view) + gap; } - m_id = - util::u64_hash_combine(static_cast(sb.st_ino), {static_cast(sb.st_dev), offset, size}); + // A read-write mapping is not an immutable data source, so it must not be shared through id-based caches. + m_id = (mmap_mode == MmapMode::READ_WRITE) + ? no_mapping_id + : util::u64_hash_combine(static_cast(sb.st_ino), + {static_cast(sb.st_dev), offset, size}); } uint64_t get_id() const noexcept override { diff --git a/src/common/util/src/os/win/win_mmap_object.cpp b/src/common/util/src/os/win/win_mmap_object.cpp index d87702b774b0f7..63e812e1135231 100644 --- a/src/common/util/src/os/win/win_mmap_object.cpp +++ b/src/common/util/src/os/win/win_mmap_object.cpp @@ -651,6 +651,10 @@ void MapHolder::setup(HANDLE file_handle, size_t offset, size_t size, bool no_pl const size_t total_va_size = util::align_size_up(r_length, gran); set_id(file_handle, offset, size); + if (mode == MmapMode::READ_WRITE) { + // A read-write mapping is not an immutable data source, so it must not be shared through id-based caches. + m_id = no_mapping_id; + } if (m_size == 0) { return; diff --git a/src/core/tests/mmap_object.cpp b/src/core/tests/mmap_object.cpp index e4f129206b3012..9c03b87c32310e 100644 --- a/src/core/tests/mmap_object.cpp +++ b/src/core/tests/mmap_object.cpp @@ -270,7 +270,6 @@ TEST_F(ReadWriteMappingTest, writes_at_offset_leave_other_bytes_intact) { auto expected = m_content; std::fill_n(expected.begin() + k_offset, k_size, uint8_t{0x5A}); - ASSERT_NE(expected, m_content); { auto mm = load_mmap_object(m_file_path, k_offset, k_size, false, MmapMode::READ_WRITE); @@ -285,6 +284,20 @@ TEST_F(ReadWriteMappingTest, writes_at_offset_leave_other_bytes_intact) { EXPECT_THAT(read_file(), ElementsAreArray(expected)); } +TEST_F(ReadWriteMappingTest, read_write_mappings_report_no_mapping_id) { + auto rw_whole = load_mmap_object(m_file_path, 0, auto_size, false, MmapMode::READ_WRITE); + auto rw_part = load_mmap_object(m_file_path, 128, 256, false, MmapMode::READ_WRITE); + auto ro = load_mmap_object(m_file_path); + + ASSERT_NE(rw_whole, nullptr); + ASSERT_NE(rw_part, nullptr); + ASSERT_NE(ro, nullptr); + + EXPECT_EQ(rw_whole->get_id(), no_mapping_id); + EXPECT_EQ(rw_part->get_id(), no_mapping_id); + EXPECT_NE(ro->get_id(), no_mapping_id); +} + class HintEvictTest : public ::testing::Test { protected: std::filesystem::path m_file_path; From 569801b65e3843489ea671415d224c1418c7911d Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Tue, 11 Aug 2026 15:20:57 +0200 Subject: [PATCH 78/84] apply reviewer comment --- src/core/tests/mmap_object.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/core/tests/mmap_object.cpp b/src/core/tests/mmap_object.cpp index 9c03b87c32310e..f89b8d159fc324 100644 --- a/src/core/tests/mmap_object.cpp +++ b/src/core/tests/mmap_object.cpp @@ -270,15 +270,11 @@ TEST_F(ReadWriteMappingTest, writes_at_offset_leave_other_bytes_intact) { auto expected = m_content; std::fill_n(expected.begin() + k_offset, k_size, uint8_t{0x5A}); + ASSERT_NE(expected, m_content); { auto mm = load_mmap_object(m_file_path, k_offset, k_size, false, MmapMode::READ_WRITE); - ASSERT_NE(mm, nullptr); - ASSERT_EQ(mm->size(), k_size); - ASSERT_THAT(std::vector(m_content.begin() + k_offset, m_content.begin() + k_offset + k_size), - ElementsAreArray(reinterpret_cast(mm->data()), mm->size())); - - std::fill_n(reinterpret_cast(mm->data()), mm->size(), uint8_t{0x5A}); + std::fill_n(reinterpret_cast(mm->data()), k_size, uint8_t{0x5A}); } EXPECT_THAT(read_file(), ElementsAreArray(expected)); From ea39144ee57932d01767b247c242a2864294d76e Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Wed, 12 Aug 2026 10:45:00 +0200 Subject: [PATCH 79/84] apply reviewer comments --- src/common/util/src/os/win/win_mmap_object.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/common/util/src/os/win/win_mmap_object.cpp b/src/common/util/src/os/win/win_mmap_object.cpp index 63e812e1135231..83296d4b1933a9 100644 --- a/src/common/util/src/os/win/win_mmap_object.cpp +++ b/src/common/util/src/os/win/win_mmap_object.cpp @@ -338,7 +338,7 @@ class MapHolder : public ov::MappedMemory { void set_id(HANDLE h, size_t offset, size_t size); /** @brief Core setup shared by set() and set_from_handle(). */ - void setup(HANDLE file_handle, size_t offset, size_t size, bool no_placeholder, MmapMode mode = MmapMode::READ); + void setup(HANDLE file_handle, size_t offset, size_t size, bool no_placeholder, MmapMode mode); /** @brief Try to establish the placeholder mapping. * Returns true on success; caller falls back to legacy path on false. @@ -346,7 +346,7 @@ class MapHolder : public ov::MappedMemory { bool try_placeholder_setup(size_t aligned_offset, size_t head_pad, size_t total_va_size, size_t file_size); /** @brief Legacy single-call MapViewOfFile path (no partial-release support). */ - void legacy_setup(size_t aligned_offset, size_t head_pad, size_t size, MmapMode mode = MmapMode::READ); + void legacy_setup(size_t aligned_offset, size_t head_pad, size_t size, MmapMode mode); /** * @brief Computes the clamped, gran-aligned VA range to evict. @@ -715,7 +715,7 @@ void MapHolder::set_from_handle(FileHandle handle, size_t offset, size_t size) { throw std::runtime_error{"DuplicateHandle failed: " + std::to_string(::GetLastError())}; } HandleHolder owned{dup}; - setup(owned.get(), offset, size, false); + setup(owned.get(), offset, size, false, MmapMode::READ); // owned goes out of scope here: file handle closed. // m_handle (section object) keeps the file data accessible independently. } From 7c9af3104649682c2f1c95af4e8b77a7237ccda1 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Wed, 12 Aug 2026 11:12:50 +0200 Subject: [PATCH 80/84] added missing docs for cpu pointer (scope of PR 36539) --- .../gpu-device/remote-tensor-api-gpu-plugin.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst b/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst index e42171a4f0325e..def794430780e8 100644 --- a/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst +++ b/docs/articles_en/openvino-workflow/running-inference/inference-devices-and-modes/gpu-device/remote-tensor-api-gpu-plugin.rst @@ -233,6 +233,19 @@ For more details, see the code snippets below: :language: cpp :fragment: [wrap_usm_pointer] + .. tab-item:: CPU pointer + :sync: cpu-pointer + + Use this overload when your application owns a CPU virtual address, for example memory + allocated with ``ov::util::aligned_alloc`` or memory mapped from a file. On the OpenCL + backend, the pointer address and allocation size must be aligned to + ``ov::intel_gpu::cacheline_size``. The memory must remain valid for the whole lifetime of + the created remote tensor. + + .. doxygensnippet:: docs/articles_en/assets/snippets/gpu/remote_objects_creation.cpp + :language: cpp + :fragment: [wrap_cpu_pointer] + .. tab-item:: cl_mem :sync: cl-mem From adcecaf43914bc0951190c91441baef2fc24ff40 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Wed, 12 Aug 2026 12:13:16 +0200 Subject: [PATCH 81/84] edit test --- .../ocl_remote_tensor_tests.cpp | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp index f7886780f2044d..f9e3db17eaa1fd 100644 --- a/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp +++ b/src/plugins/intel_gpu/tests/functional/remote_tensor_tests/ocl_remote_tensor_tests.cpp @@ -3107,16 +3107,14 @@ TEST(GpuRemoteTensorFromCpu, smoke_allocAlignedCPUMemory) { ov::util::aligned_free(output_ptr); } -// : the tensor data starts at `offset` bytes into the file and spans `byte_size` bytes, so the -// file is `offset + byte_size` long. Both values are independent - `offset` only controls how much padding precedes -// the data and is varied to cover mmap allocation granularity boundaries. + using MmapFileMemoryParams = std::tuple; class GpuRemoteTensorFromFile : public ::testing::TestWithParam { public: static std::string getTestCaseName(const testing::TestParamInfo& obj) { - const auto& [offset, byte_size] = obj.param; - return "offset_" + std::to_string(offset) + "_bytes_" + std::to_string(byte_size); + const auto& [offset, bytes_after_offset] = obj.param; + return "offset_" + std::to_string(offset) + "_bytes_after_offset_" + std::to_string(bytes_after_offset); } protected: @@ -3152,22 +3150,20 @@ class GpuRemoteTensorFromFile : public ::testing::TestWithParam(); - // Store input data in a file at a page-aligned offset, so the resulting file size is offset + byte_size. const auto input_values = make_values(element_count); write_data_at_offset(m_file_path, offset, input_values); - ASSERT_EQ(std::filesystem::file_size(m_file_path), offset + byte_size); + ASSERT_EQ(std::filesystem::file_size(m_file_path), offset + bytes_after_offset); - // Host pointers are imported in whole cachelines, so the destination buffer is padded accordingly. const std::size_t cacheline = core.get_property(target_device, ov::intel_gpu::cacheline_size); - const std::size_t output_buffer_size = ((byte_size + cacheline - 1) / cacheline) * cacheline; + const std::size_t output_buffer_size = ((bytes_after_offset + cacheline - 1) / cacheline) * cacheline; void* output_ptr = ov::util::aligned_alloc(output_buffer_size, cacheline); std::fill_n(static_cast(output_ptr), output_buffer_size, 0); @@ -3198,17 +3194,17 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemoryAsInput) { } TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemoryAsOutput) { - const auto& [offset, byte_size] = GetParam(); + const auto& [offset, bytes_after_offset] = GetParam(); ov::Core core; std::string target_device = ov::test::utils::DEVICE_GPU; - const ov::Shape shape{byte_size / sizeof(float)}; + const ov::Shape shape{bytes_after_offset / sizeof(float)}; const size_t element_count = ov::shape_size(shape); auto ctx = core.get_default_context(target_device).as(); const auto input_values = make_values(element_count); write_data_at_offset(m_file_path, offset, std::vector(element_count, 0.0f)); - ASSERT_EQ(std::filesystem::file_size(m_file_path), offset + byte_size); + ASSERT_EQ(std::filesystem::file_size(m_file_path), offset + bytes_after_offset); { auto remote_output_tensor = ctx.create_tensor( @@ -3228,7 +3224,7 @@ TEST_P(GpuRemoteTensorFromFile, smoke_mmapFileMemoryAsOutput) { std::vector file_content(element_count, 0.0f); std::ifstream file(m_file_path, std::ios::binary); file.seekg(offset); - file.read(reinterpret_cast(file_content.data()), byte_size); + file.read(reinterpret_cast(file_content.data()), bytes_after_offset); for (size_t i = 0; i < element_count; ++i) { EXPECT_FLOAT_EQ(file_content[i], input_values[i]) << "Mismatch in file at index " << i; } @@ -3257,8 +3253,8 @@ static std::vector generate_mmap_file_memory_params() { std::vector params; params.reserve(layouts.size()); - for (const auto& [offset, byte_size] : layouts) { - params.emplace_back(offset, byte_size); + for (const auto& [offset, bytes_after_offset] : layouts) { + params.emplace_back(offset, bytes_after_offset); } return params; } From 57d397646d38bbb9ce25b7a1977128117d060f80 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Thu, 13 Aug 2026 14:19:39 +0200 Subject: [PATCH 82/84] delete unnecessary flag - FILE_MAP_WRITE is read write access --- src/common/util/src/os/win/win_mmap_object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/util/src/os/win/win_mmap_object.cpp b/src/common/util/src/os/win/win_mmap_object.cpp index 83296d4b1933a9..78724b2350cede 100644 --- a/src/common/util/src/os/win/win_mmap_object.cpp +++ b/src/common/util/src/os/win/win_mmap_object.cpp @@ -619,7 +619,7 @@ bool MapHolder::try_placeholder_setup(size_t aligned_offset, size_t head_pad, si } void MapHolder::legacy_setup(size_t aligned_offset, size_t head_pad, size_t size, MmapMode mode) { - const DWORD access = (mode == MmapMode::READ_WRITE) ? (FILE_MAP_READ | FILE_MAP_WRITE) : FILE_MAP_READ; + const DWORD access = (mode == MmapMode::READ_WRITE) ? FILE_MAP_WRITE : FILE_MAP_READ; if (auto view = ::MapViewOfFile(m_handle.get(), access, static_cast(aligned_offset >> 32), From 7ad7ea9a51f4bb3e2c597efec5ccd74fbac9ced7 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Thu, 13 Aug 2026 17:25:55 +0200 Subject: [PATCH 83/84] better comment, better mode --- src/common/util/src/os/win/win_mmap_object.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/common/util/src/os/win/win_mmap_object.cpp b/src/common/util/src/os/win/win_mmap_object.cpp index 78724b2350cede..61a5e20f5a8bda 100644 --- a/src/common/util/src/os/win/win_mmap_object.cpp +++ b/src/common/util/src/os/win/win_mmap_object.cpp @@ -619,7 +619,7 @@ bool MapHolder::try_placeholder_setup(size_t aligned_offset, size_t head_pad, si } void MapHolder::legacy_setup(size_t aligned_offset, size_t head_pad, size_t size, MmapMode mode) { - const DWORD access = (mode == MmapMode::READ_WRITE) ? FILE_MAP_WRITE : FILE_MAP_READ; + const DWORD access = (mode == MmapMode::READ_WRITE) ? FILE_MAP_ALL_ACCESS : FILE_MAP_READ; if (auto view = ::MapViewOfFile(m_handle.get(), access, static_cast(aligned_offset >> 32), @@ -668,8 +668,7 @@ void MapHolder::setup(HANDLE file_handle, size_t offset, size_t size, bool no_pl // When no_placeholder is set, skip the placeholder/VEH path to guarantee a single uniform AllocationBase // (required for NPU zero-copy blob import). Otherwise prefer placeholder for RSS reduction. - // Read-write mappings always take the legacy path: the VEH only remaps on read faults, so an evicted - // granule hit by a write would fault indefinitely. + // RW mappings are ignored by the current VEH registration: the handler only remaps read faults. if (no_placeholder || mode == MmapMode::READ_WRITE || !try_placeholder_setup(m_aligned_offset, head_pad, total_va_size, file_size)) { legacy_setup(m_aligned_offset, head_pad, m_size, mode); From 21a8abee0f31d121e612696054f5debdb001fea6 Mon Sep 17 00:00:00 2001 From: Michal Miotk Date: Thu, 13 Aug 2026 18:16:08 +0200 Subject: [PATCH 84/84] enable file share delete for rw variant --- src/common/util/src/os/win/win_mmap_object.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/util/src/os/win/win_mmap_object.cpp b/src/common/util/src/os/win/win_mmap_object.cpp index 61a5e20f5a8bda..5043dd62fec4ba 100644 --- a/src/common/util/src/os/win/win_mmap_object.cpp +++ b/src/common/util/src/os/win/win_mmap_object.cpp @@ -679,7 +679,7 @@ void MapHolder::set(const std::filesystem::path& path, size_t offset, size_t siz const bool writable = mode == MmapMode::READ_WRITE; auto fh = ::CreateFileW(path.c_str(), writable ? (GENERIC_READ | GENERIC_WRITE) : GENERIC_READ, - writable ? (FILE_SHARE_READ | FILE_SHARE_WRITE) : (FILE_SHARE_READ | FILE_SHARE_DELETE), + writable ? (FILE_SHARE_READ | FILE_SHARE_WRITE| FILE_SHARE_DELETE) : (FILE_SHARE_READ | FILE_SHARE_DELETE), nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,