26
26
#include " core/graph/indexed_sub_graph.h"
27
27
#include " core/graph/model.h"
28
28
#include " core/graph/model_load_utils.h"
29
+ #include " core/graph/model_saving_options.h"
29
30
#include " core/graph/node_attr_utils.h"
30
31
#include " core/graph/op.h"
31
32
#include " core/graph/runtime_optimization_record_container.h"
@@ -4085,16 +4086,128 @@ ONNX_NAMESPACE::GraphProto Graph::ToGraphProto() const {
4085
4086
return result;
4086
4087
}
4087
4088
4088
- ONNX_NAMESPACE::GraphProto Graph::ToGraphProtoWithExternalInitializers (const std::filesystem::path& external_file_path,
4089
- const std::filesystem::path& model_file_path,
4090
- size_t initializer_size_threshold,
4091
- const OffsetAlignmentInfo& align_info) const {
4089
+ // Create a recursive function that does bottom up with subgraphs
4090
+ ONNX_NAMESPACE::GraphProto Graph::ToGraphProtoWithExternalInitiallizersImpl (
4091
+ const std::filesystem::path& model_path,
4092
+ const std::filesystem::path& external_file_path,
4093
+ const ModelSavingOptions& model_saving_options,
4094
+ ONNX_NAMESPACE::GraphProto& output_graph_proto,
4095
+ std::ostream& external_stream,
4096
+ int64_t & external_offset) const {
4097
+ // update external_offset for alignment
4098
+ // need to do padding before write actual tensor data as we do offset alignment at the begin of
4099
+ // large tensors (offset need to be page aligned and allocation granularity aligned) like below:
4100
+ // \242\2557\256\023.\031&0000000000000000\332)k+\253\246\342\246(&\006!\347\232\374\236\325\026\032+\36XXXX
4101
+ // |<---small tensor---->|<---padding--->|<------------------large tensor----------------------------->|
4102
+ auto compute_and_pad = [&external_stream](int64_t allocation_granularity, int64_t & external_offset) {
4103
+ // Align to the larger of the page size or the allocation granularity
4104
+ int64_t alignment_factor = std::max (static_cast <int64_t >(4096 ), allocation_granularity);
4105
+ // Align to the next page or alloc granularity boundary
4106
+ int64_t new_external_offset = static_cast <int64_t >(
4107
+ std::floor ((external_offset + alignment_factor - 1 ) / alignment_factor)) *
4108
+ alignment_factor;
4109
+
4110
+ // padding tensor with zeros for alignment
4111
+ for (int64_t index = external_offset; index != new_external_offset; ++index ) {
4112
+ external_stream << ' \0 ' ;
4113
+ }
4114
+ external_offset = new_external_offset;
4115
+ };
4116
+
4117
+ // Process subgraphs
4118
+ for (const auto & node : Nodes ()) {
4119
+ if (node.ContainsSubgraph ()) {
4120
+ // Let find this node in the output_graph_proto
4121
+ auto hit = std::find_if (output_graph_proto.node ().begin (),
4122
+ output_graph_proto.node ().end (),
4123
+ [&node](const ONNX_NAMESPACE::NodeProto& proto) {
4124
+ return proto.name () == node.Name ();
4125
+ });
4126
+ ORT_ENFORCE (hit != output_graph_proto.node ().end (), " Node " , node.Name (),
4127
+ " not found in output_graph_proto" );
4128
+ auto & result_node = *hit;
4129
+ for (const auto & [name, subgraph] : node.GetAttributeNameToSubgraphMap ()) {
4130
+ // Lets find this subgraph in the result_node
4131
+ auto sub_hit = std::find_if (result_node.attribute ().begin (),
4132
+ result_node.attribute ().end (),
4133
+ [&name](const ONNX_NAMESPACE::AttributeProto& proto) {
4134
+ return proto.name () == name;
4135
+ });
4136
+ ORT_ENFORCE (sub_hit != result_node.attribute ().end (), " Subgraph " , name,
4137
+ " not found in node " , node.Name ());
4138
+ }
4139
+ }
4140
+ }
4141
+
4142
+ // Add the initializers to the result graph.
4143
+ for (const auto & initializer : graph_proto_->initializer ()) {
4144
+ #if !defined(DISABLE_SPARSE_TENSORS)
4145
+ if (IsSparseInitializer (initializer.name ())) {
4146
+ // Sparse tensors are added to the ONNX file.
4147
+ auto & sparse_initializer = *output_graph_proto.add_sparse_initializer ();
4148
+ auto status = utils::DenseTensorToSparseTensorProto (initializer, model_path, sparse_initializer);
4149
+ ORT_ENFORCE (status.IsOK (), " Failed to convert dense initializer to sparse" );
4150
+ } else {
4151
+ #endif
4152
+ // Dense tensors larger than the threshold are added to the external file.
4153
+ TensorProto* output_proto = output_graph_proto.add_initializer ();
4154
+
4155
+ std::vector<uint8_t > raw_data;
4156
+ ORT_THROW_IF_ERROR (utils::UnpackInitializerData (initializer, model_path, raw_data));
4157
+ size_t tensor_bytes_size = raw_data.size ();
4158
+ if (tensor_bytes_size < model_saving_options.initializer_size_threshold ) {
4159
+ *output_proto = initializer;
4160
+ continue ;
4161
+ }
4162
+
4163
+ // update external_offset for alignment
4164
+ // need to do padding before write actual tensor data as we do offset alignment at the begin of
4165
+ // large tensors (offset need to be page aligned and allocation granularity aligned) like below:
4166
+ // \242\2557\256\023.\031&0000000000000000\332)k+\253\246\342\246(&\006!\347\232\374\236\325\026\032+\36XXXX
4167
+ // |<---small tensor---->|<---padding--->|<------------------large tensor----------------------------->|
4168
+ if (model_saving_options.align_offset && static_cast <int64_t >(tensor_bytes_size) >
4169
+ model_saving_options.align_threshold ) {
4170
+ compute_and_pad (model_saving_options.allocation_granularity , external_offset);
4171
+ }
4172
+
4173
+ if (!external_stream.write (reinterpret_cast <const char *>(raw_data.data ()), tensor_bytes_size)) {
4174
+ ORT_THROW (" Failed to write external initializers to file: " , modified_external_file_path);
4175
+ }
4176
+
4177
+ ExternalDataInfo::SetExternalLocationToProto (external_file_path, external_offset,
4178
+ tensor_bytes_size, *output_proto);
4179
+
4180
+ output_proto->set_name (initializer.name ());
4181
+ output_proto->set_data_type (initializer.data_type ());
4182
+ for (int i = 0 ; i != initializer.dims_size (); ++i) {
4183
+ output_proto->add_dims (initializer.dims (i));
4184
+ }
4185
+ output_proto->set_doc_string (initializer.doc_string ());
4186
+
4187
+ external_offset += tensor_bytes_size;
4188
+
4189
+ const PrepackedForSerialization::Subgraph* prepacked_subgraph = nullptr ;
4190
+ if (model_saving_options.prepacked_for_save != nullptr ) {
4191
+ prepacked_subgraph = *model_saving_options.prepacked_for_save ->FindOrCreateSubgraph (*this );
4192
+ }
4193
+
4194
+ #if !defined(DISABLE_SPARSE_TENSORS)
4195
+ }
4196
+ #endif
4197
+ }
4198
+ }
4199
+
4200
+ ONNX_NAMESPACE::GraphProto Graph::ToGraphProtoWithExternalInitializers (
4201
+ const std::filesystem::path& external_file_path,
4202
+ const std::filesystem::path& model_file_path,
4203
+ const ModelSavingOptions& model_saving_options) const {
4092
4204
GraphProto result;
4093
4205
ToGraphProtoInternal (result);
4094
4206
ORT_ENFORCE (external_file_path.is_relative ());
4095
4207
// If model_file_path is just a file name without a path separator, for example: "model.onnx". Its parent path could
4096
4208
// be empty. Else, save external data file in same directory as the model.
4097
4209
const std::filesystem::path modified_external_file_path = model_file_path.parent_path () / external_file_path;
4210
+ const auto & model_path = ModelPath ();
4098
4211
4099
4212
// Create the external file.
4100
4213
std::ofstream external_stream (modified_external_file_path, std::ofstream::out | std::ofstream::binary);
@@ -4122,7 +4235,6 @@ ONNX_NAMESPACE::GraphProto Graph::ToGraphProtoWithExternalInitializers(const std
4122
4235
};
4123
4236
4124
4237
// Add the initializers to the result graph.
4125
- const auto & model_path = ModelPath ();
4126
4238
#if !defined(DISABLE_SPARSE_TENSORS)
4127
4239
const auto sparse_end = sparse_tensor_names_.end ();
4128
4240
#endif
@@ -4142,7 +4254,7 @@ ONNX_NAMESPACE::GraphProto Graph::ToGraphProtoWithExternalInitializers(const std
4142
4254
std::vector<uint8_t > raw_data;
4143
4255
ORT_THROW_IF_ERROR (utils::UnpackInitializerData (initializer, model_path, raw_data));
4144
4256
size_t tensor_bytes_size = raw_data.size ();
4145
- if (tensor_bytes_size < initializer_size_threshold) {
4257
+ if (tensor_bytes_size < model_saving_options. initializer_size_threshold ) {
4146
4258
*output_proto = initializer;
4147
4259
continue ;
4148
4260
}
@@ -4152,8 +4264,9 @@ ONNX_NAMESPACE::GraphProto Graph::ToGraphProtoWithExternalInitializers(const std
4152
4264
// large tensors (offset need to be page aligned and allocation granularity aligned) like below:
4153
4265
// \242\2557\256\023.\031&0000000000000000\332)k+\253\246\342\246(&\006!\347\232\374\236\325\026\032+\36XXXX
4154
4266
// |<---small tensor---->|<---padding--->|<------------------large tensor----------------------------->|
4155
- if (align_info.align_offset && static_cast <int64_t >(tensor_bytes_size) > align_info.align_threshold ) {
4156
- compute_and_pad (align_info.allocation_granularity , external_offset);
4267
+ if (model_saving_options.align_offset && static_cast <int64_t >(tensor_bytes_size) >
4268
+ model_saving_options.align_threshold ) {
4269
+ compute_and_pad (model_saving_options.allocation_granularity , external_offset);
4157
4270
}
4158
4271
4159
4272
if (!external_stream.write (reinterpret_cast <const char *>(raw_data.data ()), tensor_bytes_size)) {
0 commit comments