2018-11-20 00:48:22 +00:00
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using System ;
using System.Runtime.InteropServices ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
namespace Microsoft.ML.OnnxRuntime
{
/// <summary>
2018-11-23 04:56:43 +00:00
/// Represents an Inference Session on an ONNX Model
2018-11-20 00:48:22 +00:00
/// </summary>
2019-05-20 22:48:14 +00:00
public class InferenceSession : IDisposable
2018-11-20 00:48:22 +00:00
{
protected IntPtr _nativeHandle ;
2019-10-04 23:38:00 +00:00
protected Dictionary < string , NodeMetadata > _inputMetadata , _outputMetadata , _overridableInitializerMetadata ;
2019-08-14 19:02:02 +00:00
private SessionOptions _builtInSessionOptions = null ;
private RunOptions _builtInRunOptions = null ;
2018-11-20 00:48:22 +00:00
#region Public API
2018-11-26 06:46:21 +00:00
/// <summary>
/// Constructs an InferenceSession from a model file
/// </summary>
/// <param name="modelPath"></param>
2018-11-20 00:48:22 +00:00
public InferenceSession ( string modelPath )
{
2019-08-14 19:02:02 +00:00
_builtInSessionOptions = new SessionOptions ( ) ; // need to be disposed
Init ( modelPath , _builtInSessionOptions ) ;
2018-11-20 00:48:22 +00:00
}
2019-08-14 19:02:02 +00:00
2018-11-26 06:46:21 +00:00
/// <summary>
/// Constructs an InferenceSession from a model file, with some additional session options
/// </summary>
/// <param name="modelPath"></param>
/// <param name="options"></param>
2018-11-20 00:48:22 +00:00
public InferenceSession ( string modelPath , SessionOptions options )
{
2019-08-14 19:02:02 +00:00
Init ( modelPath , options ) ;
2018-11-20 00:48:22 +00:00
}
2019-09-24 18:59:04 +00:00
/// <summary>
/// Constructs an InferenceSession from a model data in byte array
/// </summary>
/// <param name="model"></param>
public InferenceSession ( byte [ ] model )
{
_builtInSessionOptions = new SessionOptions ( ) ; // need to be disposed
Init ( model , _builtInSessionOptions ) ;
}
/// <summary>
/// Constructs an InferenceSession from a model data in byte array, with some additional session options
/// </summary>
/// <param name="model"></param>
/// <param name="options"></param>
public InferenceSession ( byte [ ] model , SessionOptions options )
{
Init ( model , options ) ;
}
2019-08-14 19:02:02 +00:00
/// <summary>
/// Meta data regarding the input nodes, keyed by input names
/// </summary>
2018-11-20 00:48:22 +00:00
public IReadOnlyDictionary < string , NodeMetadata > InputMetadata
{
get
{
2019-05-20 22:48:14 +00:00
return _inputMetadata ;
2018-11-20 00:48:22 +00:00
}
}
2019-08-14 19:02:02 +00:00
/// <summary>
/// Metadata regarding the output nodes, keyed by output names
/// </summary>
2018-11-20 00:48:22 +00:00
public IReadOnlyDictionary < string , NodeMetadata > OutputMetadata
{
get
{
2019-05-20 22:48:14 +00:00
return _outputMetadata ;
2018-11-20 00:48:22 +00:00
}
}
2019-10-04 23:38:00 +00:00
/// <summary>
/// Metadata regarding the overridable initializers, keyed by node names
/// </summary>
public IReadOnlyDictionary < string , NodeMetadata > OverridableInitializerMetadata
{
get
{
return _overridableInitializerMetadata ;
}
}
2019-08-14 19:02:02 +00:00
2018-11-26 06:46:21 +00:00
/// <summary>
/// Runs the loaded model for the given inputs, and fetches all the outputs.
/// </summary>
2020-04-08 18:57:40 +00:00
/// <param name="inputs">specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
2019-08-14 19:02:02 +00:00
/// <returns>Output Tensors in a Collection of NamedOnnxValue. User must dispose the output.</returns>
2019-01-29 05:40:19 +00:00
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > Run ( IReadOnlyCollection < NamedOnnxValue > inputs )
2018-11-23 04:56:43 +00:00
{
2018-11-26 06:46:21 +00:00
string [ ] outputNames = new string [ _outputMetadata . Count ] ;
_outputMetadata . Keys . CopyTo ( outputNames , 0 ) ;
return Run ( inputs , outputNames ) ;
2018-11-23 04:56:43 +00:00
}
2018-11-26 06:46:21 +00:00
/// <summary>
/// Runs the loaded model for the given inputs, and fetches the outputs specified in <paramref name="outputNames"/>.
/// </summary>
2020-04-08 18:57:40 +00:00
/// <param name="inputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names to fetch.</param>
2019-08-14 19:02:02 +00:00
/// <returns>Output Tensors in a Collection of NamedOnnxValue. User must dispose the output.</returns>
2019-01-29 05:40:19 +00:00
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > Run ( IReadOnlyCollection < NamedOnnxValue > inputs , IReadOnlyCollection < string > outputNames )
2018-11-20 00:48:22 +00:00
{
2020-04-08 18:57:40 +00:00
return Run ( inputs , outputNames , _builtInRunOptions ) ;
2018-11-20 00:48:22 +00:00
}
/// <summary>
2020-04-08 18:57:40 +00:00
/// Runs the loaded model for the given inputs, and fetches the specified outputs in <paramref name="outputNames"/>. Uses the given RunOptions for this run.
2018-11-20 00:48:22 +00:00
/// </summary>
2020-04-08 18:57:40 +00:00
/// <param name="inputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names to fetch.</param>
2018-11-20 00:48:22 +00:00
/// <param name="options"></param>
2019-08-14 19:02:02 +00:00
/// <returns>Output Tensors in a Collection of NamedOnnxValue. User must dispose the output.</returns>
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > Run ( IReadOnlyCollection < NamedOnnxValue > inputs , IReadOnlyCollection < string > outputNames , RunOptions options )
2018-11-20 00:48:22 +00:00
{
2020-04-08 18:57:40 +00:00
// prepare inputs
var inputNamesArray = new string [ inputs . Count ] ;
var inputValuesArray = new IntPtr [ inputs . Count ] ;
var pinnedInputBufferHandles = new System . Buffers . MemoryHandle [ inputs . Count ] ;
var disposeInputs = new bool [ inputs . Count ] ;
2018-11-20 00:48:22 +00:00
2019-04-06 05:29:54 +00:00
int inputIndex = 0 ;
2018-11-20 00:48:22 +00:00
foreach ( var input in inputs )
{
2020-04-08 18:57:40 +00:00
inputNamesArray [ inputIndex ] = input . Name ;
2018-11-20 00:48:22 +00:00
2018-11-23 04:56:43 +00:00
// create Tensor from the input if feasible, else throw notsupported exception for now
2020-04-08 18:57:40 +00:00
input . ToNativeOnnxValue (
out inputValuesArray [ inputIndex ] ,
out pinnedInputBufferHandles [ inputIndex ] ,
out disposeInputs [ inputIndex ] ) ;
2018-11-20 00:48:22 +00:00
2019-04-06 05:29:54 +00:00
inputIndex + + ;
2018-11-20 00:48:22 +00:00
}
2020-04-08 18:57:40 +00:00
// prepare outputs
string [ ] outputNamesArray = outputNames as string [ ] ? ? outputNames . ToArray ( ) ;
IntPtr [ ] outputValuesArray = new IntPtr [ outputNames . Count ] ;
2018-11-20 00:48:22 +00:00
2018-12-28 22:53:19 +00:00
IntPtr status = NativeMethods . OrtRun (
2020-04-08 18:57:40 +00:00
_nativeHandle ,
2019-08-22 17:14:50 +00:00
options . Handle ,
2020-04-08 18:57:40 +00:00
inputNamesArray ,
inputValuesArray ,
( UIntPtr ) inputs . Count ,
2018-11-20 00:48:22 +00:00
outputNamesArray ,
2019-05-20 22:48:14 +00:00
( UIntPtr ) outputNames . Count ,
2020-04-08 18:57:40 +00:00
outputValuesArray /* Empty array is passed in to receive output OrtValue pointers */
2018-11-20 00:48:22 +00:00
) ;
try
{
NativeApiStatus . VerifySuccess ( status ) ;
2020-04-08 18:57:40 +00:00
var result = new DisposableList < DisposableNamedOnnxValue > ( outputValuesArray . Length ) ;
for ( int i = 0 ; i < outputValuesArray . Length ; i + + )
2018-11-20 00:48:22 +00:00
{
2020-04-08 18:57:40 +00:00
result . Add ( DisposableNamedOnnxValue . CreateFromOnnxValue ( outputNamesArray [ i ] , outputValuesArray [ i ] ) ) ;
2018-11-20 00:48:22 +00:00
}
return result ;
}
catch ( OnnxRuntimeException e )
{
//clean up the individual output tensors if it is not null;
2020-04-08 18:57:40 +00:00
for ( int i = 0 ; i < outputValuesArray . Length ; i + + )
2018-11-20 00:48:22 +00:00
{
2020-04-08 18:57:40 +00:00
if ( outputValuesArray [ i ] ! = IntPtr . Zero )
2018-11-20 00:48:22 +00:00
{
2020-04-08 18:57:40 +00:00
NativeMethods . OrtReleaseValue ( outputValuesArray [ i ] ) ;
2018-11-20 00:48:22 +00:00
}
}
throw e ;
}
finally
{
2020-04-08 18:57:40 +00:00
for ( int i = 0 ; i < inputs . Count ; i + + )
{
if ( disposeInputs [ i ] )
{
NativeMethods . OrtReleaseValue ( inputValuesArray [ i ] ) ; // For elementary type Tensors, this should not release the buffer, but should delete the native tensor object.
// For string tensors, this releases the native memory allocated for the tensor, including the buffer
pinnedInputBufferHandles [ i ] . Dispose ( ) ;
}
}
}
}
/// <summary>
/// Runs the loaded model for the given inputs, and fetches all the outputs.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
/// <returns>Output Tensors in a Collection of NamedOnnxValue. User must dispose the output.</returns>
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues )
{
string [ ] outputNames = new string [ _outputMetadata . Count ] ;
_outputMetadata . Keys . CopyTo ( outputNames , 0 ) ;
return Run ( inputNames , inputValues , outputNames , _builtInRunOptions ) ;
}
/// <summary>
/// Runs the loaded model for the given inputs, and fetches the outputs specified in <paramref name="outputNames"/>.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names to fetch.</param>
/// <returns>Output Tensors in a Collection of NamedOnnxValue. User must dispose the output.</returns>
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues ,
IReadOnlyCollection < string > outputNames )
{
return Run ( inputNames , inputValues , outputNames , _builtInRunOptions ) ;
}
/// <summary>
/// Runs the loaded model for the given inputs, and fetches the specified outputs in <paramref name="outputNames"/>. Uses the given RunOptions for this run.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names to fetch.</param>
/// <param name="options"></param>
/// <returns>Output Tensors in a Collection of NamedOnnxValue. User must dispose the output.</returns>
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues ,
IReadOnlyCollection < string > outputNames ,
RunOptions options )
{
if ( inputNames . Count ! = inputValues . Count )
{
throw new ArgumentException ( $"Length of {nameof(inputNames)} ({inputNames.Count}) must match that of {nameof(inputValues)} ({inputValues.Count})." ) ;
}
// prepare inputs
string [ ] inputNamesArray = inputNames as string [ ] ? ? inputNames . ToArray ( ) ;
IntPtr [ ] inputValuesArray = new IntPtr [ inputNames . Count ] ;
int inputIndex = 0 ;
foreach ( var input in inputValues )
{
inputValuesArray [ inputIndex ] = input . Value ;
inputIndex + + ;
}
// prepare outputs
string [ ] outputNamesArray = outputNames as string [ ] ? ? outputNames . ToArray ( ) ;
IntPtr [ ] outputValuesArray = new IntPtr [ outputNames . Count ] ;
IntPtr status = NativeMethods . OrtRun (
_nativeHandle ,
options . Handle ,
inputNamesArray ,
inputValuesArray ,
( UIntPtr ) inputNames . Count ,
outputNamesArray ,
( UIntPtr ) outputNames . Count ,
outputValuesArray /* Empty array is passed in to receive output OrtValue pointers */
) ;
try
{
NativeApiStatus . VerifySuccess ( status ) ;
var result = new DisposableList < DisposableNamedOnnxValue > ( outputValuesArray . Length ) ;
for ( int i = 0 ; i < outputValuesArray . Length ; i + + )
{
result . Add ( DisposableNamedOnnxValue . CreateFromOnnxValue ( outputNamesArray [ i ] , outputValuesArray [ i ] ) ) ;
}
return result ;
}
catch ( OnnxRuntimeException e )
{
//clean up the individual output tensors if it is not null;
for ( uint i = 0 ; i < outputValuesArray . Length ; i + + )
{
if ( outputValuesArray [ i ] ! = IntPtr . Zero )
{
NativeMethods . OrtReleaseValue ( outputValuesArray [ i ] ) ;
}
}
throw e ;
}
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs.
///
/// Outputs need to be created with correct type and dimension to accept the fetched data.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names. Should match <paramref name="outputValues"/>.</param>
/// <param name="outputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the output values.</param>
public void Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues ,
IReadOnlyCollection < string > outputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > outputValues )
{
Run ( inputNames , inputValues , outputNames , outputValues , _builtInRunOptions ) ;
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs. Uses the given RunOptions for this run.
///
/// Outputs need to be created with correct type and dimension to accept the fetched data.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names. Should match <paramref name="outputValues"/>.</param>
/// <param name="outputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the output values.</param>
/// <param name="options"></param>
public void Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues ,
IReadOnlyCollection < string > outputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > outputValues ,
RunOptions options )
{
if ( inputNames . Count ! = inputValues . Count )
{
throw new ArgumentException ( $"Length of {nameof(inputNames)} ({inputNames.Count}) must match that of {nameof(inputValues)} ({inputValues.Count})." ) ;
}
if ( outputNames . Count ! = outputValues . Count )
{
throw new ArgumentException ( $"Length of {nameof(outputNames)} ({outputNames.Count}) must match that of {nameof(outputValues)} ({outputValues.Count})." ) ;
}
// prepare inputs
string [ ] inputNamesArray = inputNames as string [ ] ? ? inputNames . ToArray ( ) ;
IntPtr [ ] inputValuesArray = new IntPtr [ inputNames . Count ] ;
int inputIndex = 0 ;
foreach ( var input in inputValues )
{
inputValuesArray [ inputIndex ] = input . Value ;
inputIndex + + ;
}
// prepare outputs
string [ ] outputNamesArray = outputNames as string [ ] ? ? outputNames . ToArray ( ) ;
IntPtr [ ] outputValuesArray = new IntPtr [ outputNames . Count ] ;
int outputIndex = 0 ;
foreach ( var output in outputValues )
{
outputValuesArray [ outputIndex ] = output . Value ;
outputIndex + + ;
}
IntPtr status = NativeMethods . OrtRun (
_nativeHandle ,
options . Handle ,
inputNamesArray ,
inputValuesArray ,
( UIntPtr ) inputNames . Count ,
outputNamesArray ,
( UIntPtr ) outputNames . Count ,
outputValuesArray /* pointers to Pre-allocated OrtValue instances */
) ;
NativeApiStatus . VerifySuccess ( status ) ;
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs.
///
/// Outputs need to be created with correct type and dimension to receive the fetched data.
/// </summary>
/// <param name="inputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
/// <param name="output">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the output values.</param>
public void Run (
IReadOnlyCollection < NamedOnnxValue > inputs ,
IReadOnlyCollection < NamedOnnxValue > outputs )
{
Run ( inputs , outputs , _builtInRunOptions ) ;
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs. Uses the given RunOptions for this run.
///
/// Outputs need to be created with correct type and dimension to receive the fetched data.
/// </summary>
/// <param name="inputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
/// <param name="output">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the output values.</param>
/// <param name="options"></param>
public void Run (
IReadOnlyCollection < NamedOnnxValue > inputs ,
IReadOnlyCollection < NamedOnnxValue > outputs ,
RunOptions options )
{
var inputNamesArray = new string [ inputs . Count ] ;
var inputValuesArray = new IntPtr [ inputs . Count ] ;
var pinnedInputBufferHandles = new System . Buffers . MemoryHandle [ inputs . Count ] ;
var disposeInputs = new bool [ inputs . Count ] ;
var outputNamesArray = new string [ outputs . Count ] ;
var outputValuesArray = new IntPtr [ outputs . Count ] ;
var pinnedOutputBufferHandles = new System . Buffers . MemoryHandle [ outputs . Count ] ;
var disposeOutputs = new bool [ outputs . Count ] ;
try
{
// prepare inputs
int inputIndex = 0 ;
2020-03-24 01:36:12 +00:00
foreach ( var input in inputs )
2018-11-20 00:48:22 +00:00
{
2020-04-08 18:57:40 +00:00
inputNamesArray [ inputIndex ] = input . Name ;
// create native OrtValue from the input if feasible, else throw notsupported exception for now
input . ToNativeOnnxValue (
out inputValuesArray [ inputIndex ] ,
out pinnedInputBufferHandles [ inputIndex ] ,
out disposeInputs [ inputIndex ] ) ;
inputIndex + + ;
}
// prepare outputs
int outputIndex = 0 ;
foreach ( var output in outputs )
{
outputNamesArray [ outputIndex ] = output . Name ;
// create native OrtValue from the output if feasible, else throw notsupported exception for now
output . ToNativeOnnxValue (
out outputValuesArray [ outputIndex ] ,
out pinnedOutputBufferHandles [ outputIndex ] ,
out disposeOutputs [ outputIndex ] ) ;
outputIndex + + ;
}
IntPtr status = NativeMethods . OrtRun (
_nativeHandle ,
options . Handle ,
inputNamesArray ,
inputValuesArray ,
( UIntPtr ) inputs . Count ,
outputNamesArray ,
( UIntPtr ) outputs . Count ,
outputValuesArray /* pointers to Pre-allocated OrtValue instances */
) ;
NativeApiStatus . VerifySuccess ( status ) ;
}
finally
{
for ( int i = 0 ; i < inputs . Count ; i + + )
{
if ( disposeInputs [ i ] )
{
NativeMethods . OrtReleaseValue ( inputValuesArray [ i ] ) ; // For elementary type Tensors, this should not release the buffer, but should delete the native tensor object.
// For string tensors, this releases the native memory allocated for the tensor, including the buffer
pinnedInputBufferHandles [ i ] . Dispose ( ) ;
}
}
for ( int i = 0 ; i < outputs . Count ; i + + )
{
if ( disposeOutputs [ i ] )
2020-03-24 01:36:12 +00:00
{
2020-04-08 18:57:40 +00:00
NativeMethods . OrtReleaseValue ( outputValuesArray [ i ] ) ; // For elementary type Tensors, this should not release the buffer, but should delete the native tensor object.
// For string tensors, this releases the native memory allocated for the tensor, including the buffer
pinnedOutputBufferHandles [ i ] . Dispose ( ) ;
2020-03-24 01:36:12 +00:00
}
2020-04-08 18:57:40 +00:00
}
}
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs.
///
/// Outputs need to be created with correct type and dimension to receive the fetched data.
/// </summary>
/// <param name="inputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names. Should match <paramref name="outputValues"/>.</param>
/// <param name="outputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the output values.</param>
public void Run (
IReadOnlyCollection < NamedOnnxValue > inputs ,
IReadOnlyCollection < string > outputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > outputValues )
{
Run ( inputs , outputNames , outputValues , _builtInRunOptions ) ;
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs. Uses the given RunOptions for this run.
///
/// Outputs need to be created with correct type and dimension to receive the fetched data.
/// </summary>
/// <param name="inputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names. Should match <paramref name="outputValues"/>.</param>
/// <param name="outputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the output values.</param>
/// <param name="options"></param>
public void Run (
IReadOnlyCollection < NamedOnnxValue > inputs ,
IReadOnlyCollection < string > outputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > outputValues ,
RunOptions options )
{
if ( outputNames . Count ! = outputValues . Count )
{
throw new ArgumentException ( $"Length of {nameof(outputNames)} ({outputNames.Count}) must match that of {nameof(outputValues)} ({outputValues.Count})." ) ;
}
var inputNamesArray = new string [ inputs . Count ] ;
var inputValuesArray = new IntPtr [ inputs . Count ] ;
var pinnedInputBufferHandles = new System . Buffers . MemoryHandle [ inputs . Count ] ;
var disposeInputs = new bool [ inputs . Count ] ;
try
{
// prepare inputs
int inputIndex = 0 ;
foreach ( var input in inputs )
{
inputNamesArray [ inputIndex ] = input . Name ;
// create native OrtValue from the input if feasible, else throw notsupported exception for now
input . ToNativeOnnxValue (
out inputValuesArray [ inputIndex ] ,
out pinnedInputBufferHandles [ inputIndex ] ,
out disposeInputs [ inputIndex ] ) ;
2020-03-24 01:36:12 +00:00
inputIndex + + ;
2018-11-20 00:48:22 +00:00
}
2020-04-08 18:57:40 +00:00
// prepare outputs
string [ ] outputNamesArray = outputNames as string [ ] ? ? outputNames . ToArray ( ) ;
IntPtr [ ] outputValuesArray = new IntPtr [ outputNames . Count ] ;
int outputIndex = 0 ;
foreach ( var output in outputValues )
{
outputValuesArray [ outputIndex ] = output . Value ;
outputIndex + + ;
}
IntPtr status = NativeMethods . OrtRun (
_nativeHandle ,
options . Handle ,
inputNamesArray ,
inputValuesArray ,
( UIntPtr ) inputs . Count ,
outputNamesArray ,
( UIntPtr ) outputNames . Count ,
outputValuesArray /* pointers to Pre-allocated OrtValue instances */
) ;
NativeApiStatus . VerifySuccess ( status ) ;
}
finally
{
for ( int i = 0 ; i < inputs . Count ; i + + )
{
if ( disposeInputs [ i ] )
{
NativeMethods . OrtReleaseValue ( inputValuesArray [ i ] ) ; // For elementary type Tensors, this should not release the buffer, but should delete the native tensor object.
// For string tensors, this releases the native memory allocated for the tensor, including the buffer
pinnedInputBufferHandles [ i ] . Dispose ( ) ;
}
}
}
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs.
///
/// Outputs need to be created with correct type and dimension to receive the fetched data.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
/// <param name="output">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the output values.</param>
public void Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues ,
IReadOnlyCollection < NamedOnnxValue > outputs )
{
Run ( inputNames , inputValues , outputs , _builtInRunOptions ) ;
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs. Uses the given RunOptions for this run.
///
/// Outputs need to be created with correct type and dimension to receive the fetched data.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
/// <param name="output">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the output values.</param>
/// <param name="options"></param>
public void Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues ,
IReadOnlyCollection < NamedOnnxValue > outputs ,
RunOptions options )
{
if ( inputNames . Count ! = inputValues . Count )
{
throw new ArgumentException ( $"Length of {nameof(inputNames)} ({inputNames.Count}) must match that of {nameof(inputValues)} ({inputValues.Count})." ) ;
2018-11-20 00:48:22 +00:00
}
2019-05-20 22:48:14 +00:00
2020-04-08 18:57:40 +00:00
var outputNamesArray = new string [ outputs . Count ] ;
var outputValuesArray = new IntPtr [ outputs . Count ] ;
var pinnedOutputBufferHandles = new System . Buffers . MemoryHandle [ outputs . Count ] ;
var disposeOutputs = new bool [ outputs . Count ] ;
try
{
// prepare inputs
string [ ] inputNamesArray = inputNames as string [ ] ? ? inputNames . ToArray ( ) ;
IntPtr [ ] inputValuesArray = new IntPtr [ inputNames . Count ] ;
int inputIndex = 0 ;
foreach ( var input in inputValues )
{
inputValuesArray [ inputIndex ] = input . Value ;
inputIndex + + ;
}
// prepare outputs
int outputIndex = 0 ;
foreach ( var output in outputs )
{
outputNamesArray [ outputIndex ] = output . Name ;
// create native OrtValue from the output if feasible, else throw notsupported exception for now
output . ToNativeOnnxValue (
out outputValuesArray [ outputIndex ] ,
out pinnedOutputBufferHandles [ outputIndex ] ,
out disposeOutputs [ outputIndex ] ) ;
outputIndex + + ;
}
IntPtr status = NativeMethods . OrtRun (
_nativeHandle ,
options . Handle ,
inputNamesArray ,
inputValuesArray ,
( UIntPtr ) inputNames . Count ,
outputNamesArray ,
( UIntPtr ) outputs . Count ,
outputValuesArray /* pointers to Pre-allocated OrtValue instances */
) ;
NativeApiStatus . VerifySuccess ( status ) ;
}
finally
{
for ( int i = 0 ; i < outputs . Count ; i + + )
{
if ( disposeOutputs [ i ] )
{
NativeMethods . OrtReleaseValue ( outputValuesArray [ i ] ) ; // For elementary type Tensors, this should not release the buffer, but should delete the native tensor object.
// For string tensors, this releases the native memory allocated for the tensor, including the buffer
pinnedOutputBufferHandles [ i ] . Dispose ( ) ;
}
}
}
2018-11-20 00:48:22 +00:00
}
2020-04-08 18:57:40 +00:00
2018-11-26 06:46:21 +00:00
//TODO: kept internal until implemented
internal ModelMetadata ModelMetadata
{
get
{
return new ModelMetadata ( ) ; //TODO: implement
}
}
2018-11-20 00:48:22 +00:00
#endregion
#region private methods
2019-08-14 19:02:02 +00:00
2019-09-24 18:59:04 +00:00
private void Init ( string modelPath , SessionOptions options )
2019-08-14 19:02:02 +00:00
{
var envHandle = OnnxRuntime . Handle ;
2019-09-24 18:59:04 +00:00
var session = IntPtr . Zero ;
2019-10-25 03:39:16 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtCreateSession ( envHandle , NativeMethods . GetPlatformSerializedString ( modelPath ) , options . Handle , out session ) ) ;
2019-08-14 19:02:02 +00:00
2019-09-24 18:59:04 +00:00
InitWithSessionHandle ( session , options ) ;
}
private void Init ( byte [ ] modelData , SessionOptions options )
{
var envHandle = OnnxRuntime . Handle ;
var session = IntPtr . Zero ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtCreateSessionFromArray ( envHandle , modelData , ( UIntPtr ) modelData . Length , options . Handle , out session ) ) ;
InitWithSessionHandle ( session , options ) ;
}
/// <summary>
/// Initializes the session object with a native session handle
/// </summary>
/// <param name="session">Handle of a native session object</param>
/// <param name="options">Session options</param>
private void InitWithSessionHandle ( IntPtr session , SessionOptions options )
{
_nativeHandle = session ;
2019-08-14 19:02:02 +00:00
try
{
// Initialize input/output metadata
_inputMetadata = new Dictionary < string , NodeMetadata > ( ) ;
_outputMetadata = new Dictionary < string , NodeMetadata > ( ) ;
2019-10-04 23:38:00 +00:00
_overridableInitializerMetadata = new Dictionary < string , NodeMetadata > ( ) ;
2019-08-14 19:02:02 +00:00
// get input count
UIntPtr inputCount = UIntPtr . Zero ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetInputCount ( _nativeHandle , out inputCount ) ) ;
2019-10-04 23:38:00 +00:00
// get all the input names and metadata
2019-08-14 19:02:02 +00:00
for ( ulong i = 0 ; i < ( ulong ) inputCount ; i + + )
{
var iname = GetInputName ( i ) ;
_inputMetadata [ iname ] = GetInputMetadata ( i ) ;
}
// get output count
UIntPtr outputCount = UIntPtr . Zero ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetOutputCount ( _nativeHandle , out outputCount ) ) ;
2019-10-04 23:38:00 +00:00
// get all the output names and metadata
2019-08-14 19:02:02 +00:00
for ( ulong i = 0 ; i < ( ulong ) outputCount ; i + + )
{
_outputMetadata [ GetOutputName ( i ) ] = GetOutputMetadata ( i ) ;
}
2019-10-04 23:38:00 +00:00
// get overridable initializer count
UIntPtr initilaizerCount = UIntPtr . Zero ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetOverridableInitializerCount ( _nativeHandle , out initilaizerCount ) ) ;
// get all the overridable initializer names and metadata
for ( ulong i = 0 ; i < ( ulong ) initilaizerCount ; i + + )
{
_overridableInitializerMetadata [ GetOverridableInitializerName ( i ) ] = GetOverridableInitializerMetadata ( i ) ;
}
2019-08-14 19:02:02 +00:00
}
catch ( OnnxRuntimeException e )
{
if ( _nativeHandle ! = IntPtr . Zero )
{
NativeMethods . OrtReleaseSession ( _nativeHandle ) ;
_nativeHandle = IntPtr . Zero ;
}
throw e ;
}
_builtInRunOptions = new RunOptions ( ) ; // create a default built-in run option, and avoid creating a new one every run() call
}
2018-11-20 00:48:22 +00:00
private string GetOutputName ( ulong index )
{
IntPtr nameHandle = IntPtr . Zero ;
string str = null ;
2019-05-20 22:48:14 +00:00
IntPtr status = NativeMethods . OrtSessionGetOutputName (
2018-11-20 00:48:22 +00:00
_nativeHandle ,
2019-05-20 22:48:14 +00:00
( UIntPtr ) index ,
2018-11-20 00:48:22 +00:00
NativeMemoryAllocator . DefaultInstance . Handle ,
out nameHandle ) ;
try
{
NativeApiStatus . VerifySuccess ( status ) ;
str = Marshal . PtrToStringAnsi ( nameHandle ) ; //assumes charset = ANSI
}
2019-05-20 22:48:14 +00:00
finally
2018-11-20 00:48:22 +00:00
{
if ( nameHandle ! = IntPtr . Zero )
{
NativeMemoryAllocator . DefaultInstance . FreeMemory ( nameHandle ) ;
}
}
return str ;
}
private string GetInputName ( ulong index )
{
IntPtr nameHandle = IntPtr . Zero ;
string str = null ;
2018-12-28 22:53:19 +00:00
IntPtr status = NativeMethods . OrtSessionGetInputName (
2018-11-20 00:48:22 +00:00
_nativeHandle ,
2019-05-20 22:48:14 +00:00
( UIntPtr ) index ,
2018-11-20 00:48:22 +00:00
NativeMemoryAllocator . DefaultInstance . Handle ,
out nameHandle ) ;
try
{
2019-05-20 22:48:14 +00:00
2018-11-20 00:48:22 +00:00
NativeApiStatus . VerifySuccess ( status ) ;
str = Marshal . PtrToStringAnsi ( nameHandle ) ; //assumes charset = ANSI
}
finally
{
if ( nameHandle ! = IntPtr . Zero )
{
NativeMemoryAllocator . DefaultInstance . FreeMemory ( nameHandle ) ;
}
}
return str ;
}
2019-10-04 23:38:00 +00:00
private string GetOverridableInitializerName ( ulong index )
{
IntPtr nameHandle = IntPtr . Zero ;
string str = null ;
IntPtr status = NativeMethods . OrtSessionGetOverridableInitializerName (
_nativeHandle ,
( UIntPtr ) index ,
NativeMemoryAllocator . DefaultInstance . Handle ,
out nameHandle ) ;
try
{
NativeApiStatus . VerifySuccess ( status ) ;
str = Marshal . PtrToStringAnsi ( nameHandle ) ; //assumes charset = ANSI
}
finally
{
if ( nameHandle ! = IntPtr . Zero )
{
NativeMemoryAllocator . DefaultInstance . FreeMemory ( nameHandle ) ;
}
}
return str ;
}
2018-11-20 00:48:22 +00:00
2018-11-23 04:56:43 +00:00
private NodeMetadata GetInputMetadata ( ulong index )
{
IntPtr typeInfo = IntPtr . Zero ;
try
{
2019-05-20 22:48:14 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetInputTypeInfo ( _nativeHandle , ( UIntPtr ) index , out typeInfo ) ) ;
2018-11-23 04:56:43 +00:00
return GetMetadataFromTypeInfo ( typeInfo ) ;
}
finally
{
if ( typeInfo ! = IntPtr . Zero )
{
2019-01-26 03:41:10 +00:00
NativeMethods . OrtReleaseTypeInfo ( typeInfo ) ;
2018-11-23 04:56:43 +00:00
}
}
}
2018-11-20 00:48:22 +00:00
2018-11-23 04:56:43 +00:00
private NodeMetadata GetOutputMetadata ( ulong index )
{
IntPtr typeInfo = IntPtr . Zero ;
try
{
2019-05-20 22:48:14 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetOutputTypeInfo ( _nativeHandle , ( UIntPtr ) index , out typeInfo ) ) ;
2018-11-23 04:56:43 +00:00
return GetMetadataFromTypeInfo ( typeInfo ) ;
}
finally
{
if ( typeInfo ! = IntPtr . Zero )
2019-10-04 23:38:00 +00:00
{
NativeMethods . OrtReleaseTypeInfo ( typeInfo ) ;
}
}
}
private NodeMetadata GetOverridableInitializerMetadata ( ulong index )
{
IntPtr typeInfo = IntPtr . Zero ;
try
{
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetOverridableInitializerTypeInfo ( _nativeHandle , ( UIntPtr ) index , out typeInfo ) ) ;
return GetMetadataFromTypeInfo ( typeInfo ) ;
}
finally
{
if ( typeInfo ! = IntPtr . Zero )
2018-11-23 04:56:43 +00:00
{
2019-01-26 03:41:10 +00:00
NativeMethods . OrtReleaseTypeInfo ( typeInfo ) ;
2018-11-23 04:56:43 +00:00
}
}
}
2019-03-06 00:00:40 +00:00
internal static NodeMetadata GetMetadataFromTypeInfo ( IntPtr typeInfo )
2018-11-23 04:56:43 +00:00
{
2019-06-11 01:36:04 +00:00
OnnxValueType valueType ;
unsafe
{
2019-07-30 01:35:28 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtGetOnnxTypeFromTypeInfo ( typeInfo , new IntPtr ( & valueType ) ) ) ;
2019-06-11 01:36:04 +00:00
}
2019-03-12 17:11:14 +00:00
if ( valueType ! = OnnxValueType . ONNX_TYPE_TENSOR & & valueType ! = OnnxValueType . ONNX_TYPE_SPARSETENSOR )
{
2020-03-24 01:36:12 +00:00
return new NodeMetadata ( valueType , new int [ ] { } , new string [ ] { } , typeof ( NamedOnnxValue ) ) ;
2019-03-12 17:11:14 +00:00
}
2019-06-11 01:36:04 +00:00
IntPtr tensorInfo ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtCastTypeInfoToTensorInfo ( typeInfo , out tensorInfo ) ) ; //(IntPtr)(int)(uint)
2019-03-06 00:00:40 +00:00
// Convert the newly introduced OrtTypeInfo* to the older OrtTypeAndShapeInfo*
if ( tensorInfo = = IntPtr . Zero )
return null ;
2018-11-23 04:56:43 +00:00
2019-06-11 01:36:04 +00:00
TensorElementType type ;
unsafe
{
NativeApiStatus . VerifySuccess ( NativeMethods . OrtGetTensorElementType ( tensorInfo , new IntPtr ( & type ) ) ) ;
}
2018-11-23 04:56:43 +00:00
Type dotnetType = null ;
int width = 0 ;
2019-05-20 22:48:14 +00:00
TensorElementTypeConverter . GetTypeAndWidth ( type , out dotnetType , out width ) ;
2019-06-11 01:36:04 +00:00
UIntPtr numDimensions ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtGetDimensionsCount ( tensorInfo , out numDimensions ) ) ;
2019-10-12 22:46:28 +00:00
2018-11-23 04:56:43 +00:00
long [ ] dimensions = new long [ ( int ) numDimensions ] ;
2019-10-12 22:46:28 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtGetDimensions ( tensorInfo , dimensions , numDimensions ) ) ;
2018-11-23 04:56:43 +00:00
int [ ] intDimensions = new int [ ( int ) numDimensions ] ;
2019-05-20 22:48:14 +00:00
for ( var i = 0 ; i < ( long ) numDimensions ; i + + )
2018-11-23 04:56:43 +00:00
{
intDimensions [ i ] = ( int ) dimensions [ i ] ;
}
2019-10-12 22:46:28 +00:00
IntPtr [ ] dimensionNamePtrs = new IntPtr [ ( int ) numDimensions ] ;
NativeApiStatus . VerifySuccess (
NativeMethods . OrtGetSymbolicDimensions ( tensorInfo , dimensionNamePtrs , numDimensions ) ) ;
string [ ] symbolicDimensions = new string [ ( int ) numDimensions ] ;
for ( var i = 0 ; i < ( int ) numDimensions ; i + + )
{
symbolicDimensions [ i ] = Marshal . PtrToStringAnsi ( dimensionNamePtrs [ i ] ) ; //assumes charset = ANSI
}
2020-03-24 01:36:12 +00:00
2019-10-12 22:46:28 +00:00
return new NodeMetadata ( valueType , intDimensions , symbolicDimensions , dotnetType ) ;
2018-11-23 04:56:43 +00:00
}
#endregion
2018-11-20 00:48:22 +00:00
#region destructors disposers
~ InferenceSession ( )
{
Dispose ( false ) ;
}
public void Dispose ( )
{
GC . SuppressFinalize ( this ) ;
Dispose ( true ) ;
}
protected virtual void Dispose ( bool disposing )
{
if ( disposing )
{
// cleanup managed resources
2019-08-14 19:02:02 +00:00
if ( _builtInSessionOptions ! = null )
{
_builtInSessionOptions . Dispose ( ) ;
}
if ( _builtInRunOptions ! = null )
{
_builtInRunOptions . Dispose ( ) ;
}
2018-11-20 00:48:22 +00:00
}
// cleanup unmanaged resources
if ( _nativeHandle ! = IntPtr . Zero )
{
2018-12-18 19:39:46 +00:00
NativeMethods . OrtReleaseSession ( _nativeHandle ) ;
2018-11-20 00:48:22 +00:00
}
}
#endregion
}
2018-11-23 04:56:43 +00:00
/// <summary>
/// Resembles type and shape information of session-graph nodes, used for communicating the shape/type of input/output nodes
/// </summary>
public class NodeMetadata
2018-11-20 00:48:22 +00:00
{
2019-03-12 17:11:14 +00:00
private OnnxValueType _onnxValueType ;
2018-11-23 04:56:43 +00:00
private int [ ] _dimensions ;
2019-10-12 22:46:28 +00:00
private string [ ] _symbolicDimensions ;
2018-11-23 04:56:43 +00:00
private Type _type ;
2019-10-12 22:46:28 +00:00
internal NodeMetadata ( OnnxValueType onnxValueType , int [ ] dimensions , string [ ] symbolicDimensions , Type type )
2018-11-20 00:48:22 +00:00
{
2019-03-12 17:11:14 +00:00
_onnxValueType = onnxValueType ;
2018-11-23 04:56:43 +00:00
_dimensions = dimensions ;
2019-10-12 22:46:28 +00:00
_symbolicDimensions = symbolicDimensions ;
2018-11-23 04:56:43 +00:00
_type = type ;
}
2019-03-12 17:11:14 +00:00
public OnnxValueType OnnxValueType
{
get
{
return _onnxValueType ;
}
}
2018-11-23 04:56:43 +00:00
public int [ ] Dimensions
{
get
{
return _dimensions ;
}
2018-11-20 00:48:22 +00:00
}
2019-10-12 22:46:28 +00:00
public string [ ] SymbolicDimensions
{
get
{
return _symbolicDimensions ;
}
}
2018-11-26 06:46:21 +00:00
public System . Type ElementType
2018-11-20 00:48:22 +00:00
{
2018-11-23 04:56:43 +00:00
get
{
return _type ;
}
2018-11-20 00:48:22 +00:00
}
2018-11-26 06:46:21 +00:00
public bool IsTensor
{
get
{
return true ; // currently only Tensor nodes are supported
}
}
2018-11-20 00:48:22 +00:00
}
2019-05-20 22:48:14 +00:00
internal class ModelMetadata
2018-11-20 00:48:22 +00:00
{
2018-11-26 06:46:21 +00:00
//TODO: placeholder for Model metadata. Currently C-API does not expose this.
2018-11-20 00:48:22 +00:00
}
2018-11-23 04:56:43 +00:00
2018-11-20 00:48:22 +00:00
}