Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions Samples/WindowsML/Shared/cpp/ArgumentParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,10 @@ namespace Shared
{
options.ep_policy = OrtExecutionProviderDevicePolicy_DEFAULT;
}
else if (policy_str == L"DISABLE")
{
options.ep_policy = std::nullopt;
}
else
{
std::wcout << L"Unknown EP policy: " << policy_str << L", using default (DISABLE)\n";
std::wcout << L"Unknown EP policy: " << policy_str << L". Valid values are: NPU, CPU, GPU, DEFAULT\n";
throw std::invalid_argument("Unknown EP policy value");
}
}
else if (arguments[i] == L"--perf_mode" && i + 1 < arguments.size())
Expand Down Expand Up @@ -183,15 +180,15 @@ namespace Shared
{
std::wcout << L"Usage: Application.exe [options]\n"
<< L"Options:\n"
<< L" --ep_policy <policy> Set execution provider selection policy (NPU, CPU, GPU, DEFAULT, DISABLE)\n"
<< L" --ep_policy <policy> Set execution provider selection policy (NPU, CPU, GPU, DEFAULT)\n"
<< L" --ep_name <name> Explicit execution provider name (mutually exclusive with --ep_policy)\n"
<< L" --device_type <type> Device type for OpenVINOExecutionProvider (NPU, GPU, CPU) when multiple present\n"
<< L" --perf_mode <mode> Set EP performance mode (MAX_PERFORMANCE, MAX_EFFICIENCY, DEFAULT)\n"
<< L" --compile Compile the model\n"
<< L" --download Download required packages\n"
<< L" --use_model_catalog Use the model catalog for model selection\n"
<< L" --model <path> Path to the input ONNX model (default: SqueezeNet.onnx in executable directory)\n"
<< L" --compiled_output <path> Path for compiled output model (default: SqueezeNet_ctx.onnx)\n"
<< L" --compiled_output <path> Path for compiled output model (default: auto-generated with device info)\n"
<< L" --image_path <path> Path to the input image (default: sample kitten image)\n"
<< L"\n"
<< L"Exactly one of --ep_policy or --ep_name must be specified.\n"
Expand Down
54 changes: 53 additions & 1 deletion Samples/WindowsML/Shared/cpp/ModelManager.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE.md in the repo root for license information.
#include "ModelManager.h"
#include <iostream>
Expand Down Expand Up @@ -253,5 +253,57 @@ namespace Shared
return ModelVariant::Default;
}

std::filesystem::path ModelManager::GenerateCompiledModelPath(
const std::filesystem::path& modelPath,
const std::filesystem::path& executableFolder,
const CommandLineOptions& options,
Ort::Env& env)
Comment thread
mahabayana marked this conversation as resolved.
Outdated
{
// If user explicitly specified --compiled_output, use it as-is
if (!options.output_path.empty())
{
return std::filesystem::path(options.output_path);
}

std::wstring baseName = modelPath.stem().wstring();
std::wstring suffix;

if (options.ep_policy.has_value())
{
std::string policyStr = ArgumentParser::ToString(options.ep_policy.value());
suffix = L"_" + std::wstring(policyStr.begin(), policyStr.end());
}
else if (!options.ep_name.empty())
{
suffix = L"_" + options.ep_name;

// Try to determine device type
std::wstring deviceType;
if (options.device_type.has_value())
{
deviceType = options.device_type.value();
}

if (!deviceType.empty())
{
suffix += L"_" + deviceType;
}
}

if (options.perf_mode == PerformanceMode::MaxPerformance)
{
suffix += L"_MaxPerformance";
}
else if (options.perf_mode == PerformanceMode::MaxEfficiency)
{
suffix += L"_MaxEfficiency";
}

std::wstring fileName = baseName + L"_ctx" + suffix + L".onnx";
auto result = executableFolder / fileName;
std::wcout << L"Compiled model path: " << result.wstring() << std::endl;
return result;
}

} // namespace Shared
} // namespace WindowsML
11 changes: 11 additions & 0 deletions Samples/WindowsML/Shared/cpp/ModelManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ namespace Shared
std::wstring& outputModelPath,
std::wstring& outputImagePath);

/// <summary>
/// Generate a device-specific compiled model path.
/// Encodes EP policy/name, device type, and performance mode so compiled models
/// for different device configurations don't collide.
/// </summary>
static std::filesystem::path GenerateCompiledModelPath(
const std::filesystem::path& modelPath,
const std::filesystem::path& executableFolder,
const CommandLineOptions& options,
Ort::Env& env);

/// <summary>
/// Get model path for specified variant
/// </summary>
Expand Down
13 changes: 4 additions & 9 deletions Samples/WindowsML/Shared/cs/ArgumentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public class Options
public bool Download { get; set; } = false;
public bool UseModelCatalog { get; set; } = false;
public string ModelPath { get; set; } = string.Empty;
public string OutputPath { get; set; } = "SqueezeNet_ctx.onnx";
public string OutputPath { get; set; } = string.Empty;
public string ImagePath { get; set; } = string.Empty;
public ModelVariant Variant { get; set; } = ModelVariant.Default;
public PerformanceMode PerfMode { get; set; } = PerformanceMode.Default;
Expand Down Expand Up @@ -75,13 +75,8 @@ public static Options ParseOptions(string[] args)
case "DEFAULT":
options.EpPolicy = ExecutionProviderDevicePolicy.DEFAULT;
break;
case "DISABLE":
options.EpPolicy = null;
break;
default:
Console.WriteLine($"Unknown EP policy: {policyStr}, using default (DISABLE)");
options.EpPolicy = null;
break;
throw new Exception($"Unknown EP policy: {policyStr}. Valid values are: NPU, CPU, GPU, DEFAULT.");
}
}
break;
Expand Down Expand Up @@ -213,15 +208,15 @@ public static Options ParseOptions(string[] args)
public static void PrintHelp()
{
Console.WriteLine("Options:");
Console.WriteLine(" --ep_policy <policy> Set execution provider policy (NPU, CPU, GPU, DEFAULT, DISABLE)");
Console.WriteLine(" --ep_policy <policy> Set execution provider policy (NPU, CPU, GPU, DEFAULT)");
Console.WriteLine(" --ep_name <name> Explicit execution provider name (mutually exclusive with --ep_policy)");
Console.WriteLine(" --device_type <type> Optional hardware device type to use when EP supports multiple (e.g. CPU, GPU, NPU)");
Console.WriteLine(" --perf_mode <mode> Set EP performance mode (MAX_PERFORMANCE, MAX_EFFICIENCY, DEFAULT)");
Console.WriteLine(" --compile Compile the model");
Console.WriteLine(" --download Download required packages");
Console.WriteLine(" --use_model_catalog Use the model catalog for model discovery");
Console.WriteLine(" --model <path> Path to input ONNX model (default: SqueezeNet.onnx)");
Console.WriteLine(" --compiled_output <path> Path for compiled output model (default: SqueezeNet_ctx.onnx)");
Console.WriteLine(" --compiled_output <path> Path for compiled output model (default: auto-generated with device info)");
Console.WriteLine(" --image_path <path> Path to the input image (default: sample kitten image)");
Console.WriteLine(" --help, -h Display this help message");
Console.WriteLine();
Expand Down
1 change: 1 addition & 0 deletions Samples/WindowsML/Shared/cs/ExecutionProviderManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public static async Task InitializeProvidersAsync(bool allowDownload = false)
if (allowDownload || readyState != ExecutionProviderReadyState.NotPresent)
{
await provider.EnsureReadyAsync();
Console.WriteLine($" Updated Ready state: {provider.ReadyState}");
}

provider.TryRegister();
Expand Down
60 changes: 56 additions & 4 deletions Samples/WindowsML/Shared/cs/ModelManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,60 @@ public static string GetModelVariantPath(string executableFolder, ModelVariant v
return modelPath;
}

/// <summary>
/// Generate a device-specific compiled model path.
/// Encodes EP policy/name, device type, and performance mode into the filename
/// so that compiled models for different device configurations don't collide.
/// </summary>
public static string GenerateCompiledModelPath(string modelPath, string executableFolder, Options options, OrtEnv? ortEnv = null)
{
// If user explicitly specified --compiled_output, use it as-is
if (!string.IsNullOrEmpty(options.OutputPath))
{
return options.OutputPath.Contains(Path.DirectorySeparatorChar) ?
options.OutputPath : Path.Combine(executableFolder, options.OutputPath);
}

string baseName = Path.GetFileNameWithoutExtension(modelPath);
string suffix = BuildDeviceSuffix(options, ortEnv);

string fileName = $"{baseName}_ctx{suffix}.onnx";
Console.WriteLine($"Compiled model path: {Path.Combine(executableFolder, fileName)}");
return Path.Combine(executableFolder, fileName);
}

/// <summary>
/// Build a device-identifying suffix for the compiled model filename.
/// </summary>
private static string BuildDeviceSuffix(Options options, OrtEnv? ortEnv)
Comment thread
mahabayana marked this conversation as resolved.
Outdated
{
var parts = new List<string>();

if (options.EpPolicy.HasValue)
{
parts.Add(options.EpPolicy.Value.ToString());
}
else if (!string.IsNullOrEmpty(options.EpName))
{
parts.Add(options.EpName);

// Try to determine device type
string? deviceType = options.DeviceType;

if (!string.IsNullOrEmpty(deviceType))
{
parts.Add(deviceType);
}
}

if (options.PerfMode != PerformanceMode.Default)
{
parts.Add(options.PerfMode.ToString());
}

return parts.Count > 0 ? "_" + string.Join("_", parts) : "";
}

/// <summary>
/// Resolve model paths with intelligent variant selection
/// </summary>
Expand Down Expand Up @@ -192,8 +246,7 @@ public static string GetModelVariantPath(string executableFolder, ModelVariant v
modelPath = GetModelVariantPath(executableFolder, variant);
}

string compiledModelPath = options.OutputPath.Contains(Path.DirectorySeparatorChar) ?
options.OutputPath : Path.Combine(executableFolder, options.OutputPath);
string compiledModelPath = GenerateCompiledModelPath(modelPath, executableFolder, options, ortEnv);

if (!File.Exists(labelsPath))
{
Expand Down Expand Up @@ -225,8 +278,7 @@ public static (string modelPath, string compiledModelPath, string labelsPath) Re
modelPath = GetModelVariantPath(executableFolder, options.Variant);
}

string compiledModelPath = options.OutputPath.Contains(Path.DirectorySeparatorChar) ?
options.OutputPath : Path.Combine(executableFolder, options.OutputPath);
string compiledModelPath = GenerateCompiledModelPath(modelPath, executableFolder, options);

string labelsPath = Path.Combine(executableFolder, "SqueezeNet.Labels.txt");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ IAsyncAction RunInferenceAsync(const CommandLineOptions& options)
std::vector<std::string> labels = ModelManager::LoadLabels(labelsPath);

std::filesystem::path outputPath =
options.output_path.empty() ? executableFolder / L"SqueezeNet_ctx.onnx" : std::filesystem::path(options.output_path);
ModelManager::GenerateCompiledModelPath(modelPath, executableFolder, options, env);

std::filesystem::path imagePath =
options.image_path.empty() ? executableFolder / L"image.png" : std::filesystem::path(options.image_path);
Expand Down
13 changes: 10 additions & 3 deletions Samples/WindowsML/cpp/CppConsoleDesktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ This sample demonstrates how to use ONNX Runtime in a C++ desktop application, f
```
CppConsoleDesktop.exe [options]
Options:
--ep_policy <policy> Set execution provider policy (NPU, CPU, GPU, DEFAULT, DISABLE). Default: DISABLE
--ep_policy <policy> Set execution provider policy (NPU, CPU, GPU, DEFAULT)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that there's an open PR for addressing the issue w/ the DISABLE option: (#620). How would you all like to reconcile this? @mahabayana @yeelam-gordon

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adrastogi @yeelam-gordon
The samples require either --ep_policy or --ep_name to be set. The following scenarios fail due to DISABLE:

Scenario 1 : User explicitly passes DISABLE: The user runs --ep_policy DISABLE. The parser sets ep_policy = null and ep_name remains empty. Validation sees neither is set and throws: "Missing EP selection" - even though the user did specify a policy.

Scenario 2 : User passes an invalid value (e.g., a typo): The user runs --ep_policy NPUU. The parser prints "Unknown EP policy: NPUU, using default (DISABLE)" and silently sets ep_policy = null. Validation again throws "Missing EP selection" : masking the real error (an invalid value)

Thus I would propose this resolution

  • Removed DISABLE as a valid --ep_policy option. If the user's intention is to default to CPU behavior, they can use DEFAULT instead, making DISABLE redundant.
  • Unknown values now throw immediately with the list of valid options (NPU, CPU, GPU, DEFAULT), so typos and invalid inputs are caught at parse time rather than falling through to a incorrect validation error

--compile Compile the model
--download Download required packages
--model <path> Path to input ONNX model (default: SqueezeNet.onnx in executable directory)
--compiled_output <path> Path for compiled output model (default: SqueezeNet_ctx.onnx)
--compiled_output <path> Path for compiled output model (default: auto-generated with device info)
--image_path <path> Path to the input image (default: sample kitten image)
```

Expand Down Expand Up @@ -68,7 +68,14 @@ for (const auto& [ep_name, devices] : ep_device_map)

### 2. Model Compilation

The sample shows how to compile an ONNX model for optimized execution:
The sample shows how to compile an ONNX model for optimized execution. Compiled model filenames
are automatically generated with device-specific identifiers to prevent collisions:

- Policy mode: `SqueezeNet_ctx_PREFER_GPU.onnx`
- Explicit EP: `SqueezeNet_ctx_DML_GPU.onnx`
- With perf mode: `SqueezeNet_ctx_PREFER_NPU_MaxPerformance.onnx`

Use `--compiled_output` to override with a custom path.

```cpp
#include <win_onnxruntime_cxx_api.h>
Expand Down
13 changes: 10 additions & 3 deletions Samples/WindowsML/cs/CSharpConsoleDesktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ This sample demonstrates how to use ONNX Runtime in a C# desktop application, fo
```shell
CSharpConsoleDesktop.exe [options]
Options:
--ep_policy <policy> Set execution provider policy (NPU, CPU, GPU, DEFAULT, DISABLE). Default: DISABLE
--ep_policy <policy> Set execution provider policy (NPU, CPU, GPU, DEFAULT)
--compile Compile the model
--download Download required packages
--model <path> Path to input ONNX model (default: SqueezeNet.onnx in executable directory)
--compiled_output <path> Path for compiled output model (default: SqueezeNet_ctx.onnx)
--compiled_output <path> Path for compiled output model (default: auto-generated with device info)
--image_path <path> Path to the input image (default: sample kitten image)
```

Expand Down Expand Up @@ -80,7 +80,14 @@ foreach (KeyValuePair<string, List<OrtEpDevice>> epGroup in epDeviceMap)

### 2. Model Compilation

The sample shows how to compile an ONNX model for optimized execution:
The sample shows how to compile an ONNX model for optimized execution. Compiled model filenames
are automatically generated with device-specific identifiers to prevent collisions:

- Policy mode: `SqueezeNet_ctx_PREFER_GPU.onnx`
- Explicit EP: `SqueezeNet_ctx_DML_GPU.onnx`
- With perf mode: `SqueezeNet_ctx_PREFER_NPU_MaxPerformance.onnx`

Use `--compiled_output` to override with a custom path.

```csharp
// Create compilation options from session options
Expand Down