Skip to content

Commit 5248845

Browse files
Merge pull request #1189 from intel/sync_msft_07072026
Sync with Microsoft ONNX Runtime - 07072026
2 parents da0c53d + 1ba52f1 commit 5248845

5 files changed

Lines changed: 104 additions & 19 deletions

File tree

include/onnxruntime/core/session/onnxruntime_session_options_config_keys.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -355,9 +355,12 @@ static const char* const kOrtSessionOptionsOptimizedModelExternalInitializersFil
355355
static const char* const kOrtSessionOptionsOptimizedModelExternalInitializersMinSizeInBytes =
356356
"session.optimized_model_external_initializers_min_size_in_bytes";
357357

358-
// When loading model from memory buffer and the model has external initializers
359-
// Use this config to set the external data file folder path
360-
// All external data files should be in the same folder
358+
// Specifies the folder path used to resolve a model's external initializer data files.
359+
// When set, external initializers are loaded from this folder instead of the model's own
360+
// directory, overriding the model directory. This applies whether the model is loaded from a
361+
// file path or from a memory buffer/stream. All external data files must be in the same folder.
362+
// Typical uses include loading models with external data from memory, sharing a weights file
363+
// across models, and weightless/cache models whose weights live outside the model directory.
361364
static const char* const kOrtSessionOptionsModelExternalInitializersFileFolderPath =
362365
"session.model_external_initializers_file_folder_path";
363366

onnxruntime/core/graph/model.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -711,6 +711,19 @@ Status Model::Load(const PathString& file_path, std::shared_ptr<Model>& p_model,
711711
return LoadModel(file_path, p_model, local_registries, logger, options);
712712
}
713713

714+
GSL_SUPPRESS(r .30) // spurious warnings. p_model is potentially reset in the internal call to Load
715+
GSL_SUPPRESS(r .35)
716+
Status Model::Load(const PathString& file_path, const PathString& graph_model_path,
717+
std::shared_ptr<Model>& p_model,
718+
const IOnnxRuntimeOpSchemaRegistryList* local_registries,
719+
const logging::Logger& logger, const ModelOptions& options) {
720+
const auto loader = [&graph_model_path, &p_model, local_registries, &logger, &options](int fd) {
721+
return Model::Load(fd, graph_model_path, p_model, local_registries, logger, options);
722+
};
723+
724+
return LoadModelHelper(file_path, loader);
725+
}
726+
714727
Status Model::SaveWithExternalInitializers(Model& model, const std::filesystem::path& file_path,
715728
const std::filesystem::path& external_file_name,
716729
const ModelSavingOptions& save_options) {

onnxruntime/core/graph/model.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,16 @@ class Model {
251251
const logging::Logger& logger,
252252
const ModelOptions& options = {});
253253

254+
// Reads the model bytes from file_path but stores graph_model_path as the graph's model path.
255+
// graph_model_path is used as the base directory for resolving external initializers, so this
256+
// overload lets callers load a model file while resolving its external data from a different folder.
257+
static common::Status Load(const PathString& file_path,
258+
const PathString& graph_model_path,
259+
/*out*/ std::shared_ptr<Model>& p_model,
260+
const IOnnxRuntimeOpSchemaRegistryList* local_registries,
261+
const logging::Logger& logger,
262+
const ModelOptions& options = {});
263+
254264
static common::Status Load(int fd, /*out*/ ONNX_NAMESPACE::ModelProto& model_proto);
255265

256266
static common::Status Load(int fd, /*out*/ std::shared_ptr<Model>& p_model,

onnxruntime/core/session/inference_session.cc

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,21 @@ int ParseSpinDurationUs(std::string_view str, const char* config_key,
129129
return spin_us;
130130
}
131131

132+
#if !defined(ORT_MINIMAL_BUILD)
133+
// Returns the virtual model path derived from the
134+
// kOrtSessionOptionsModelExternalInitializersFileFolderPath config option, or an empty
135+
// PathString when the option is not set. When set, external initializers are resolved
136+
// relative to this folder, overriding the model's own directory.
137+
PathString GetExternalInitializersFolderModelPath(const ConfigOptions& config_options) {
138+
const std::string external_data_folder_path = config_options.GetConfigOrDefault(
139+
kOrtSessionOptionsModelExternalInitializersFileFolderPath, "");
140+
if (external_data_folder_path.empty()) {
141+
return PathString{};
142+
}
143+
return ToPathString(external_data_folder_path + "/virtual_model.onnx");
144+
}
145+
#endif // !defined(ORT_MINIMAL_BUILD)
146+
132147
// Parse a spin backoff max config value (exponential-backoff cap). Defaults to
133148
// 1 (no backoff, one SpinPause() per iteration). Values >= 2 enable backoff.
134149
unsigned int ParseSpinBackoffMax(std::string_view str, const char* config_key,
@@ -1171,7 +1186,7 @@ common::Status InferenceSession::LoadWithLoader(std::function<common::Status(std
11711186

11721187
common::Status InferenceSession::LoadOnnxModel(const PathString& model_uri) {
11731188
model_location_ = model_uri;
1174-
auto loader = [this](std::shared_ptr<onnxruntime::Model>& model) {
1189+
auto loader = [this, model_uri](std::shared_ptr<onnxruntime::Model>& model) {
11751190
#ifdef ENABLE_LANGUAGE_INTEROP_OPS
11761191
LoadInterOp(model_location_, interop_domains_, [&](const char* msg) { LOGS(*session_logger_, WARNING) << msg; });
11771192
InlinedVector<OrtCustomOpDomain*> domain_ptrs;
@@ -1182,10 +1197,19 @@ common::Status InferenceSession::LoadOnnxModel(const PathString& model_uri) {
11821197

11831198
const bool strict_shape_type_inference = session_options_.config_options.GetConfigOrDefault(
11841199
kOrtSessionOptionsConfigStrictShapeTypeInference, "0") == "1";
1200+
ModelOptions model_opts(true, strict_shape_type_inference, check_load_cancellation_fn_);
1201+
1202+
// When set, the external initializers folder overrides the model's own directory as the
1203+
// base for resolving external data. The model bytes are still read from model_uri.
1204+
PathString external_data_model_path = GetExternalInitializersFolderModelPath(session_options_.config_options);
1205+
if (!external_data_model_path.empty()) {
1206+
model_location_ = external_data_model_path;
1207+
return onnxruntime::Model::Load(model_uri, model_location_, model,
1208+
HasLocalSchema() ? &custom_schema_registries_ : nullptr,
1209+
*session_logger_, model_opts);
1210+
}
11851211
return onnxruntime::Model::Load(model_location_, model, HasLocalSchema() ? &custom_schema_registries_ : nullptr,
1186-
*session_logger_,
1187-
ModelOptions(true, strict_shape_type_inference,
1188-
check_load_cancellation_fn_));
1212+
*session_logger_, model_opts);
11891213
};
11901214

11911215
common::Status st = LoadWithLoader(loader, "model_loading_uri");
@@ -1270,10 +1294,9 @@ common::Status InferenceSession::Load(const void* model_data, int model_data_len
12701294
const bool strict_shape_type_inference = session_options_.config_options.GetConfigOrDefault(
12711295
kOrtSessionOptionsConfigStrictShapeTypeInference, "0") == "1";
12721296

1273-
std::string external_data_folder_path = session_options_.config_options.GetConfigOrDefault(
1274-
kOrtSessionOptionsModelExternalInitializersFileFolderPath, "");
1275-
if (!external_data_folder_path.empty() && model_location_.empty()) {
1276-
model_location_ = ToPathString(external_data_folder_path + "/virtual_model.onnx");
1297+
PathString external_data_model_path = GetExternalInitializersFolderModelPath(session_options_.config_options);
1298+
if (!external_data_model_path.empty()) {
1299+
model_location_ = external_data_model_path;
12771300
}
12781301

12791302
return onnxruntime::Model::Load(std::move(model_proto), model_location_, model,
@@ -1308,10 +1331,9 @@ common::Status InferenceSession::LoadOnnxModel(ModelProto model_proto) {
13081331
const bool strict_shape_type_inference = session_options_.config_options.GetConfigOrDefault(
13091332
kOrtSessionOptionsConfigStrictShapeTypeInference, "0") == "1";
13101333

1311-
std::string external_data_folder_path = session_options_.config_options.GetConfigOrDefault(
1312-
kOrtSessionOptionsModelExternalInitializersFileFolderPath, "");
1313-
if (!external_data_folder_path.empty() && model_location_.empty()) {
1314-
model_location_ = ToPathString(external_data_folder_path + "/virtual_model.onnx");
1334+
PathString external_data_model_path = GetExternalInitializersFolderModelPath(session_options_.config_options);
1335+
if (!external_data_model_path.empty()) {
1336+
model_location_ = external_data_model_path;
13151337
}
13161338

13171339
// This call will move model_proto to the constructed model instance
@@ -1354,10 +1376,9 @@ common::Status InferenceSession::Load(std::istream& model_istream, bool allow_re
13541376
strict_shape_type_inference,
13551377
check_load_cancellation_fn_);
13561378

1357-
std::string external_data_folder_path = session_options_.config_options.GetConfigOrDefault(
1358-
kOrtSessionOptionsModelExternalInitializersFileFolderPath, "");
1359-
if (!external_data_folder_path.empty() && model_location_.empty()) {
1360-
model_location_ = ToPathString(external_data_folder_path + "/virtual_model.onnx");
1379+
PathString external_data_model_path = GetExternalInitializersFolderModelPath(session_options_.config_options);
1380+
if (!external_data_model_path.empty()) {
1381+
model_location_ = external_data_model_path;
13611382
}
13621383

13631384
return onnxruntime::Model::Load(std::move(model_proto), model_location_, model,
@@ -1388,6 +1409,11 @@ common::Status InferenceSession::Load() {
13881409
const bool allow_released_opsets_only = session_options_.config_options.GetConfigOrDefault(
13891410
kOrtSessionOptionsConfigStrictAllowReleasedOpsetsOnly, "1") == "1";
13901411

1412+
PathString external_data_model_path = GetExternalInitializersFolderModelPath(session_options_.config_options);
1413+
if (!external_data_model_path.empty()) {
1414+
model_location_ = external_data_model_path;
1415+
}
1416+
13911417
// Pass on ownership of the parsed ModelProto to the Model instance (its job here is done by this stage)
13921418
return Model::Load(std::move(this->model_proto_), model_location_, model,
13931419
HasLocalSchema() ? &custom_schema_registries_ : nullptr, *session_logger_,

onnxruntime/test/shared_lib/test_model_loading.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "core/common/narrow.h"
77
#include "test/util/include/asserts.h"
88
#include <fstream>
9+
#include <filesystem>
910
#include <limits>
1011
#include "test_fixture.h"
1112
#include "file_util.h"
@@ -275,6 +276,38 @@ TEST(CApiTest, TestLoadModelFromArrayWithExternalInitializersViaSetExternalDataP
275276
ASSERT_EQ(std::remove(generated_bin_path.c_str()), 0);
276277
}
277278

279+
// The model has external data. Load the model from a file path whose directory does NOT contain
280+
// the external data file, and set model_external_initializers_file_folder_path to the folder that
281+
// does. This verifies the option is honored for file-path loads and overrides the model directory.
282+
TEST(CApiTest, TestLoadModelFromPathWithExternalInitializersViaSetExternalDataPath) {
283+
const std::string model_file_name = "conv_qdq_external_ini.onnx";
284+
const std::string test_folder = "testdata/";
285+
286+
// Copy just the model file (not the external .bin) into a subfolder so the model's own directory
287+
// cannot resolve the external initializers.
288+
const std::string model_only_folder = test_folder + "ext_ini_path_override/";
289+
std::error_code ec;
290+
std::filesystem::create_directories(model_only_folder, ec);
291+
ASSERT_FALSE(ec);
292+
const std::string copied_model_path = model_only_folder + model_file_name;
293+
std::filesystem::copy_file(test_folder + model_file_name, copied_model_path,
294+
std::filesystem::copy_options::overwrite_existing, ec);
295+
ASSERT_FALSE(ec) << "Failed to copy model file: " << ec.message();
296+
297+
Ort::SessionOptions so;
298+
so.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_DISABLE_ALL);
299+
// Point external initializers at the original folder, overriding the copied model's directory.
300+
so.AddConfigEntry(kOrtSessionOptionsModelExternalInitializersFileFolderPath, test_folder.c_str());
301+
302+
// Loading succeeds only if the external initializers are resolved from the override folder.
303+
const PathString copied_model_path_t(copied_model_path.begin(), copied_model_path.end());
304+
Ort::Session session(*ort_env.get(), copied_model_path_t.c_str(), so);
305+
306+
// Cleanup.
307+
std::filesystem::remove_all(model_only_folder, ec);
308+
ASSERT_FALSE(ec) << "Failed to remove temp folder: " << ec.message();
309+
}
310+
278311
#ifndef _WIN32
279312
struct FileDescriptorTraits {
280313
using Handle = int;

0 commit comments

Comments
 (0)