Skip to content

Commit e0779f2

Browse files
authored
CVS-176847 Reshape Input to apply to OVIR models as well (#864)
To prevent recompilation of epctx models, a check was introduced to prevent the provider option 'reshape_input' from being used with epctx graphs. However, this also prevents OVIR models from compiling with the reshape input. This PR modifies the check on epctx graphs to allow reshape for OVIR models and also adds a method to reshape it within the ImportEPCtxOVIREncapsulation() function. Test: onnxruntime_perf_test -m times -r 1 -e openvino -v -C "ep.context_enable|1 ep.context_file_path|ov_encoder_30_40_ctx.onnx" -i "device_type|NPU reshape_input|input_features[30..40,80,3000]" "openvino_encoder_model.onnx" This should fail if input data>40 is provided. Also removed redundant validation and checks on the OVEP side, to let OV handle reshape validation completely.
1 parent fa8f464 commit e0779f2

File tree

6 files changed

+14
-102
lines changed

6 files changed

+14
-102
lines changed

onnxruntime/core/providers/openvino/backend_manager.cc

Lines changed: 2 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ BackendManager::BackendManager(SessionContext& session_context,
9696
ptr_stream_t model_stream;
9797
std::unique_ptr<onnx::ModelProto> model_proto;
9898
if (subgraph_context_.is_ep_ctx_graph) {
99-
if (!session_context_.reshape.empty()) {
99+
if (!session_context_.reshape.empty() && !subgraph_context_.is_ep_ctx_ovir_encapsulated) {
100100
std::string exception_str =
101101
"[OpenVINO-EP] Bounded dynamic model execution using provider option reshape_input is not supported for OVEP EPContext model";
102102
ORT_THROW(exception_str);
@@ -231,21 +231,8 @@ bool BackendManager::ModelHasBatchedInputs(const ONNX_NAMESPACE::ModelProto& mod
231231
bool BackendManager::ModelHasSymbolicInputDims(const onnxruntime::GraphViewer& subgraph) const {
232232
const auto& graph_inputs = subgraph.GetInputs();
233233

234-
// First validate shapes if provided by user
235-
bool shapes_valid = true;
236-
if (!session_context_.reshape.empty()) {
237-
try {
238-
ValidateInputShapes(session_context_.reshape, graph_inputs);
239-
} catch (const std::exception& e) {
240-
LOGS_DEFAULT(ERROR) << "[OpenVINO-EP] Shape validation failed: " << e.what();
241-
session_context_.reshape.clear(); // Clear the shape map as it's invalid
242-
shapes_valid = false;
243-
}
244-
}
245-
246234
// Count dynamic inputs and check if reshape covers all of them
247235
size_t dynamic_input_count = 0;
248-
bool all_dynamic_inputs_covered = true;
249236

250237
for (const auto* input : graph_inputs) {
251238
// Skip dangling inputs (no consumers)
@@ -273,14 +260,6 @@ bool BackendManager::ModelHasSymbolicInputDims(const onnxruntime::GraphViewer& s
273260
// If dynamic, count it and check if reshape covers it
274261
if (has_dynamic_dim) {
275262
dynamic_input_count++;
276-
277-
// Check if this dynamic input is covered by reshape input
278-
if (!session_context_.reshape.empty() &&
279-
session_context_.reshape.find(input->Name()) == session_context_.reshape.end()) {
280-
all_dynamic_inputs_covered = false;
281-
LOGS_DEFAULT(WARNING) << "[OpenVINO-EP] reshape_input is provided but doesn't cover dynamic input: "
282-
<< input->Name();
283-
}
284263
}
285264
}
286265

@@ -290,22 +269,8 @@ bool BackendManager::ModelHasSymbolicInputDims(const onnxruntime::GraphViewer& s
290269
if (session_context_.reshape.empty()) {
291270
return has_symbolic_dims; // Return based on whether model has symbolic dims
292271
}
272+
else return false;
293273

294-
// For dynamic models with incomplete reshape coverage, clear shapes
295-
if (has_symbolic_dims && !all_dynamic_inputs_covered) {
296-
session_context_.reshape.clear();
297-
LOGS_DEFAULT(WARNING) << "reshape_input does not cover all dynamic dimensions, "
298-
<< "ignoring all provided shapes";
299-
return true; // Model is dynamic
300-
}
301-
302-
// If shapes are valid with complete coverage for dynamic model, treat as concrete
303-
if (has_symbolic_dims && shapes_valid && all_dynamic_inputs_covered) {
304-
LOGS_DEFAULT(INFO) << "All dynamic dimensions successfully covered by reshape_input";
305-
return false; // Model is now effectively static with concrete shapes
306-
}
307-
308-
return has_symbolic_dims; // Return dynamic status based on symbolic dimensions
309274
}
310275

311276
// Check to see if the graph is QDQ
@@ -690,40 +655,6 @@ BackendManager::ReWriteBatchDimWithOne(const ONNX_NAMESPACE::ModelProto& model_p
690655
return model_copy;
691656
}
692657

693-
void BackendManager::ValidateInputShapes(const reshape_t& shapes,
694-
const std::vector<const NodeArg*>& graph_inputs) const {
695-
for (const auto& [tensor_name, requested_shape] : shapes) {
696-
// Find matching input in graph
697-
const NodeArg* graph_input = nullptr;
698-
for (const auto* input : graph_inputs) {
699-
if (input->Name() == tensor_name) {
700-
graph_input = input;
701-
break;
702-
}
703-
}
704-
705-
if (!graph_input) {
706-
ORT_THROW("Input '" + tensor_name + "' specified in reshape_input does not exist in the graph");
707-
}
708-
709-
const ONNX_NAMESPACE::TensorShapeProto* graph_shape = graph_input->Shape();
710-
if (!graph_shape) {
711-
ORT_THROW("Graph input '" + tensor_name + "' has no shape information");
712-
}
713-
714-
// Check dimensions count matches
715-
size_t graph_dim_count = graph_shape->dim_size();
716-
size_t requested_dim_count = requested_shape.get_max_shape().size();
717-
718-
if (graph_dim_count != requested_dim_count) {
719-
ORT_THROW("Dimensions mismatch for input '" + tensor_name +
720-
"': graph expects " + std::to_string(graph_dim_count) +
721-
" dimensions but reshape_input specifies " +
722-
std::to_string(requested_dim_count) + " dimensions");
723-
}
724-
}
725-
}
726-
727658
void BackendManager::Compute(OrtKernelContext* context) {
728659
Ort::KernelContext ctx(context);
729660
std::chrono::high_resolution_clock::time_point start_compute, end_compute;

onnxruntime/core/providers/openvino/backend_manager.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ class BackendManager {
4242
std::unordered_set<std::string> IdentifyDynamicInputs(const onnxruntime::GraphViewer& subgraph,
4343
const std::vector<const NodeArg*>& graph_inputs) const;
4444
bool ModelHasBatchedInputs(const ONNX_NAMESPACE::ModelProto& model_proto) const;
45-
void ValidateInputShapes(const reshape_t& shapes,
46-
const std::vector<const NodeArg*>& graph_inputs) const;
4745

4846
std::shared_ptr<ONNX_NAMESPACE::ModelProto>
4947
ReWriteBatchDimWithOne(const ONNX_NAMESPACE::ModelProto& model_proto);

onnxruntime/core/providers/openvino/backends/basic_backend.cc

Lines changed: 2 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ BasicBackend::BasicBackend(std::unique_ptr<ONNX_NAMESPACE::ModelProto>& model_pr
6363
hw_target,
6464
device_config,
6565
enable_causallm,
66-
model_file_path());
66+
model_file_path(),
67+
session_context_);
6768
} else {
6869
// If the blob is held in an EPContext node, then skip FE+Compile
6970
// and directly move on to creating a backend with the executable blob
@@ -308,26 +309,6 @@ void BasicBackend::SetOVDeviceConfiguration(ov::AnyMap& device_config) {
308309
}
309310
}
310311

311-
void BasicBackend::ValidateOrtDimsAgainstPartialShape(const std::vector<int64_t>& ort_dims,
312-
const ov::PartialShape& partial_shape) const {
313-
// Check if the number of dimensions matches
314-
if (static_cast<int64_t>(ort_dims.size()) != partial_shape.rank().get_length()) {
315-
ORT_THROW("Mismatch in number of dimensions between ORT tensor and OpenVINO PartialShape.");
316-
}
317-
// Validate each dimension
318-
for (size_t i = 0; i < ort_dims.size(); ++i) {
319-
const auto& ov_dim = partial_shape[i]; // OpenVINO dimension at index i
320-
int64_t ort_dim = ort_dims[i]; // ORT dimension at index i
321-
322-
// Check if the ORT dimension is within the specified range
323-
int64_t min_dim = ov_dim.get_min_length();
324-
int64_t max_dim = ov_dim.get_max_length();
325-
if (ort_dim < min_dim || ort_dim > max_dim) {
326-
ORT_THROW(" ORT Dimension is out of range");
327-
}
328-
}
329-
}
330-
331312
void BasicBackend::RewindKVCache(size_t index) {
332313
infer_req_pool_->forEachIdleRequest([&](OVInferRequestPtr& infer_request) {
333314
infer_request->RewindKVCache(index);
@@ -374,9 +355,6 @@ void BasicBackend::Infer(OrtKernelContext* ctx) const {
374355
// Set the input shape based on the input tensor from ort
375356
auto tensor = context.GetInput(input_info.onnx_index);
376357
auto ort_shape = tensor.GetTensorTypeAndShapeInfo().GetShape();
377-
if (input_info.IsBoundedDynamic()) {
378-
ValidateOrtDimsAgainstPartialShape(ort_shape, input_info.shape);
379-
}
380358
auto input_shape = ParameterShape(ort_shape);
381359

382360
infer_request->SetTensor(input_info.name,

onnxruntime/core/providers/openvino/backends/basic_backend.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ struct ParameterInfo {
3636
// Query methods
3737
bool IsStatic() const { return dynamic_flags == 0; }
3838
bool IsFullyDynamic() const { return dynamic_flags & 1; }
39-
bool IsBoundedDynamic() const { return dynamic_flags & 2; }
4039
bool IsMixed() const { return (dynamic_flags & 3) == 3; }
4140

4241
// Setter methods
@@ -147,8 +146,6 @@ class BasicBackend : public IBackend {
147146
void EnableStreams(ov::AnyMap& device_config);
148147
void SetNumThreads(ov::AnyMap& device_config);
149148
void SetOVDeviceConfiguration(ov::AnyMap& device_config);
150-
void ValidateOrtDimsAgainstPartialShape(const std::vector<int64_t>& ort_dims,
151-
const ov::PartialShape& partial_shape) const;
152149

153150
SessionContext& session_context_;
154151
SubGraphContext subgraph_context_;

onnxruntime/core/providers/openvino/ov_interface.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ OVExeNetwork OVCore::ImportEPCtxOVIREncapsulation(std::istream& model_stream,
226226
std::string& hw_target,
227227
const ov::AnyMap& device_config,
228228
bool enable_causallm,
229-
std::filesystem::path model_file_path) {
229+
std::filesystem::path model_file_path,
230+
const SessionContext& session_context) {
230231
return OvExceptionBoundary<false>([&]() {
231232
OVExeNetwork exe;
232233

@@ -259,6 +260,11 @@ OVExeNetwork OVCore::ImportEPCtxOVIREncapsulation(std::istream& model_stream,
259260
// Load the model explicitly with XML contents
260261
std::shared_ptr<ov::Model> model = core.read_model(xml_file_path.string());
261262

263+
if (!session_context.reshape.empty()) {
264+
LOGS_DEFAULT(INFO) << log_tag << "Reshaping OV-IR model to specified shape";
265+
model->reshape(session_context.reshape);
266+
}
267+
262268
if (enable_causallm) {
263269
exe = OVCore::Get()->StatefulCompileModel(model, hw_target, device_config);
264270
} else {

onnxruntime/core/providers/openvino/ov_interface.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class OVCore;
3939
class OVInferRequest;
4040
class OVExeNetwork;
4141
struct ModelBlobWrapper;
42+
struct SessionContext;
4243

4344
typedef ov::Tensor OVTensor;
4445
typedef ov::ProfilingInfo OVProfilingInfo;
@@ -77,7 +78,8 @@ struct OVCore : WeakSingleton<OVCore> {
7778
std::string& hw_target,
7879
const ov::AnyMap& device_config,
7980
bool enable_causallm,
80-
std::filesystem::path model_file_path);
81+
std::filesystem::path model_file_path,
82+
const SessionContext& session_context);
8183

8284
std::vector<std::string> GetAvailableDevices() const;
8385
std::vector<std::string> GetAvailableDevices(const std::string& device_type) const;

0 commit comments

Comments
 (0)