onnxruntime/csharp/test/Microsoft.ML.OnnxRuntime.Tests.Common/OrtIoBindingAllocationTest.cs
raoanag 424107a82a
Merge main to WindowsAI (#18122)
### Description
Merge main to WindowsAI



### Motivation and Context
<!-- - Why is this change required? What problem does it solve?
- If it fixes an open issue, please link to the issue here. -->

---------

Signed-off-by: Nash <george.nash@intel.com>
Signed-off-by: Yiming Hu <yiming.hu@amd.com>
Signed-off-by: Liqun Fu <liqfu@microsoft.com>
Co-authored-by: Kaz Nishimura <kazssym@linuxfront.com>
Co-authored-by: Tianlei Wu <tlwu@microsoft.com>
Co-authored-by: Nat Kershaw (MSFT) <nakersha@microsoft.com>
Co-authored-by: Yulong Wang <7679871+fs-eire@users.noreply.github.com>
Co-authored-by: Changming Sun <chasun@microsoft.com>
Co-authored-by: zesongw <zesong.wang@intel.com>
Co-authored-by: Yi Zhang <zhanyi@microsoft.com>
Co-authored-by: Dmitri Smirnov <yuslepukhin@users.noreply.github.com>
Co-authored-by: Yifan Li <109183385+yf711@users.noreply.github.com>
Co-authored-by: simonjub <78098752+simonjub@users.noreply.github.com>
Co-authored-by: PeixuanZuo <94887879+PeixuanZuo@users.noreply.github.com>
Co-authored-by: Adrian Lizarraga <adlizarraga@microsoft.com>
Co-authored-by: Edward Chen <18449977+edgchen1@users.noreply.github.com>
Co-authored-by: Arthur Islamov <arthur@islamov.ai>
Co-authored-by: Jambay Kinley <jambaykinley@microsoft.com>
Co-authored-by: Justin Chu <justinchuby@users.noreply.github.com>
Co-authored-by: Wei-Sheng Chin <wschin@outlook.com>
Co-authored-by: Bowen Bao <bowbao@microsoft.com>
Co-authored-by: Hariharan Seshadri <shariharan91@gmail.com>
Co-authored-by: Numfor Tiapo <numsmt2@gmail.com>
Co-authored-by: Vincent Wang <wangwchpku@outlook.com>
Co-authored-by: Pranav Sharma <prs@microsoft.com>
Co-authored-by: George Nash <george.nash@intel.com>
Co-authored-by: Abhishek Jindal <abjindal@microsoft.com>
Co-authored-by: pengwa <pengwa@microsoft.com>
Co-authored-by: Yiming Hu <woinck@users.noreply.github.com>
Co-authored-by: Jiajia Qin <jiajia.qin@intel.com>
Co-authored-by: Lukas Berbuer <36054362+lukasberbuer@users.noreply.github.com>
Co-authored-by: Wanming Lin <wanming.lin@intel.com>
Co-authored-by: Xavier Dupré <xadupre@users.noreply.github.com>
Co-authored-by: aimilefth <60664743+aimilefth@users.noreply.github.com>
Co-authored-by: Baiju Meswani <bmeswani@microsoft.com>
Co-authored-by: Adam Pocock <adam.pocock@oracle.com>
Co-authored-by: Chi Lo <54722500+chilo-ms@users.noreply.github.com>
Co-authored-by: RandySheriffH <48490400+RandySheriffH@users.noreply.github.com>
Co-authored-by: Randy Shuai <rashuai@microsoft.com>
Co-authored-by: Vadym Stupakov <vadim.stupakov@gmail.com>
Co-authored-by: Jian Chen <cjian@microsoft.com>
Co-authored-by: Brian Lambert <98757707+brian-pieces@users.noreply.github.com>
Co-authored-by: Nicolò Lucchesi <nicolo.lucchesi@gmail.com>
Co-authored-by: liqun Fu <liqfu@microsoft.com>
Co-authored-by: trajep <trajepl@gmail.com>
Co-authored-by: Scott McKay <skottmckay@gmail.com>
Co-authored-by: Mustafa Ateş Uzun <mustafauzun0@gmail.com>
Co-authored-by: MistEO <mistereo@hotmail.com>
Co-authored-by: satyajandhyala <satya.k.jandhyala@gmail.com>
Co-authored-by: shaahji <96227573+shaahji@users.noreply.github.com>
Co-authored-by: Rachel Guo <35738743+YUNQIUGUO@users.noreply.github.com>
Co-authored-by: rachguo <rachguo@rachguos-Mini.attlocal.net>
Co-authored-by: Caroline Zhu <wolfivyaura@gmail.com>
Co-authored-by: Caroline Zhu <carolinezhu@microsoft.com>
Co-authored-by: Guenther Schmuelling <guschmue@microsoft.com>
Co-authored-by: xhcao <xinghua.cao@intel.com>
Co-authored-by: Ella Charlaix <80481427+echarlaix@users.noreply.github.com>
Co-authored-by: Xu Xing <xing.xu@intel.com>
Co-authored-by: Hector Li <hecli@microsoft.com>
Co-authored-by: Ye Wang <52801275+wangyems@users.noreply.github.com>
Co-authored-by: Your Name <you@example.com>
Co-authored-by: Benedikt Hilmes <benedikt.hilmes@rwth-aachen.de>
Co-authored-by: rachguo <rachguo@rachguos-Mac-mini.local>
Co-authored-by: George Wu <jywu@microsoft.com>
Co-authored-by: JiCheng <wejoncy@163.com>
Co-authored-by: Sheil Kumar <smk2007@gmail.com>
Co-authored-by: Sheil Kumar <sheilk@microsoft.com>
Co-authored-by: cloudhan <guangyunhan@microsoft.com>
Co-authored-by: kyoshisuki <143475866+kyoshisuki@users.noreply.github.com>
Co-authored-by: aciddelgado <139922440+aciddelgado@users.noreply.github.com>
Co-authored-by: tlwu@microsoft.com <tlwu@a100.crj0ad2y1kku1j4yxl4sj10o4e.gx.internal.cloudapp.net>
Co-authored-by: Maximilian Müller <44298237+gedoensmax@users.noreply.github.com>
Co-authored-by: Tang, Cheng <souptc@gmail.com>
Co-authored-by: Cheng Tang <chenta@microsoft.com@orttrainingdev9.d32nl1ml4oruzj4qz3bqlggovf.px.internal.cloudapp.net>
Co-authored-by: Cheng Tang <chenta@microsoft.com>
Co-authored-by: Jeff Daily <jeff.daily@amd.com>
Co-authored-by: cloudhan <cloudhan@outlook.com>
Co-authored-by: Yufeng Li <liyufeng1987@gmail.com>
Co-authored-by: Zhang Lei <zhang.huanning@hotmail.com>
Co-authored-by: Dwayne Robinson <fdwr@hotmail.com>
Co-authored-by: Zhipeng Han <zhipeng.han@outlook.com>
Co-authored-by: Thiago Crepaldi <thiago.crepaldi@microsoft.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Patrice Vignola <vignola.patrice@gmail.com>
Co-authored-by: kunal-vaishnavi <115581922+kunal-vaishnavi@users.noreply.github.com>
Co-authored-by: snadampal <87143774+snadampal@users.noreply.github.com>
Co-authored-by: Sumit Agarwal <sumitagarwal330@gmail.com>
Co-authored-by: Ashwini Khade <askhade@microsoft.com>
Co-authored-by: Yang Gu <yang.gu@intel.com>
Co-authored-by: Cheng Tang <chenta@a100.crj0ad2y1kku1j4yxl4sj10o4e.gx.internal.cloudapp.net>
Co-authored-by: mindest <30493312+mindest@users.noreply.github.com>
Co-authored-by: Scott McKay <Scott.McKay@microsoft.com>
Co-authored-by: Xavier Dupre <xadupre@microsoft.com@orttrainingdev9.d32nl1ml4oruzj4qz3bqlggovf.px.internal.cloudapp.net>
Co-authored-by: guyang3532 <62738430+guyang3532@users.noreply.github.com>
Co-authored-by: Carson M <carson@pyke.io>
Co-authored-by: sophies927 <107952697+sophies927@users.noreply.github.com>
2023-10-27 17:08:01 -07:00

389 lines
No EOL
15 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
using Microsoft.ML.OnnxRuntime.Tensors;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using Xunit;
using static Microsoft.ML.OnnxRuntime.Tests.InferenceTest;
namespace Microsoft.ML.OnnxRuntime.Tests
{
[Collection("OrtBinding Tests")]
public class OrtIoBindingAllocationTests : IDisposable
{
private const string _inputName = "data_0";
private const string _outputName = "softmaxout_1";
private static readonly OrtAllocator _allocator = OrtAllocator.DefaultInstance;
private readonly RunOptions _runOptions;
private readonly InferenceSession _session;
private readonly float[] _inputData;
private readonly float[] _outputData;
private readonly long[] _inputShape;
private readonly long _inputSizeInBytes;
private readonly long[] _outputShape;
private readonly long _outputSizeInBytes;
private readonly OrtSafeMemoryHandle _inputNativeAllocation;
private readonly OrtSafeMemoryHandle _outputNativeAllocation;
private readonly DisposableListTest<IDisposable> _dispList = new DisposableListTest<IDisposable>();
private bool _disposed = false;
public OrtIoBindingAllocationTests()
{
var tuple = OpenSessionSqueezeNet();
_session = tuple.Item1;
_dispList.Add(_session);
_runOptions = new RunOptions();
_dispList.Add(_runOptions);
_inputData = tuple.Item2;
_outputData = tuple.Item4;
var inputMeta = _session.InputMetadata;
var outputMeta = _session.OutputMetadata;
_inputShape = Array.ConvertAll<int, long>(inputMeta[_inputName].Dimensions, Convert.ToInt64);
_outputShape = Array.ConvertAll<int, long>(outputMeta[_outputName].Dimensions, Convert.ToInt64);
var inputShapeSize = ShapeUtils.GetSizeForShape(_inputShape);
Assert.Equal(inputShapeSize, _inputData.Length);
var outputShapeSize = ShapeUtils.GetSizeForShape(_outputShape);
Assert.Equal(outputShapeSize, _outputData.Length);
_inputSizeInBytes = inputShapeSize * sizeof(float);
IntPtr allocPtr = Marshal.AllocHGlobal((int)_inputSizeInBytes);
_inputNativeAllocation = new OrtSafeMemoryHandle(allocPtr);
_dispList.Add(_inputNativeAllocation);
PopulateNativeBuffer<float>(allocPtr, _inputData);
_outputSizeInBytes = outputShapeSize * sizeof(float);
allocPtr = Marshal.AllocHGlobal((int)_outputSizeInBytes);
_outputNativeAllocation = new OrtSafeMemoryHandle(allocPtr);
}
// Probably redundant as we have no native resources
~OrtIoBindingAllocationTests()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
_dispList.Dispose();
}
_disposed = true;
}
/// <summary>
/// This works only for allocations accessible from host memory
/// </summary>
/// <param name="buffer"></param>
/// <param name="elements"></param>
private static void PopulateNativeBuffer<T>(OrtMemoryAllocation buffer, T[] elements)
{
PopulateNativeBuffer(buffer.Pointer, elements);
}
private static void PopulateNativeBuffer<T>(IntPtr buffer, T[] elements)
{
Span<T> bufferSpan;
unsafe
{
bufferSpan = new Span<T>(buffer.ToPointer(), elements.Length);
}
elements.CopyTo(bufferSpan);
}
/// <summary>
/// Checks that the contents of the native buffer matches the expected output.
/// </summary>
private void CheckOutput(IntPtr resultBuffer)
{
Span<byte> bufferSpan;
unsafe
{
bufferSpan = new Span<byte>(resultBuffer.ToPointer(), (int)_outputSizeInBytes);
}
var outputSpan = MemoryMarshal.Cast<byte, float>(bufferSpan);
Assert.Equal(_outputData, outputSpan.ToArray(), new FloatComparer());
}
private void ClearOutput()
{
Span<byte> bufferSpan;
unsafe
{
bufferSpan = new Span<byte>(_outputNativeAllocation.Handle.ToPointer(), (int)_outputSizeInBytes);
}
bufferSpan.Clear();
}
/// <summary>
/// Use to free globally allocated memory. Could not find
/// a framework class.
/// </summary>
class OrtSafeMemoryHandle : SafeHandle
{
public OrtSafeMemoryHandle(IntPtr allocPtr) : base(allocPtr, true) { }
public override bool IsInvalid => handle == IntPtr.Zero;
public IntPtr Handle => handle;
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(handle);
handle = IntPtr.Zero;
return true;
}
}
[Fact(DisplayName = "TestIOBindingWithOrtValues")]
public void TestIOBindingWithOrtValues()
{
ClearOutput();
using (var ioBinding = _session.CreateIoBinding())
{
// Input OrtValue on top if input buffer
using (var tensor = OrtValue.CreateTensorValueWithData(OrtMemoryInfo.DefaultInstance,
TensorElementType.Float,
_inputShape, _inputNativeAllocation.Handle, _inputSizeInBytes))
{
ioBinding.BindInput(_inputName, tensor);
}
// Output OrtValue on top if output buffer
using (var tensor = OrtValue.CreateTensorValueWithData(OrtMemoryInfo.DefaultInstance,
TensorElementType.Float,
_outputShape, _outputNativeAllocation.Handle, _outputSizeInBytes))
{
ioBinding.BindOutput(_outputName, tensor);
}
ioBinding.SynchronizeBoundInputs();
using (var results = _session.RunWithBoundResults(_runOptions, ioBinding))
{
ioBinding.SynchronizeBoundOutputs();
Assert.Single(results);
var res = results.First();
Assert.True(res.IsTensor);
var typeAndShape = res.GetTensorTypeAndShape();
Assert.Equal(_outputData.LongLength, typeAndShape.ElementCount);
var dataSpan = res.GetTensorDataAsSpan<float>();
Assert.Equal(_outputData, dataSpan.ToArray(), new FloatComparer());
// The result is good, but we want to make sure that the result actually is
// in the output memory, not some other place
CheckOutput(_outputNativeAllocation.Handle);
}
var outputNames = ioBinding.GetOutputNames();
Assert.Single(outputNames);
Assert.Equal(_outputName, outputNames[0]);
}
}
[Fact(DisplayName = "TestIOBindingWithDeviceBoundOutput")]
public void TestIOBindingWithDeviceBoundOutput()
{
ClearOutput();
using (var ioBinding = _session.CreateIoBinding())
{
// Input OrtValue on top if input buffer
using (var tensor = OrtValue.CreateTensorValueWithData(OrtMemoryInfo.DefaultInstance,
TensorElementType.Float,
_inputShape, _inputNativeAllocation.Handle, _inputSizeInBytes))
{
ioBinding.BindInput(_inputName, tensor);
}
// The output will go into the Ort allocated OrtValue
ioBinding.BindOutputToDevice(_outputName, OrtMemoryInfo.DefaultInstance);
ioBinding.SynchronizeBoundInputs();
using (var results = _session.RunWithBoundResults(_runOptions, ioBinding))
{
ioBinding.SynchronizeBoundOutputs();
Assert.Single(results);
var res = results.First();
Assert.True(res.IsTensor);
var typeAndShape = res.GetTensorTypeAndShape();
Assert.Equal(_outputData.LongLength, typeAndShape.ElementCount);
var dataSpan = res.GetTensorDataAsSpan<float>();
Assert.Equal(_outputData, dataSpan.ToArray(), new FloatComparer());
}
}
}
[Fact(DisplayName = "TestIOBindingToOrtAllocatedBuffer")]
public void TestIOBindingToOrtAllocatedBuffer()
{
var ortAllocationInput = _allocator.Allocate((uint)_inputSizeInBytes);
_dispList.Add(ortAllocationInput);
PopulateNativeBuffer<float>(ortAllocationInput, _inputData);
var ortAllocationOutput = _allocator.Allocate((uint)_outputSizeInBytes);
_dispList.Add(ortAllocationOutput);
using (var ioBinding = _session.CreateIoBinding())
{
// Still supporting OrtAllocations overload
ioBinding.BindInput(_inputName, Tensors.TensorElementType.Float, _inputShape, ortAllocationInput);
ioBinding.BindOutput(_outputName, Tensors.TensorElementType.Float, _outputShape, ortAllocationOutput);
ioBinding.SynchronizeBoundInputs();
using (var outputs = _session.RunWithBoundResults(_runOptions, ioBinding))
{
ioBinding.SynchronizeBoundOutputs();
Assert.Single(outputs);
var res = outputs.First();
Assert.True(res.IsTensor);
var typeAndShape = res.GetTensorTypeAndShape();
Assert.Equal(_outputData.LongLength, typeAndShape.ElementCount);
var dataSpan = res.GetTensorDataAsSpan<float>();
Assert.Equal(_outputData, dataSpan.ToArray(), new FloatComparer());
CheckOutput(ortAllocationOutput.Pointer);
}
}
}
}
[Collection("OrtBinding Tests")]
public class OrtBidingCircularTest
{
[Fact(DisplayName = "TestIOBinding Demonstrate Circular")]
public void TestIOBindingDemonstrateCircular()
{
// This model has input and output of the same shape, so we can easily feed
// output to input using binding, or not using one. The example makes use of
// the binding to demonstrate the circular feeding.
// With the OrtValue API exposed, one create OrtValues over arbitrary buffers and feed them to the model using
// OrtValues based Run APIs. Thus, the Binding is not necessary any longer
//
// However, here is the demonstration by popular request.
var model = TestDataLoader.LoadModelFromEmbeddedResource("mul_1.onnx");
const string inputName = "X";
const string outputName = "Y";
long[] inputOutputShape = { 3, 2 };
float[] input = { 1.0F, 2.0F, 3.0F, 4.0F, 5.0F, 6.0F };
var inputOutputShapeSize = ShapeUtils.GetSizeForShape(inputOutputShape);
Assert.Equal(inputOutputShapeSize, input.LongLength);
var memInput = new Memory<float>(input);
IntPtr inputPtr;
// Output data on the first iteration
float[] firstIterExpectedOutput = { 1.0F, 4.0F, 9.0F, 16.0F, 25.0F, 36.0F };
Assert.Equal(inputOutputShapeSize, firstIterExpectedOutput.LongLength);
using (var cleanUp = new DisposableListTest<IDisposable>())
{
var runOptions = new RunOptions();
cleanUp.Add(runOptions);
var session = new InferenceSession(model);
cleanUp.Add(session);
var ioBinding = session.CreateIoBinding();
cleanUp.Add(ioBinding);
var pinInput = memInput.Pin();
cleanUp.Add(pinInput);
// This can be a ptr to arbitrary buffer, not necessarily a pinned one
unsafe
{
inputPtr = (IntPtr)pinInput.Pointer;
}
// Bind the input
using (var ortInput = OrtValue.CreateTensorValueWithData(OrtMemoryInfo.DefaultInstance,
TensorElementType.Float, inputOutputShape, inputPtr, input.Length * sizeof(float)))
{
ioBinding.BindInput(inputName, ortInput);
}
// We could have bound the output as well, but we simply bind it to a device in this case.
// Just check the result the first time around.
ioBinding.BindOutputToDevice(outputName, OrtMemoryInfo.DefaultInstance);
ioBinding.SynchronizeBoundInputs();
// We dispose the output after we rebind it to the input because it will be copied during binding.
using (var results = session.RunWithBoundResults(runOptions, ioBinding))
{
ioBinding.SynchronizeBoundOutputs();
Assert.Single(results); // One output
var res = results.First();
Assert.True(res.IsTensor);
var typeShape = res.GetTensorTypeAndShape();
Assert.Equal(TensorElementType.Float, typeShape.ElementDataType);
Assert.Equal(inputOutputShape, typeShape.Shape);
Assert.Equal(inputOutputShapeSize, typeShape.ElementCount);
// First time around the output should match the expected
Assert.Equal(firstIterExpectedOutput, res.GetTensorDataAsSpan<float>().ToArray());
// Now we rebind the output to the input
// It is the same name, so the OrtValue would be replaced.
ioBinding.BindInput(inputName, res);
}
// Let's do it 2 more times.
const int iterations = 2;
for (int i = 0; i < iterations; ++i)
{
using (var results = session.RunWithBoundResults(runOptions, ioBinding))
{
ioBinding.SynchronizeBoundOutputs();
Assert.Single(results); // One output
var res = results.First();
Assert.True(res.IsTensor);
var typeShape = res.GetTensorTypeAndShape();
Assert.Equal(TensorElementType.Float, typeShape.ElementDataType);
Assert.Equal(inputOutputShapeSize, typeShape.ElementCount);
Assert.Equal(inputOutputShape, typeShape.Shape);
ioBinding.BindInput(inputName, res);
}
}
}
}
}
}