Skip to content

Commit 15eeef7

Browse files
authored
Expose the Onnx runtime option for setting the number of threads (#5962)
1 parent 1dfccca commit 15eeef7

File tree

5 files changed

+142
-28
lines changed

5 files changed

+142
-28
lines changed

src/Microsoft.ML.OnnxTransformer/OnnxCatalog.cs

+13-2
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,19 @@ public static OnnxScoringEstimator ApplyOnnxModel(this TransformsCatalog catalog
9696
string modelFile,
9797
int? gpuDeviceId = null,
9898
bool fallbackToCpu = false)
99-
=> new OnnxScoringEstimator(CatalogUtils.GetEnvironment(catalog), new[] { outputColumnName }, new[] { inputColumnName },
100-
modelFile, gpuDeviceId, fallbackToCpu);
99+
=> new OnnxScoringEstimator(CatalogUtils.GetEnvironment(catalog), new[] { outputColumnName }, new[] { inputColumnName },
100+
modelFile, gpuDeviceId, fallbackToCpu);
101+
102+
/// <summary>
103+
/// Create a <see cref="OnnxScoringEstimator"/> using the specified <see cref="OnnxOptions"/>.
104+
/// Please refer to <see cref="OnnxScoringEstimator"/> to learn more about the necessary dependencies,
105+
/// and how to run it on a GPU.
106+
/// </summary>
107+
/// <param name="catalog">The transform's catalog.</param>
108+
/// <param name="options">Options for the <see cref="OnnxScoringEstimator"/>.</param>
109+
public static OnnxScoringEstimator ApplyOnnxModel(this TransformsCatalog catalog, OnnxOptions options)
110+
=> new OnnxScoringEstimator(CatalogUtils.GetEnvironment(catalog), options.OutputColumns, options.InputColumns, options.ModelFile,
111+
options.GpuDeviceId, options.FallbackToCpu, options.ShapeDictionary, options.RecursionLimit, options.InterOpNumThreads, options.IntraOpNumThreads);
101112

102113
/// <summary>
103114
/// Create a <see cref="OnnxScoringEstimator"/>, which applies a pre-trained Onnx model to the <paramref name="inputColumnName"/> column.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Collections.Generic;
6+
7+
namespace Microsoft.ML.Transforms.Onnx
8+
{
9+
/// <summary>
10+
/// The options for an <see cref="OnnxScoringEstimator"/>.
11+
/// </summary>
12+
public sealed class OnnxOptions
13+
{
14+
/// <summary>
15+
/// Path to the onnx model file.
16+
/// </summary>
17+
public string ModelFile;
18+
19+
/// <summary>
20+
/// Name of the input column.
21+
/// </summary>
22+
public string[] InputColumns;
23+
24+
/// <summary>
25+
/// Name of the output column.
26+
/// </summary>
27+
public string[] OutputColumns;
28+
29+
/// <summary>
30+
/// GPU device id to run on (e.g. 0,1,..). Null for CPU. Requires CUDA 9.1.
31+
/// </summary>
32+
public int? GpuDeviceId = null;
33+
34+
/// <summary>
35+
/// If true, resumes execution on CPU upon GPU error. If false, will raise the GPU exception.
36+
/// </summary>
37+
public bool FallbackToCpu = false;
38+
39+
/// <summary>
40+
/// ONNX shapes to be used over those loaded from <see cref="ModelFile"/>.
41+
/// </summary>
42+
public IDictionary<string, int[]> ShapeDictionary;
43+
44+
/// <summary>
45+
/// Protobuf CodedInputStream recursion limit.
46+
/// </summary>
47+
public int RecursionLimit = 100;
48+
49+
/// <summary>
50+
/// Controls the number of threads used to parallelize the execution of the graph (across nodes).
51+
/// </summary>
52+
public int? InterOpNumThreads = null;
53+
54+
/// <summary>
55+
/// Controls the number of threads to use to run the model.
56+
/// </summary>
57+
public int? IntraOpNumThreads = null;
58+
}
59+
}

src/Microsoft.ML.OnnxTransformer/OnnxTransform.cs

+20-5
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ internal sealed class Options : TransformInputBase
9090

9191
[Argument(ArgumentType.AtMostOnce, HelpText = "Protobuf CodedInputStream recursion limit.", SortOrder = 6)]
9292
public int RecursionLimit = 100;
93+
94+
[Argument(ArgumentType.AtMostOnce, HelpText = "Controls the number of threads used to parallelize the execution of the graph (across nodes).", SortOrder = 7)]
95+
public int? InterOpNumThreads = null;
96+
97+
[Argument(ArgumentType.AtMostOnce, HelpText = "Controls the number of threads to use to run the model.", SortOrder = 8)]
98+
public int? IntraOpNumThreads = null;
9399
}
94100

95101
/// <summary>
@@ -244,7 +250,8 @@ private OnnxTransformer(IHostEnvironment env, Options options, byte[] modelBytes
244250
Host.CheckNonWhiteSpace(options.ModelFile, nameof(options.ModelFile));
245251
Host.CheckIO(File.Exists(options.ModelFile), "Model file {0} does not exists.", options.ModelFile);
246252
// Because we cannot delete the user file, ownModelFile should be false.
247-
Model = new OnnxModel(options.ModelFile, options.GpuDeviceId, options.FallbackToCpu, ownModelFile: false, shapeDictionary: shapeDictionary, options.RecursionLimit);
253+
Model = new OnnxModel(options.ModelFile, options.GpuDeviceId, options.FallbackToCpu, ownModelFile: false, shapeDictionary: shapeDictionary, options.RecursionLimit,
254+
options.InterOpNumThreads, options.IntraOpNumThreads);
248255
}
249256
else
250257
{
@@ -309,8 +316,11 @@ internal OnnxTransformer(IHostEnvironment env, string modelFile, int? gpuDeviceI
309316
/// <param name="fallbackToCpu">If GPU error, raise exception or fallback to CPU.</param>
310317
/// <param name="shapeDictionary"></param>
311318
/// <param name="recursionLimit">Optional, specifies the Protobuf CodedInputStream recursion limit. Default value is 100.</param>
319+
/// <param name="interOpNumThreads">Controls the number of threads used to parallelize the execution of the graph (across nodes).</param>
320+
/// <param name="intraOpNumThreads">Controls the number of threads to use to run the model.</param>
312321
internal OnnxTransformer(IHostEnvironment env, string[] outputColumnNames, string[] inputColumnNames, string modelFile, int? gpuDeviceId = null, bool fallbackToCpu = false,
313-
IDictionary<string, int[]> shapeDictionary = null, int recursionLimit = 100)
322+
IDictionary<string, int[]> shapeDictionary = null, int recursionLimit = 100,
323+
int? interOpNumThreads = null, int? intraOpNumThreads = null)
314324
: this(env, new Options()
315325
{
316326
ModelFile = modelFile,
@@ -319,7 +329,9 @@ internal OnnxTransformer(IHostEnvironment env, string[] outputColumnNames, strin
319329
GpuDeviceId = gpuDeviceId,
320330
FallbackToCpu = fallbackToCpu,
321331
CustomShapeInfos = shapeDictionary?.Select(pair => new CustomShapeInfo(pair.Key, pair.Value)).ToArray(),
322-
RecursionLimit = recursionLimit
332+
RecursionLimit = recursionLimit,
333+
InterOpNumThreads = interOpNumThreads,
334+
IntraOpNumThreads = intraOpNumThreads
323335
})
324336
{
325337
}
@@ -856,9 +868,12 @@ internal OnnxScoringEstimator(IHostEnvironment env, string modelFile, int? gpuDe
856868
/// <param name="fallbackToCpu">If GPU error, raise exception or fallback to CPU.</param>
857869
/// <param name="shapeDictionary"></param>
858870
/// <param name="recursionLimit">Optional, specifies the Protobuf CodedInputStream recursion limit. Default value is 100.</param>
871+
/// <param name="interOpNumThreads">Controls the number of threads used to parallelize the execution of the graph (across nodes).</param>
872+
/// <param name="intraOpNumThreads">Controls the number of threads to use to run the model.</param>
859873
internal OnnxScoringEstimator(IHostEnvironment env, string[] outputColumnNames, string[] inputColumnNames, string modelFile,
860-
int? gpuDeviceId = null, bool fallbackToCpu = false, IDictionary<string, int[]> shapeDictionary = null, int recursionLimit = 100)
861-
: this(env, new OnnxTransformer(env, outputColumnNames, inputColumnNames, modelFile, gpuDeviceId, fallbackToCpu, shapeDictionary, recursionLimit))
874+
int? gpuDeviceId = null, bool fallbackToCpu = false, IDictionary<string, int[]> shapeDictionary = null, int recursionLimit = 100,
875+
int? interOpNumThreads = null, int? intraOpNumThreads = null)
876+
: this(env, new OnnxTransformer(env, outputColumnNames, inputColumnNames, modelFile, gpuDeviceId, fallbackToCpu, shapeDictionary, recursionLimit, interOpNumThreads, intraOpNumThreads))
862877
{
863878
}
864879

src/Microsoft.ML.OnnxTransformer/OnnxUtils.cs

+18-3
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,11 @@ public OnnxVariableInfo(string name, OnnxShape shape, Type typeInOnnxRuntime, Da
165165
/// no longer needed.</param>
166166
/// <param name="shapeDictionary"></param>
167167
/// <param name="recursionLimit">Optional, specifies the Protobuf CodedInputStream recursion limit. Default value is 100.</param>
168+
/// <param name="interOpNumThreads">Controls the number of threads used to parallelize the execution of the graph (across nodes).</param>
169+
/// <param name="intraOpNumThreads">Controls the number of threads to use to run the model.</param>
168170
public OnnxModel(string modelFile, int? gpuDeviceId = null, bool fallbackToCpu = false,
169-
bool ownModelFile = false, IDictionary<string, int[]> shapeDictionary = null, int recursionLimit = 100)
171+
bool ownModelFile = false, IDictionary<string, int[]> shapeDictionary = null, int recursionLimit = 100,
172+
int? interOpNumThreads = null, int? intraOpNumThreads = null)
170173
{
171174
// If we don't own the model file, _disposed should be false to prevent deleting user's file.
172175
_disposed = false;
@@ -181,15 +184,27 @@ public OnnxModel(string modelFile, int? gpuDeviceId = null, bool fallbackToCpu =
181184
catch (OnnxRuntimeException)
182185
{
183186
if (fallbackToCpu)
184-
_session = new InferenceSession(modelFile);
187+
{
188+
var sessionOptions = new SessionOptions()
189+
{
190+
InterOpNumThreads = interOpNumThreads.GetValueOrDefault(),
191+
IntraOpNumThreads = intraOpNumThreads.GetValueOrDefault()
192+
};
193+
_session = new InferenceSession(modelFile, sessionOptions);
194+
}
185195
else
186196
// If called from OnnxTransform, is caught and rethrown
187197
throw;
188198
}
189199
}
190200
else
191201
{
192-
_session = new InferenceSession(modelFile);
202+
var sessionOptions = new SessionOptions()
203+
{
204+
InterOpNumThreads = interOpNumThreads.GetValueOrDefault(),
205+
IntraOpNumThreads = intraOpNumThreads.GetValueOrDefault()
206+
};
207+
_session = new InferenceSession(modelFile, sessionOptions);
193208
}
194209

195210
try

test/Microsoft.ML.OnnxTransformerTest/OnnxTransformTests.cs

+32-18
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Licensed to the .NET Foundation under one or more agreements.
1+
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33
// See the LICENSE file in the project root for more information.
44

@@ -119,8 +119,10 @@ public OnnxTransformTests(ITestOutputHelper output) : base(output)
119119
{
120120
}
121121

122-
[OnnxFact]
123-
public void TestSimpleCase()
122+
[OnnxTheory]
123+
[InlineData(false)]
124+
[InlineData(true)]
125+
public void TestSimpleCase(bool useOptionsCtor)
124126
{
125127
var modelFile = "squeezenet/00000001/model.onnx";
126128
var samplevector = GetSampleArrayData();
@@ -139,7 +141,19 @@ public void TestSimpleCase()
139141
var xyData = new List<TestDataXY> { new TestDataXY() { A = new float[InputSize] } };
140142
var stringData = new List<TestDataDifferntType> { new TestDataDifferntType() { data_0 = new string[InputSize] } };
141143
var sizeData = new List<TestDataSize> { new TestDataSize() { data_0 = new float[2] } };
142-
var pipe = ML.Transforms.ApplyOnnxModel(new[] { "softmaxout_1" }, new[] { "data_0" }, modelFile, gpuDeviceId: _gpuDeviceId, fallbackToCpu: _fallbackToCpu);
144+
var options = new OnnxOptions()
145+
{
146+
OutputColumns = new[] { "softmaxout_1" },
147+
InputColumns = new[] {"data_0" },
148+
ModelFile = modelFile,
149+
GpuDeviceId = _gpuDeviceId,
150+
FallbackToCpu = _fallbackToCpu,
151+
InterOpNumThreads = 1,
152+
IntraOpNumThreads = 1
153+
};
154+
var pipe = useOptionsCtor ?
155+
ML.Transforms.ApplyOnnxModel(options) :
156+
ML.Transforms.ApplyOnnxModel(options.OutputColumns, options.InputColumns, modelFile, gpuDeviceId: _gpuDeviceId, fallbackToCpu: _fallbackToCpu);
143157

144158
var invalidDataWrongNames = ML.Data.LoadFromEnumerable(xyData);
145159
var invalidDataWrongTypes = ML.Data.LoadFromEnumerable(stringData);
@@ -713,14 +727,14 @@ public void TestOnnxModelNotDisposal()
713727

714728
private class OnnxMapInput
715729
{
716-
[OnnxMapType(typeof(int),typeof(float))]
717-
public IDictionary<int,float> Input { get; set; }
730+
[OnnxMapType(typeof(int), typeof(float))]
731+
public IDictionary<int, float> Input { get; set; }
718732
}
719733

720734
private class OnnxMapOutput
721735
{
722-
[OnnxMapType(typeof(int),typeof(float))]
723-
public IDictionary<int,float> Output { get; set; }
736+
[OnnxMapType(typeof(int), typeof(float))]
737+
public IDictionary<int, float> Output { get; set; }
724738
}
725739

726740
/// <summary>
@@ -753,10 +767,10 @@ public void SmokeInMemoryOnnxMapTypeTest()
753767
var transformedDataView = model.Transform(dataView);
754768
var transformedDataPoints = ML.Data.CreateEnumerable<OnnxMapOutput>(transformedDataView, false).ToList();
755769

756-
for(int i = 0; i < dataPoints.Count(); ++i)
770+
for (int i = 0; i < dataPoints.Count(); ++i)
757771
{
758772
Assert.Equal(dataPoints[i].Input.Count(), transformedDataPoints[i].Output.Count());
759-
foreach(var pair in dataPoints[i].Input)
773+
foreach (var pair in dataPoints[i].Input)
760774
Assert.Equal(pair.Value, transformedDataPoints[i].Output[pair.Key + 1]);
761775
}
762776
}
@@ -815,7 +829,7 @@ public void TestOnnxTransformWithCustomShapes()
815829
transformedDataViews[2] = onnxTransformer[2].Transform(dataView);
816830

817831
// Conduct the same check for all the 3 called public APIs.
818-
foreach(var transformedDataView in transformedDataViews)
832+
foreach (var transformedDataView in transformedDataViews)
819833
{
820834
var transformedDataPoints = ML.Data.CreateEnumerable<PredictionWithCustomShape>(transformedDataView, false).ToList();
821835

@@ -901,32 +915,32 @@ public void SpecifyOnnxShapes()
901915
Assert.False(somethingWrong);
902916

903917
// Case 3: this shape conflicts with output shape [1, 1, 1, 5] loaded from the model.
904-
shapeDictionary= new Dictionary<string, int[]>() {
918+
shapeDictionary = new Dictionary<string, int[]>() {
905919
{ "outb", new int[] { 5, 6 } },
906920
};
907-
somethingWrong= false;
921+
somethingWrong = false;
908922
try
909923
{
910924
TryModelWithCustomShapesHelper(shapeDictionary);
911925
}
912926
catch
913927
{
914-
somethingWrong= true;
928+
somethingWrong = true;
915929
}
916930
Assert.True(somethingWrong);
917931

918932
// Case 4: this shape works with output shape [1, 1, 1, 5] loaded from the model.
919-
shapeDictionary= new Dictionary<string, int[]>() {
933+
shapeDictionary = new Dictionary<string, int[]>() {
920934
{ "outb", new int[] { -1, -1, -1, -1 } },
921935
};
922-
somethingWrong= false;
936+
somethingWrong = false;
923937
try
924938
{
925939
TryModelWithCustomShapesHelper(shapeDictionary);
926940
}
927941
catch
928942
{
929-
somethingWrong= true;
943+
somethingWrong = true;
930944
}
931945
Assert.False(somethingWrong);
932946
}
@@ -1024,7 +1038,7 @@ public void TestOnnxTransformSaveAndLoadWithRecursionLimit()
10241038
var pipe = ML.Transforms.LoadImages("data_0", imageFolder, "imagePath")
10251039
.Append(ML.Transforms.ResizeImages("data_0", imageHeight, imageWidth))
10261040
.Append(ML.Transforms.ExtractPixels("data_0", interleavePixelColors: true))
1027-
.Append(ML.Transforms.ApplyOnnxModel(new []{ "softmaxout_1" }, new []{ "data_0" }, modelFile,
1041+
.Append(ML.Transforms.ApplyOnnxModel(new[] { "softmaxout_1" }, new[] { "data_0" }, modelFile,
10281042
gpuDeviceId: _gpuDeviceId, fallbackToCpu: _fallbackToCpu, shapeDictionary: null, recursionLimit: 50));
10291043

10301044
TestEstimatorCore(pipe, data);

0 commit comments

Comments
 (0)