From 4fc0b22012539c1261949064d1ec3bf3f0819649 Mon Sep 17 00:00:00 2001 From: Jan Iwaszkiewicz Date: Wed, 28 Jun 2023 07:56:29 +0200 Subject: [PATCH] [PyOV] Force copy of not writable numpy arrays (#18194) --- .../python/src/openvino/runtime/ie_api.py | 8 ++--- .../utils/data_helpers/data_dispatcher.py | 5 ++++ .../tests/test_runtime/test_infer_request.py | 30 +++++++++++++++++++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/bindings/python/src/openvino/runtime/ie_api.py b/src/bindings/python/src/openvino/runtime/ie_api.py index 9e0161daebf..56b0b8fa254 100644 --- a/src/bindings/python/src/openvino/runtime/ie_api.py +++ b/src/bindings/python/src/openvino/runtime/ie_api.py @@ -58,7 +58,7 @@ class InferRequest(_InferRequestWrapper): Tensors for every input in form of: * `numpy.ndarray` and all the types that are castable to it, e.g. `torch.Tensor` Data that is going to be copied: - * `numpy.ndarray` which are not C contiguous + * `numpy.ndarray` which are not C contiguous and/or not writable (WRITEABLE flag is set to False) * inputs which data types are mismatched from Infer Request's inputs * inputs that should be in `BF16` data type * scalar inputs (i.e. `np.float_`/`int`/`float`) @@ -118,7 +118,7 @@ class InferRequest(_InferRequestWrapper): Tensors for every input in form of: * `numpy.ndarray` and all the types that are castable to it, e.g. `torch.Tensor` Data that is going to be copied: - * `numpy.ndarray` which are not C contiguous + * `numpy.ndarray` which are not C contiguous and/or not writable (WRITEABLE flag is set to False) * inputs which data types are mismatched from Infer Request's inputs * inputs that should be in `BF16` data type * scalar inputs (i.e. `np.float_`/`int`/`float`) @@ -246,7 +246,7 @@ class CompiledModel(CompiledModelBase): Tensors for every input in form of: * `numpy.ndarray` and all the types that are castable to it, e.g. `torch.Tensor` Data that is going to be copied: - * `numpy.ndarray` which are not C contiguous + * `numpy.ndarray` which are not C contiguous and/or not writable (WRITEABLE flag is set to False) * inputs which data types are mismatched from Infer Request's inputs * inputs that should be in `BF16` data type * scalar inputs (i.e. `np.float_`/`int`/`float`) @@ -340,7 +340,7 @@ class AsyncInferQueue(AsyncInferQueueBase): Tensors for every input in form of: * `numpy.ndarray` and all the types that are castable to it, e.g. `torch.Tensor` Data that is going to be copied: - * `numpy.ndarray` which are not C contiguous + * `numpy.ndarray` which are not C contiguous and/or not writable (WRITEABLE flag is set to False) * inputs which data types are mismatched from Infer Request's inputs * inputs that should be in `BF16` data type * scalar inputs (i.e. `np.float_`/`int`/`float`) diff --git a/src/bindings/python/src/openvino/runtime/utils/data_helpers/data_dispatcher.py b/src/bindings/python/src/openvino/runtime/utils/data_helpers/data_dispatcher.py index 62beadea9d3..683b4bf4efb 100644 --- a/src/bindings/python/src/openvino/runtime/utils/data_helpers/data_dispatcher.py +++ b/src/bindings/python/src/openvino/runtime/utils/data_helpers/data_dispatcher.py @@ -70,6 +70,11 @@ def _( tensor = Tensor(tensor_type, value.shape) tensor.data[:] = value.view(tensor_dtype) return tensor + # WA for "not writeable" edge-case, always copy. + if value.flags["WRITEABLE"] is False: + tensor = Tensor(tensor_type, value.shape) + tensor.data[:] = value.astype(tensor_dtype) if tensor_dtype != value.dtype else value + return tensor # If types are mismatched, convert and always copy. if tensor_dtype != value.dtype: return Tensor(value.astype(tensor_dtype), shared_memory=False) diff --git a/src/bindings/python/tests/test_runtime/test_infer_request.py b/src/bindings/python/tests/test_runtime/test_infer_request.py index 4f730a0bc77..0159b123ad8 100644 --- a/src/bindings/python/tests/test_runtime/test_infer_request.py +++ b/src/bindings/python/tests/test_runtime/test_infer_request.py @@ -1112,3 +1112,33 @@ def test_mixed_dynamic_infer(device, shared_flag, input_data): else: assert not np.shares_memory(input_data[0], input_tensor0.data) assert not np.shares_memory(input_data[1], input_tensor1.data) + + +@pytest.mark.parametrize("shared_flag", [True, False]) +@pytest.mark.parametrize(("input_data", "change_flags"), [ + ({0: np.frombuffer(b"\x01\x02\x03\x04", np.uint8)}, False), + ({0: np.array([1, 2, 3, 4], dtype=np.uint8)}, True), +]) +def test_not_writable_inputs_infer(device, shared_flag, input_data, change_flags): + if change_flags is True: + input_data[0].setflags(write=0) + # identity model + input_shape = [4] + param_node = ops.parameter(input_shape, np.uint8, name="data0") + core = Core() + model = Model(param_node, [param_node]) + compiled = core.compile_model(model, "CPU") + + results = compiled(input_data, shared_memory=shared_flag) + + assert np.array_equal(results[0], input_data[0]) + + request = compiled.create_infer_request() + results = request.infer(input_data, shared_memory=shared_flag) + + assert np.array_equal(results[0], input_data[0]) + + input_tensor = request.get_input_tensor(0) + + # Not writable inputs should always be copied. + assert not np.shares_memory(input_data[0], input_tensor.data)