Skip to content

Commit 2be36d3

Browse files
committed
remove Runtimecontent from DynamicArguments and split memref and ovshape from infer and dynmaic
1 parent 717e9d1 commit 2be36d3

4 files changed

Lines changed: 200 additions & 182 deletions

File tree

src/plugins/intel_npu/src/backend/include/zero_dynamic_infer_request.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,16 @@ class ZeroDynamicInferRequest final : public ZeroInferRequest {
3232
const std::vector<ov::SoPtr<ov::ITensor>>& tensors,
3333
const std::optional<size_t>& batchSize = std::nullopt) override;
3434

35-
void predict_output_shapes(std::vector<MemRefType>& outputMemRef);
36-
void check_tensor_and_predicted_shapes(const std::vector<MemRefType>& outputMemRef);
35+
void predict_output_shapes(std::vector<ov::Shape>& predictedShapes);
36+
void check_tensor_and_predicted_shapes(const std::vector<ov::Shape>& predictedShapes);
3737

38-
void update_tensor(const std::vector<MemRefType>& outputMemRef);
38+
void update_tensor(const std::vector<ov::Shape>& predictedShapes);
3939

4040
bool _isTensorChanged = false;
4141

4242
private:
43-
// Store the vm context before predicted output shapes
44-
std::shared_ptr<DynamicArguments> _arguments;
43+
// VM execution context shared with the pipeline; created lazily, reused across inferences.
44+
std::shared_ptr<VMExecutionContext> _executionContext = std::make_shared<VMExecutionContext>();
4545
};
4646

4747
} // namespace intel_npu

src/plugins/intel_npu/src/backend/include/zero_dynamic_pipeline.hpp

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,36 @@
1010

1111
namespace intel_npu {
1212

13+
// VM runtime execution context owner. The handle is created lazily and shared between
14+
// shape prediction (infer request) and execution (pipeline) so the same context is reused.
15+
struct VMExecutionContext {
16+
npu_vm_runtime_execution_context_handle_t _handle = nullptr;
17+
18+
VMExecutionContext() = default;
19+
VMExecutionContext(const VMExecutionContext&) = delete;
20+
VMExecutionContext& operator=(const VMExecutionContext&) = delete;
21+
VMExecutionContext(VMExecutionContext&&) = delete;
22+
VMExecutionContext& operator=(VMExecutionContext&&) = delete;
23+
~VMExecutionContext();
24+
25+
// Create the context for vmRuntime if not created yet; returns the handle.
26+
npu_vm_runtime_execution_context_handle_t ensure(npu_vm_runtime_handle_t vmRuntime);
27+
};
28+
1329
struct DynamicArguments {
1430
std::vector<MemRefType> _inputsMemRef;
1531
std::vector<MemRefType> _outputsMemRef;
1632

17-
// Save the memref handle to a vector for VM execution and prediction to extend its lifetime.
18-
std::vector<npu_vm_runtime_mem_ref_handle_t> _inputMemRefHandles;
19-
std::vector<npu_vm_runtime_mem_ref_handle_t> _outputMemRefHandles;
20-
21-
// Share runtime_execution_context in param during VM execution and forecasting
22-
npu_vm_runtime_execute_params_t _executeParams = {};
33+
// True once the command lists have been recorded by a first npuVMRuntimeExecute call. Subsequent
34+
// executions can be replayed without re-recording when no tensor changed (see execute_vm_runtime).
35+
bool _commandListsRecorded = false;
2336

2437
DynamicArguments() = default;
2538
DynamicArguments(const DynamicArguments&) = delete;
2639
DynamicArguments& operator=(const DynamicArguments&) = delete;
2740
DynamicArguments(DynamicArguments&&) = delete;
2841
DynamicArguments& operator=(DynamicArguments&&) = delete;
29-
~DynamicArguments();
30-
31-
// Create the VM execution context for vmRuntime. No-op if already created.
32-
void ensureExecutionContext(npu_vm_runtime_handle_t vmRuntime);
42+
~DynamicArguments() = default;
3343

3444
void setArgumentProperties(uint32_t argi,
3545
const void* argv,
@@ -45,9 +55,7 @@ class DynamicPipeline final : public IPipeline {
4555
// Store command list handles to pass it to ExecutionEngine
4656
std::vector<ze_command_list_handle_t> _commandListHandles;
4757

48-
PipelinedCommandLists(size_t numCommandLists,
49-
const std::shared_ptr<ZeroInitStructsHolder>& init_structs,
50-
std::shared_ptr<DynamicArguments> requestArguments) {
58+
PipelinedCommandLists(size_t numCommandLists, const std::shared_ptr<ZeroInitStructsHolder>& init_structs) {
5159
_commandLists.reserve(numCommandLists);
5260
for (size_t i = 0; i < numCommandLists; i++) {
5361
_commandLists.emplace_back(std::make_unique<CommandList>(init_structs));
@@ -57,11 +65,7 @@ class DynamicPipeline final : public IPipeline {
5765
_commandListHandles.push_back(_commandLists[i]->handle());
5866
}
5967

60-
if (requestArguments != nullptr) {
61-
_arguments = requestArguments;
62-
} else {
63-
_arguments = std::make_shared<DynamicArguments>();
64-
}
68+
_arguments = std::make_shared<DynamicArguments>();
6569
}
6670

6771
size_t size() const {
@@ -137,7 +141,7 @@ class DynamicPipeline final : public IPipeline {
137141
const Config& config,
138142
const std::vector<std::vector<std::shared_ptr<ZeroTensor>>>& input_tensors,
139143
const std::vector<std::shared_ptr<ZeroTensor>>& output_tensors,
140-
std::shared_ptr<DynamicArguments> requestArguments,
144+
std::shared_ptr<VMExecutionContext> executionContext,
141145
size_t batch_size = 1);
142146

143147
DynamicPipeline(const DynamicPipeline&) = delete;
@@ -155,12 +159,13 @@ class DynamicPipeline final : public IPipeline {
155159
size_t batch_index,
156160
const std::shared_ptr<ov::ITensor>& userTensor = nullptr) override;
157161

158-
// Predicts VM runtime output shape. Independent of pipeline instance state, this depends only on the VM runtime
162+
// Predicts VM runtime output shapes for the given input/output tensors. A nullptr tensor entry falls
163+
// back to the graph metadata max shape. Independent of pipeline instance state, this depends only on the VM runtime
159164
// handle and argument-provided context, making it a static method.
160-
static void predict_output_shape(const IGraph& graph,
161-
DynamicArguments& args,
162-
std::vector<MemRefType>& inputsMemRef,
163-
std::vector<MemRefType>& outputsMemRef);
165+
static std::vector<ov::Shape> predict_output_shapes(const IGraph& graph,
166+
VMExecutionContext& executionContext,
167+
const std::vector<std::shared_ptr<ov::ITensor>>& inputTensors,
168+
const std::vector<std::shared_ptr<ov::ITensor>>& outputTensors);
164169

165170
private:
166171
void execute_vm_runtime(npu_vm_runtime_handle_t vmRuntime,
@@ -170,6 +175,8 @@ class DynamicPipeline final : public IPipeline {
170175
ze_fence_handle_t fence,
171176
ze_event_handle_t event);
172177

178+
// Shared VM execution context (owned by the infer request), reused across all command lists.
179+
std::shared_ptr<VMExecutionContext> _executionContext;
173180
std::vector<std::unique_ptr<PipelinedCommandLists>> _command_lists;
174181
};
175182

src/plugins/intel_npu/src/backend/src/zero_dynamic_infer_request.cpp

Lines changed: 32 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ void ZeroDynamicInferRequest::create_pipeline_impl() {
2828
_config,
2929
_levelZeroInputTensors,
3030
_levelZeroOutputTensors,
31-
_arguments,
31+
_executionContext,
3232
batchSize.has_value() ? batchSize.value() : utils::DEFAULT_BATCH_SIZE);
3333

3434
_logger.debug("create_pipeline_impl - completed");
@@ -203,102 +203,61 @@ void ZeroDynamicInferRequest::infer_async() {
203203
_logger.debug("infer_async - started");
204204
OV_ITT_TASK_CHAIN(ZERO_INFER, itt::domains::LevelZeroBackend, "infer_async", "start");
205205
// Store the predicted output shapes
206-
std::vector<MemRefType> outputsMemRef;
207-
predict_output_shapes(outputsMemRef);
208-
check_tensor_and_predicted_shapes(outputsMemRef);
206+
std::vector<ov::Shape> predictedShapes;
207+
predict_output_shapes(predictedShapes);
208+
check_tensor_and_predicted_shapes(predictedShapes);
209209
prepare_inputs();
210210
prepare_outputs();
211-
update_tensor(outputsMemRef);
211+
update_tensor(predictedShapes);
212212

213213
OV_ITT_TASK_NEXT(ZERO_INFER, "push");
214214
_pipeline->push();
215215
}
216216

217-
void ZeroDynamicInferRequest::predict_output_shapes(std::vector<MemRefType>& outputsMemRef) {
217+
void ZeroDynamicInferRequest::predict_output_shapes(std::vector<ov::Shape>& predictedShapes) {
218218
// TODO: If current output tensor is not large enough to be compatible with input tensor, need recreate pipeline
219219
// But reshape ZeroTensor can be used to avoid recreate pipeline now
220-
// bool reCreatePipeline = false;
221-
// Predict output shapes based on current inputs
222220

223-
if (_arguments == nullptr) {
224-
_arguments = std::make_shared<DynamicArguments>();
225-
}
221+
// Predict output shapes based on current inputs. The infer request only deals with tensors/OV shapes;
222+
// MemRef packing is done inside the pipeline layer.
223+
predictedShapes.clear();
226224

227225
if (_graph->get_handle() != nullptr && _isTensorChanged) {
228-
std::vector<MemRefType> inputsMemRef(_metadata.inputs.size());
229-
outputsMemRef.clear();
230-
outputsMemRef.resize(_metadata.outputs.size());
226+
std::vector<std::shared_ptr<ov::ITensor>> inputTensors(_metadata.inputs.size());
227+
std::vector<std::shared_ptr<ov::ITensor>> outputTensors(_metadata.outputs.size());
231228

232229
// TODO: Support Batch later
233230
// Update input Info
234-
// TENTATIVE CODE TO ALLOCATE a memref handle
235-
for (size_t i = 0; i < inputsMemRef.size(); ++i) {
236-
auto& levelZeroTensor = get_level_zero_input(i);
237-
auto& userTensor = get_user_input(i);
231+
// A null entry lets the pipeline fall back to the graph metadata max shape.
232+
for (size_t i = 0; i < inputTensors.size(); ++i) {
233+
const auto& userTensor = get_user_input(i);
238234
if (userTensor != nullptr) {
239-
// If userTensor is set, use userTensor to update memref handle
240-
const auto userTensorPtr = userTensor._ptr;
241-
OPENVINO_ASSERT(userTensorPtr != nullptr, "Input user tensor pointer is null");
242-
inputsMemRef[i].set(get_tensor_data_ptr(userTensorPtr), 0, userTensorPtr);
243-
} else if (levelZeroTensor != nullptr) {
244-
// If userTensor is not set, use levelZeroTensor to update memref handle
245-
inputsMemRef[i].set(get_tensor_data_ptr(levelZeroTensor), 0, levelZeroTensor);
235+
// If userTensor is set, use userTensor to update memref handle in prediction
236+
inputTensors[i] = userTensor._ptr;
246237
} else {
247-
// If all tensors are not set, use metadata
248-
inputsMemRef[i].setArg(nullptr);
249-
inputsMemRef[i]._offset = 0;
250-
// TODO : BatchSize not checked here
251-
inputsMemRef[i].setSize(_metadata.inputs.at(i).shapeFromCompiler.get_max_shape());
252-
inputsMemRef[i].updateStride();
238+
// If userTensor is not set, use levelZeroTensor
239+
inputTensors[i] = get_level_zero_input(i);
253240
}
254241
}
255-
256242
// Update output Info
257-
for (size_t i = 0; i < outputsMemRef.size(); ++i) {
258-
auto& levelZeroTensor = _levelZeroOutputTensors.at(i);
259-
auto& userTensor = _userOutputTensors.at(i);
243+
for (size_t i = 0; i < outputTensors.size(); ++i) {
244+
const auto& userTensor = _userOutputTensors.at(i);
260245
if (userTensor != nullptr) {
261-
// If userTensor is set, use userTensor to update memref handle
262-
const auto userTensorPtr = userTensor._ptr;
263-
OPENVINO_ASSERT(userTensorPtr != nullptr, "Output user tensor pointer is null");
264-
outputsMemRef[i].set(get_tensor_data_ptr(userTensorPtr), 0, userTensorPtr);
265-
} else if (levelZeroTensor != nullptr) {
266-
// If userTensor is not set, use levelZeroTensor to update memref handle
267-
outputsMemRef[i].set(get_tensor_data_ptr(levelZeroTensor), 0, levelZeroTensor);
246+
// If userTensor is set, use userTensor to update memref handle in prediction
247+
outputTensors[i] = userTensor._ptr;
268248
} else {
269-
// If all tensors are not set, use metadata
270-
outputsMemRef[i].setArg(nullptr);
271-
outputsMemRef[i]._offset = 0;
272-
// TODO : BatchSize not checked here
273-
outputsMemRef[i].setSize(_metadata.outputs.at(i).shapeFromCompiler.get_max_shape());
274-
outputsMemRef[i].updateStride();
249+
// If userTensor is not set, use levelZeroTensor
250+
outputTensors[i] = _levelZeroOutputTensors.at(i);
275251
}
276252
}
277253

278-
std::vector<MemRefType> originalOutputMemRef;
279-
originalOutputMemRef.resize(outputsMemRef.size());
280-
281-
for (size_t i = 0; i < outputsMemRef.size(); ++i) {
282-
originalOutputMemRef[i]._dimsCount = outputsMemRef[i]._dimsCount;
283-
originalOutputMemRef[i]._sizes = outputsMemRef[i]._sizes;
284-
originalOutputMemRef[i]._strides = outputsMemRef[i]._strides;
285-
}
286-
287-
// Get VM context before invoking VM shape prediction.
288-
DynamicArguments& dynamicArguments = *_arguments;
289-
DynamicPipeline::predict_output_shape(*_graph, dynamicArguments, inputsMemRef, outputsMemRef);
290-
291-
for (size_t i = 0; i < outputsMemRef.size(); i++) {
292-
if (!originalOutputMemRef[i].compare(outputsMemRef[i])) {
293-
_logger.debug("predict_shapes - output shape change detected");
294-
break;
295-
}
296-
}
254+
predictedShapes =
255+
DynamicPipeline::predict_output_shapes(*_graph, *_executionContext, inputTensors, outputTensors);
297256
}
298257
}
299258

300-
void ZeroDynamicInferRequest::check_tensor_and_predicted_shapes(const std::vector<MemRefType>& outputsMemRef) {
301-
if (outputsMemRef.empty()) {
259+
void ZeroDynamicInferRequest::check_tensor_and_predicted_shapes(const std::vector<ov::Shape>& predictedShapes) {
260+
if (predictedShapes.empty()) {
302261
_logger.debug("check_tensor_and_predicted_shapes - no output props to check, skip check");
303262
return;
304263
}
@@ -316,10 +275,7 @@ void ZeroDynamicInferRequest::check_tensor_and_predicted_shapes(const std::vecto
316275
continue;
317276
}
318277

319-
ov::Shape predictedShape;
320-
for (int64_t j = 0; j < outputsMemRef[i]._dimsCount; j++) {
321-
predictedShape.push_back(outputsMemRef[i]._sizes[j]);
322-
}
278+
const ov::Shape& predictedShape = predictedShapes[i];
323279
if (userTensor != nullptr) {
324280
// User set output tensor, need check size and throw exception if not large enough
325281
if (shape_size(userTensor->get_shape()) < shape_size(predictedShape)) {
@@ -345,19 +301,16 @@ void ZeroDynamicInferRequest::check_tensor_and_predicted_shapes(const std::vecto
345301
}
346302
}
347303

348-
void ZeroDynamicInferRequest::update_tensor(const std::vector<MemRefType>& outputsMemRef) {
304+
void ZeroDynamicInferRequest::update_tensor(const std::vector<ov::Shape>& predictedShapes) {
349305
// Update local level zero buffer shape with predicted shape to prepare for comparasion
350-
if (outputsMemRef.size() > 0 && _isTensorChanged) {
306+
if (predictedShapes.size() > 0 && _isTensorChanged) {
351307
for (size_t i = 0; i < _levelZeroOutputTensors.size(); i++) {
352308
auto& levelZeroTensor = _levelZeroOutputTensors.at(i);
353309
if (levelZeroTensor == nullptr) {
354310
// Do not need to update user output tensor
355311
continue;
356312
}
357-
ov::Shape predictedShape;
358-
for (int64_t j = 0; j < outputsMemRef[i]._dimsCount; j++) {
359-
predictedShape.push_back(outputsMemRef[i]._sizes[j]);
360-
}
313+
const ov::Shape& predictedShape = predictedShapes[i];
361314
if (levelZeroTensor->get_shape() != predictedShape) {
362315
_logger.info("update_tensor - reshape output tensor %d from %s to predicted shape %s",
363316
i,

0 commit comments

Comments
 (0)