Skip to content

Commit 07b586b

Browse files
authored
Merge pull request #4 from ericstj/wrapTF
[WIP] Create a minimal TF binding
2 parents 988c36e + f632824 commit 07b586b

File tree

9 files changed

+3535
-14
lines changed

9 files changed

+3535
-14
lines changed

Microsoft.ML.sln

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.CpuMath.UnitTe
103103
EndProject
104104
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.CpuMath.UnitTests.netcoreapp", "test\Microsoft.ML.CpuMath.UnitTests.netcoreapp\Microsoft.ML.CpuMath.UnitTests.netcoreapp.csproj", "{5F81A2A4-73AD-494C-B387-07D605EC8826}"
105105
EndProject
106-
107-
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Microsoft.ML.FSharp.Tests", "test\Microsoft.ML.FSharp.Tests\Microsoft.ML.FSharp.Tests.fsproj", "{802233D6-8CC0-46AD-9F23-FEE1E9AED9B3}"
106+
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Microsoft.ML.FSharp.Tests", "test\Microsoft.ML.FSharp.Tests\Microsoft.ML.FSharp.Tests.fsproj", "{802233D6-8CC0-46AD-9F23-FEE1E9AED9B3}"
108107
EndProject
109108
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.ML.ImageAnalytics", "src\Microsoft.ML.ImageAnalytics\Microsoft.ML.ImageAnalytics.csproj", "{00E38F77-1E61-4CDF-8F97-1417D4E85053}"
110109
EndProject
@@ -426,11 +425,11 @@ Global
426425
{001F3B4E-FBE4-4001-AFD2-A6A989CD1C25} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
427426
{DCF46B79-1FDB-4DBA-A263-D3D64E3AAA27} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
428427
{BF66A305-DF10-47E4-8D81-42049B149D2B} = {D3D38B03-B557-484D-8348-8BADEE4DF592}
428+
{B4E55B2D-2A92-46E7-B72F-E76D6FD83440} = {7F13E156-3EBA-4021-84A5-CD56BA72F99E}
429+
{3E4ABF07-7970-4BE6-B45B-A13D3C397545} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4}
429430
{7333EDEF-4144-405C-A5EC-6F42201857D8} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4}
430431
{A0E562A9-0E6D-470D-B180-6EB44BA84D60} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4}
431432
{5F81A2A4-73AD-494C-B387-07D605EC8826} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4}
432-
{B4E55B2D-2A92-46E7-B72F-E76D6FD83440} = {7F13E156-3EBA-4021-84A5-CD56BA72F99E}
433-
{3E4ABF07-7970-4BE6-B45B-A13D3C397545} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4}
434433
{802233D6-8CC0-46AD-9F23-FEE1E9AED9B3} = {AED9C836-31E3-4F3F-8ABC-929555D3F3C4}
435434
{00E38F77-1E61-4CDF-8F97-1417D4E85053} = {09EADF06-BE25-4228-AB53-95AE3E15B530}
436435
{A7222F41-1CF0-47D9-B80C-B4D77B027A61} = {09EADF06-BE25-4228-AB53-95AE3E15B530}

src/Microsoft.ML.Transforms/Microsoft.ML.Transforms.csproj

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFramework>netstandard2.0</TargetFramework>
55
<IncludeInPackage>Microsoft.ML</IncludeInPackage>
66
<DefineConstants>CORECLR</DefineConstants>
7-
</PropertyGroup>
8-
9-
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
10-
<NoWarn>CS8002;1591</NoWarn>
117
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
128
</PropertyGroup>
139

@@ -49,10 +45,6 @@
4945
<EmbeddedResource Include="Text\StopWords\Swedish.txt" />
5046
</ItemGroup>
5147

52-
<ItemGroup>
53-
<PackageReference Include="TensorFlowSharp" Version="1.9.0" />
54-
</ItemGroup>
55-
5648
<ItemGroup>
5749
<ProjectReference Include="..\Microsoft.ML.Api\Microsoft.ML.Api.csproj" />
5850
<ProjectReference Include="..\Microsoft.ML.Core\Microsoft.ML.Core.csproj" />
@@ -65,6 +57,11 @@
6557
<AutoGen>True</AutoGen>
6658
<DependentUpon>Resources.resx</DependentUpon>
6759
</Compile>
60+
<Compile Update="TensorFlow\TensorGeneric.cs">
61+
<DesignTime>True</DesignTime>
62+
<AutoGen>True</AutoGen>
63+
<DependentUpon>TensorGeneric.tt</DependentUpon>
64+
</Compile>
6865
</ItemGroup>
6966

7067
<ItemGroup>
@@ -74,4 +71,15 @@
7471
</EmbeddedResource>
7572
</ItemGroup>
7673

74+
<ItemGroup>
75+
<None Update="TensorFlow\TensorGeneric.tt">
76+
<Generator>TextTemplatingFileGenerator</Generator>
77+
<LastGenOutput>TensorGeneric.cs</LastGenOutput>
78+
</None>
79+
</ItemGroup>
80+
81+
<ItemGroup>
82+
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />
83+
</ItemGroup>
84+
7785
</Project>
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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;
6+
using System.Runtime.InteropServices;
7+
using System.Text;
8+
using size_t = System.UIntPtr;
9+
10+
#pragma warning disable MSML_GeneralName
11+
#pragma warning disable MSML_ParameterLocalVarName
12+
13+
namespace Microsoft.ML.Transforms.TensorFlow
14+
{
15+
/// <summary>
16+
/// This attribute can be applied to callback functions that will be invoked
17+
/// from unmanaged code to managed code.
18+
/// </summary>
19+
/// <remarks>
20+
/// <code>
21+
/// [TensorFlow.MonoPInvokeCallback (typeof (BufferReleaseFunc))]
22+
/// internal static void MyFreeFunc (IntPtr data, IntPtr length){..}
23+
/// </code>
24+
/// </remarks>
25+
internal sealed class MonoPInvokeCallbackAttribute : Attribute
26+
{
27+
/// <summary>
28+
/// Use this constructor to annotate the type of the callback function that
29+
/// will be invoked from unmanaged code.
30+
/// </summary>
31+
/// <param name="t">T.</param>
32+
public MonoPInvokeCallbackAttribute (Type t) { }
33+
}
34+
35+
[StructLayout (LayoutKind.Sequential)]
36+
internal struct LLBuffer
37+
{
38+
internal IntPtr data;
39+
internal size_t length;
40+
internal IntPtr data_deallocator;
41+
}
42+
43+
/// <summary>
44+
/// Holds a block of data, suitable to pass, or retrieve from TensorFlow.
45+
/// </summary>
46+
/// <remarks>
47+
/// <para>
48+
/// Use the TFBuffer to blobs of data into TensorFlow, or to retrieve blocks
49+
/// of data out of TensorFlow.
50+
/// </para>
51+
/// <para>
52+
/// There are two constructors to wrap existing data, one to wrap blocks that are
53+
/// pointed to by an IntPtr and one that takes a byte array that we want to wrap.
54+
/// </para>
55+
/// <para>
56+
/// The empty constructor can be used to create a new TFBuffer that can be populated
57+
/// by the TensorFlow library and returned to user code.
58+
/// </para>
59+
/// <para>
60+
/// Typically, the data consists of a serialized protocol buffer, but other data
61+
/// may also be held in a buffer.
62+
/// </para>
63+
/// </remarks>
64+
// TODO: the string ctor
65+
// TODO: perhaps we should have an implicit byte [] conversion that just calls ToArray?
66+
internal class TFBuffer : TFDisposable
67+
{
68+
// extern TF_Buffer * TF_NewBufferFromString (const void *proto, size_t proto_len);
69+
[DllImport (NativeBinding.TensorFlowLibrary)]
70+
private static extern unsafe LLBuffer* TF_NewBufferFromString (IntPtr proto, IntPtr proto_len);
71+
72+
// extern TF_Buffer * TF_NewBuffer ();
73+
[DllImport (NativeBinding.TensorFlowLibrary)]
74+
private static extern unsafe LLBuffer* TF_NewBuffer ();
75+
76+
internal TFBuffer (IntPtr handle) : base (handle) { }
77+
78+
/// <summary>
79+
/// Initializes a new instance of the <see cref="T:TensorFlow.TFBuffer"/> class.
80+
/// </summary>
81+
public unsafe TFBuffer () : base ((IntPtr)TF_NewBuffer ())
82+
{
83+
}
84+
85+
/// <summary>
86+
/// Signature of the method that is invoked to release the data.
87+
/// </summary>
88+
/// <remarks>
89+
/// Methods of this signature are invoked with the data pointer and the
90+
/// lenght pointer when then TFBuffer no longer needs to hold on to the
91+
/// data. If you are using this on platforms with static compilation
92+
/// like iOS, you need to annotate your callback with the MonoPInvokeCallbackAttribute,
93+
/// like this:
94+
///
95+
/// <code>
96+
/// [TensorFlow.MonoPInvokeCallback (typeof (BufferReleaseFunc))]
97+
/// internal static void MyFreeFunc (IntPtr data, IntPtr length){..}
98+
/// </code>
99+
/// </remarks>
100+
public delegate void BufferReleaseFunc (IntPtr data, IntPtr lenght);
101+
102+
/// <summary>
103+
/// Initializes a new instance of the <see cref="T:TensorFlow.TFBuffer"/> by wrapping the unmanaged resource pointed by the buffer.
104+
/// </summary>
105+
/// <param name="buffer">Pointer to the data that will be wrapped.</param>
106+
/// <param name="size">The size of the buffer to wrap.</param>
107+
/// <param name="release">Optional, if not null, this method will be invoked to release the block.</param>
108+
/// <remarks>
109+
/// This constructor wraps the buffer as a the data to be held by the <see cref="T:TensorFlow.TFBuffer"/>,
110+
/// if the release parameter is null, then you must ensure that the data is not released before the TFBuffer
111+
/// is no longer in use. If the value is not null, the provided method will be invoked to release
112+
/// the data when the TFBuffer is disposed, or the contents of the buffer replaced.
113+
/// </remarks>
114+
public unsafe TFBuffer (IntPtr buffer, long size, BufferReleaseFunc release) : base ((IntPtr)TF_NewBuffer ())
115+
{
116+
LLBuffer* buf = (LLBuffer*)handle;
117+
buf->data = buffer;
118+
buf->length = (size_t)size;
119+
if (release == null)
120+
buf->data_deallocator = IntPtr.Zero;
121+
else
122+
buf->data_deallocator = Marshal.GetFunctionPointerForDelegate (release);
123+
}
124+
125+
[MonoPInvokeCallback (typeof (BufferReleaseFunc))]
126+
internal static void FreeBlock (IntPtr data, IntPtr length)
127+
{
128+
Marshal.FreeHGlobal (data);
129+
}
130+
131+
internal static IntPtr FreeBufferFunc;
132+
internal static BufferReleaseFunc FreeBlockDelegate;
133+
134+
static TFBuffer ()
135+
{
136+
FreeBlockDelegate = FreeBlock;
137+
FreeBufferFunc = Marshal.GetFunctionPointerForDelegate<BufferReleaseFunc> (FreeBlockDelegate);
138+
}
139+
140+
/// <summary>
141+
/// Initializes a new instance of the <see cref="T:TensorFlow.TFBuffer"/> by making a copy of the provided byte array.
142+
/// </summary>
143+
/// <param name="buffer">Buffer of data that will be wrapped.</param>
144+
/// <remarks>
145+
/// This constructor makes a copy of the data into an unmanaged buffer,
146+
/// so the byte array is not pinned.
147+
/// </remarks>
148+
public TFBuffer (byte [] buffer) : this (buffer, 0, buffer.Length) { }
149+
150+
/// <summary>
151+
/// Initializes a new instance of the <see cref="T:TensorFlow.TFBuffer"/> by making a copy of the provided byte array.
152+
/// </summary>
153+
/// <param name="buffer">Buffer of data that will be wrapped.</param>
154+
/// <param name="start">Starting offset into the buffer to wrap.</param>
155+
/// <param name="count">Number of bytes from the buffer to keep.</param>
156+
/// <remarks>
157+
/// This constructor makes a copy of the data into an unmanaged buffer,
158+
/// so the byte array is not pinned.
159+
/// </remarks>
160+
public TFBuffer (byte [] buffer, int start, int count) : this ()
161+
{
162+
if (start < 0 || start >= buffer.Length)
163+
throw new ArgumentException ("start");
164+
if (count < 0 || count > buffer.Length - start)
165+
throw new ArgumentException ("count");
166+
unsafe
167+
{
168+
LLBuffer* buf = LLBuffer;
169+
buf->data = Marshal.AllocHGlobal (count);
170+
Marshal.Copy (buffer, start, buf->data, count);
171+
buf->length = (size_t)count;
172+
buf->data_deallocator = FreeBufferFunc;
173+
}
174+
}
175+
176+
internal unsafe LLBuffer* LLBuffer => (LLBuffer*)handle;
177+
178+
// extern void TF_DeleteBuffer (TF_Buffer *);
179+
[DllImport (NativeBinding.TensorFlowLibrary)]
180+
private static extern unsafe void TF_DeleteBuffer (LLBuffer* buffer);
181+
182+
internal override void NativeDispose (IntPtr handle)
183+
{
184+
unsafe { TF_DeleteBuffer ((LLBuffer*)handle); }
185+
}
186+
187+
// extern TF_Buffer TF_GetBuffer (TF_Buffer *buffer);
188+
[DllImport (NativeBinding.TensorFlowLibrary)]
189+
private static extern unsafe LLBuffer TF_GetBuffer (LLBuffer* buffer);
190+
191+
/// <summary>
192+
/// Returns a byte array representing the data wrapped by this buffer.
193+
/// </summary>
194+
/// <returns>The array.</returns>
195+
public byte [] ToArray ()
196+
{
197+
if (handle == IntPtr.Zero)
198+
return null;
199+
200+
unsafe
201+
{
202+
var lb = (LLBuffer*)handle;
203+
204+
var result = new byte [(int)lb->length];
205+
Marshal.Copy (lb->data, result, 0, (int)lb->length);
206+
207+
return result;
208+
}
209+
}
210+
}
211+
}

0 commit comments

Comments
 (0)