@@ -21,7 +21,7 @@ namespace Microsoft.ML.Transforms.Onnx
21
21
/// It provides API to open a session, score tensors (NamedOnnxValues) and return
22
22
/// the results.
23
23
/// </summary>
24
- internal sealed class OnnxModel
24
+ internal sealed class OnnxModel : IDisposable
25
25
{
26
26
/// <summary>
27
27
/// OnnxModelInfo contains the data that we should get from
@@ -112,18 +112,39 @@ public OnnxVariableInfo(string name, OnnxShape shape, System.Type ortType, DataV
112
112
}
113
113
}
114
114
115
- public readonly OnnxModelInfo ModelInfo ;
115
+ /// <summary>
116
+ /// The ONNXRuntime facility to execute the loaded ONNX model.
117
+ /// </summary>
116
118
private readonly InferenceSession _session ;
117
- private readonly string _modelFile ;
119
+ /// <summary>
120
+ /// Indicates if <see cref="ModelFile"/> is a temporal file created by <see cref="CreateFromBytes(byte[], int?, bool)"/>
121
+ /// or <see cref="CreateFromBytes(byte[])"/>. If <see langword="true"/>, <see cref="Dispose(bool)"/> should delete <see cref="ModelFile"/>.
122
+ /// </summary>
123
+ private bool _ownModelFile ;
124
+ /// <summary>
125
+ /// The ONNX model file that <see cref="OnnxModel"/> built upon.
126
+ /// </summary>
127
+ internal OnnxModelInfo ModelInfo { get ; }
128
+ /// <summary>
129
+ /// The location where the used ONNX model loaded from.
130
+ /// </summary>
131
+ internal string ModelFile { get ; }
118
132
119
133
/// <summary>
120
134
/// Constructs OnnxModel object from file.
121
135
/// </summary>
122
136
/// <param name="modelFile">Model file path.</param>
123
137
/// <param name="gpuDeviceId">GPU device ID to execute on. Null for CPU.</param>
124
138
/// <param name="fallbackToCpu">If true, resumes CPU execution quitely upon GPU error.</param>
125
- public OnnxModel ( string modelFile , int ? gpuDeviceId = null , bool fallbackToCpu = false )
139
+ /// <param name="ownModelFile">If true, the <paramref name="modelFile"/> will be deleted when <see cref="OnnxModel"/> is
140
+ /// no longer needed.</param>
141
+ public OnnxModel ( string modelFile , int ? gpuDeviceId = null , bool fallbackToCpu = false , bool ownModelFile = false )
126
142
{
143
+ ModelFile = modelFile ;
144
+ // If we don't own the model file, _disposed should be false to prevent deleting user's file.
145
+ _ownModelFile = ownModelFile ;
146
+ _disposed = false ;
147
+
127
148
if ( gpuDeviceId != null )
128
149
{
129
150
try
@@ -147,7 +168,7 @@ public OnnxModel(string modelFile, int? gpuDeviceId = null, bool fallbackToCpu =
147
168
148
169
// Load ONNX model file and parse its input and output schema. The reason of doing so is that ONNXRuntime
149
170
// doesn't expose full type information via its C# APIs.
150
- _modelFile = modelFile ;
171
+ ModelFile = modelFile ;
151
172
var model = new OnnxCSharpToProtoWrapper . ModelProto ( ) ;
152
173
using ( var modelStream = File . OpenRead ( modelFile ) )
153
174
model = OnnxCSharpToProtoWrapper . ModelProto . Parser . ParseFrom ( modelStream ) ;
@@ -191,7 +212,9 @@ public OnnxModel(string modelFile, int? gpuDeviceId = null, bool fallbackToCpu =
191
212
}
192
213
193
214
/// <summary>
194
- /// Create an OnnxModel from a byte[]
215
+ /// Create an OnnxModel from a byte[]. Usually, a ONNX model is consumed by <see cref="OnnxModel"/> as a file.
216
+ /// With <see cref="CreateFromBytes(byte[])"/> and <see cref="CreateFromBytes(byte[], int?, bool)"/>, it's possible
217
+ /// to use in-memory model (type: byte[]) to create <see cref="OnnxModel"/>.
195
218
/// </summary>
196
219
/// <param name="modelBytes">Bytes of the serialized model</param>
197
220
/// <returns>OnnxModel</returns>
@@ -202,6 +225,9 @@ public static OnnxModel CreateFromBytes(byte[] modelBytes)
202
225
203
226
/// <summary>
204
227
/// Create an OnnxModel from a byte[]. Set execution to GPU if required.
228
+ /// Usually, a ONNX model is consumed by <see cref="OnnxModel"/> as a file.
229
+ /// With <see cref="CreateFromBytes(byte[])"/> and <see cref="CreateFromBytes(byte[], int?, bool)"/>,
230
+ /// it's possible to use in-memory model (type: byte[]) to create <see cref="OnnxModel"/>.
205
231
/// </summary>
206
232
/// <param name="modelBytes">Bytes of the serialized model.</param>
207
233
/// <param name="gpuDeviceId">GPU device ID to execute on. Null for CPU.</param>
@@ -214,12 +240,7 @@ public static OnnxModel CreateFromBytes(byte[] modelBytes, int? gpuDeviceId = nu
214
240
215
241
var tempModelFile = Path . Combine ( tempModelDir , "model.onnx" ) ;
216
242
File . WriteAllBytes ( tempModelFile , modelBytes ) ;
217
- return new OnnxModel ( tempModelFile , gpuDeviceId , fallbackToCpu ) ;
218
-
219
- // TODO:
220
- // tempModelFile is needed in case the model needs to be saved
221
- // Either have to save the modelbytes and delete the temp dir/file,
222
- // or keep the dir/file and write proper cleanup when application closes
243
+ return new OnnxModel ( tempModelFile , gpuDeviceId , fallbackToCpu , ownModelFile : true ) ;
223
244
}
224
245
225
246
/// <summary>
@@ -233,12 +254,42 @@ public IReadOnlyCollection<NamedOnnxValue> Run(List<NamedOnnxValue> inputNamedOn
233
254
}
234
255
235
256
/// <summary>
236
- /// Convert the model to a byte array.
257
+ /// Flag used to indicate if the unmanaged resources (aka the model file <see cref="ModelFile"/>
258
+ /// and <see cref="_session"/>) have been deleted.
237
259
/// </summary>
238
- /// <returns>byte[]</returns>
239
- public byte [ ] ToByteArray ( )
260
+ private bool _disposed ;
261
+
262
+ public void Dispose ( )
263
+ {
264
+ Dispose ( true ) ;
265
+ GC . SuppressFinalize ( this ) ;
266
+ }
267
+
268
+ /// <summary>
269
+ /// There are two unmanaged resources we can dispose, <see cref="_session"/> and <see cref="ModelFile"/>
270
+ /// if <see cref="_ownModelFile"/> is <see langword="true"/>.
271
+ /// </summary>
272
+ /// <param name="disposing"></param>
273
+ private void Dispose ( bool disposing )
274
+ {
275
+ if ( ! _disposed )
276
+ {
277
+ // There are two things to be disposed.
278
+ if ( disposing )
279
+ {
280
+ // First, we release the resource token by ONNXRuntime.
281
+ _session . Dispose ( ) ;
282
+ // Second, we delete the model file if that file is not created by the user.
283
+ if ( _ownModelFile && File . Exists ( ModelFile ) )
284
+ File . Delete ( ModelFile ) ;
285
+ }
286
+ _disposed = true ;
287
+ }
288
+ }
289
+
290
+ ~ OnnxModel ( )
240
291
{
241
- return File . ReadAllBytes ( _modelFile ) ;
292
+ Dispose ( false ) ;
242
293
}
243
294
}
244
295
0 commit comments