diff --git a/TensorFlow.NET.sln.DotSettings b/TensorFlow.NET.sln.DotSettings
new file mode 100644
index 000000000..aba8725cc
--- /dev/null
+++ b/TensorFlow.NET.sln.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/src/TensorFlowNET.Core/APIs/c_api.cs b/src/TensorFlowNET.Core/APIs/c_api.cs
index adf0b86fb..56672173b 100644
--- a/src/TensorFlowNET.Core/APIs/c_api.cs
+++ b/src/TensorFlowNET.Core/APIs/c_api.cs
@@ -54,6 +54,15 @@ public static string StringPiece(IntPtr handle)
public struct DeallocatorArgs
{
+ internal static unsafe c_api.DeallocatorArgs* EmptyPtr;
+ internal static unsafe IntPtr Empty;
+
+ static unsafe DeallocatorArgs()
+ {
+ Empty = new IntPtr(EmptyPtr = (DeallocatorArgs*) Marshal.AllocHGlobal(Marshal.SizeOf()));
+ *EmptyPtr = new DeallocatorArgs() {gc_handle = IntPtr.Zero, deallocator_called = false};
+ }
+
public bool deallocator_called;
public IntPtr gc_handle;
}
diff --git a/src/TensorFlowNET.Core/Sessions/BaseSession.cs b/src/TensorFlowNET.Core/Sessions/BaseSession.cs
index c33681208..b1327339d 100644
--- a/src/TensorFlowNET.Core/Sessions/BaseSession.cs
+++ b/src/TensorFlowNET.Core/Sessions/BaseSession.cs
@@ -152,7 +152,6 @@ private NDArray[] _do_run(List target_list, List fetch_list,
{
var feeds = new KeyValuePair[feed_dict.Count];
- var ignoreDispose = new bool[feed_dict.Count];
int i = 0;
foreach (var x in feed_dict)
{
@@ -160,8 +159,9 @@ private NDArray[] _do_run(List target_list, List fetch_list,
{
switch (x.Value)
{
- case Tensor v: ignoreDispose[i] = true; feeds[i++] = new KeyValuePair(tensor._as_tf_output(), v); break;
+ case Tensor v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), v); break;
case NDArray v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v, tensor.dtype)); break;
+ case IntPtr v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break;
#if _REGEN
%types = ["sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"]
%foreach types%
@@ -194,7 +194,6 @@ private NDArray[] _do_run(List target_list, List fetch_list,
#endif
case bool v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor((byte) (v ? 1 : 0), TF_DataType.TF_BOOL)); break;
case string v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break;
- case IntPtr v: feeds[i++] = new KeyValuePair(tensor._as_tf_output(), new Tensor(v)); break;
default:
throw new NotImplementedException($"feed_dict data type {x.Value?.GetType().Name ?? ""}");
}
@@ -203,18 +202,7 @@ private NDArray[] _do_run(List target_list, List fetch_list,
var fetches = fetch_list.Select(x => x._as_tf_output()).ToArray();
//var targets = target_list;
- try
- {
- return _call_tf_sessionrun(feeds, fetches, target_list);
- } finally
- {
- for (var idx = 0; idx < feeds.Length; idx++)
- {
- if (ignoreDispose[idx])
- continue;
- feeds[idx].Value.Dispose();
- }
- }
+ return _call_tf_sessionrun(feeds, fetches, target_list);
}
private unsafe NDArray[] _call_tf_sessionrun(KeyValuePair[] feed_dict, TF_Output[] fetch_list, List target_list)
diff --git a/src/TensorFlowNET.Core/Tensors/AllocationType.cs b/src/TensorFlowNET.Core/Tensors/AllocationType.cs
new file mode 100644
index 000000000..9f5c8badd
--- /dev/null
+++ b/src/TensorFlowNET.Core/Tensors/AllocationType.cs
@@ -0,0 +1,27 @@
+namespace Tensorflow
+{
+ ///
+ /// Used internally to
+ ///
+ public enum AllocationType
+ {
+ None = 0,
+ ///
+ /// Allocation was done by passing in a pointer, might be also holding reference to a C# object.
+ ///
+ FromPointer = 1,
+ ///
+ /// Allocation was done by calling c_api.TF_AllocateTensor or TF decided it has to copy data during c_api.TF_NewTensor.
+ /// Deallocation is handled solely by Tensorflow.
+ ///
+ Tensorflow = 2,
+ ///
+ /// Allocation was done by Marshal.AllocateHGlobal
+ ///
+ Marshal = 3,
+ ///
+ /// Allocation was done by GCHandle.Alloc
+ ///
+ GCHandle = 4,
+ }
+}
\ No newline at end of file
diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs
index 5fa60eff2..42e766569 100644
--- a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs
+++ b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs
@@ -28,42 +28,37 @@ limitations under the License.
namespace Tensorflow
{
+ [SuppressMessage("ReSharper", "InvokeAsExtensionMethod")]
public partial class Tensor
{
///
- /// true if unmanaged buffer has been freed.
+ /// When Tensor was created from an object that is managed by C#'s GC - this will hold reference to prevent it from being collected.
///
- private bool _deallocator_called => _deallocatorArgs.deallocator_called;
+ protected object AllocationReferenceHolder;
///
- /// true if the Tensor was created from a managed array
+ /// The handle that was used to allocate this tensor, dependent on .
///
- private bool _isPinnedArray => _deallocatorArgs.gc_handle != IntPtr.Zero;
+ protected object AllocationHandle;
///
- /// True only if the Tensor object was created in a way that the Tensor object itself allocated memory or pinned a managed object.
- /// False if the Tensor was created from a pointer
+ /// True if this Tensor holds data allocated by C#.
///
- public bool IsMemoryOwner { get; private set; }
+ public bool IsMemoryOwner => AllocationType >= AllocationType.Marshal;
///
- /// This holds values that are used by the unmanaged deallocator callback
+ /// The allocation method used to create this Tensor.
///
- private DeallocatorArgs _deallocatorArgs = new DeallocatorArgs() { gc_handle = IntPtr.Zero };
-
- // note: they must be assigned to a static variable in order to work as unmanaged callbacks
- private static readonly Deallocator _hGlobalDeallocator = FreeHGlobalMemory;
- private static readonly Deallocator _gcHandleDeallocator = FreeGCHandle;
- private static readonly Deallocator _nothingDeallocator = FreeNothing;
+ public AllocationType AllocationType { get; protected set; }
///
- /// Create a Tensor object from an existing TF handle
+ /// Create a Tensor object from an existing TF handle
///
- ///
+ /// Handle to a object.
public Tensor(IntPtr handle)
{
_handle = handle;
- IsMemoryOwner = false;
+ //no need to set AllocationType = AllocationType.None;
}
///
@@ -71,399 +66,376 @@ public Tensor(IntPtr handle)
/// Note: the caller is responsible for freeing the memory. Calling Dispose on this object will dispose the TensorFlow tensor
/// but not the memory itself!
///
- /// Pointer to unmanaged, fixed or pinned memory which the caller owns
+ /// Pointer to unmanaged, fixed or pinned memory which the caller owns
/// Tensor shape
/// TF data type
/// Size of the tensor in memory
- public Tensor(IntPtr ptr, long[] shape, TF_DataType dType, int num_bytes)
+ public Tensor(IntPtr data_ptr, long[] shape, TF_DataType dType, int num_bytes)
{
- _handle = TF_NewTensor(dType, dims: shape, num_dims: shape.Length, data: ptr, len: (UIntPtr)num_bytes, deallocator: _nothingDeallocator, ref _deallocatorArgs);
- IsMemoryOwner = false;
+ unsafe
+ {
+ _handle = TF_NewTensor(dType, dims: shape, num_dims: shape.Length, data: data_ptr, len: (UIntPtr) num_bytes);
+ AllocationType = TF_TensorData(_handle) == data_ptr ? AllocationType.FromPointer : AllocationType.Tensorflow;
+ }
+ }
+
+ ///
+ /// Create a new Tensor from the given unmanaged memory pointer (which must be allocated, fixed or pinned by the caller)
+ /// Note: the caller is responsible for freeing the memory. Calling Dispose on this object will dispose the TensorFlow tensor
+ /// but not the memory itself!
+ ///
+ /// Pointer to unmanaged, fixed or pinned memory which the caller owns
+ /// Tensor shape
+ /// TF data type
+ /// Size of the tensor in memory
+ public unsafe Tensor(void* data_ptr, long[] shape, TF_DataType dType, int num_bytes)
+ {
+ _handle = TF_NewTensor(dType, dims: shape, num_dims: shape.Length, data: data_ptr, len: (UIntPtr) num_bytes);
+ AllocationType = TF_TensorData(_handle).ToPointer() == data_ptr ? AllocationType.FromPointer : AllocationType.Tensorflow;
}
#if _REGEN
- %types=["sbyte", "bool", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"]
+ %types = ["sbyte", "bool", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"]
%foreach types%
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(#1[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(#1)), new long[]{data.Length}, data, Marshal.SizeOf<#1>());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(#1)), new long[] {data.Length}, data, #(#1=="Complex"|"Marshal.SizeOf()"|"sizeof(#(str(#1)))"));
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(#1[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(#1)), shape, data, Marshal.SizeOf<#1>());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(#1)), shape, data, #(#1=="Complex"|"Marshal.SizeOf()"|"sizeof(#(str(#1)))"));
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(#1 value, TF_DataType? dType = null)
{
- var v = (#1*)Marshal.AllocHGlobal(sizeof(#1));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(#1)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(#1), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(#1)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(#1));
+ *(#1*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
%
#else
-
-
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(sbyte[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(sbyte)), new long[]{data.Length}, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(sbyte)), new long[] {data.Length}, data, sizeof(sbyte));
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(sbyte[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(sbyte)), shape, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(sbyte)), shape, data, sizeof(sbyte));
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(sbyte value, TF_DataType? dType = null)
{
- var v = (sbyte*)Marshal.AllocHGlobal(sizeof(sbyte));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(sbyte)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(sbyte), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(sbyte)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(sbyte));
+ *(sbyte*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(bool[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(bool)), new long[]{data.Length}, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(bool)), new long[] {data.Length}, data, sizeof(bool));
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(bool[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(bool)), shape, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(bool)), shape, data, sizeof(bool));
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(bool value, TF_DataType? dType = null)
{
- var v = (bool*)Marshal.AllocHGlobal(sizeof(bool));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(bool)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(bool), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(bool)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(bool));
+ *(bool*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(byte[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(byte)), new long[]{data.Length}, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(byte)), new long[] {data.Length}, data, sizeof(byte));
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(byte[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(byte)), shape, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(byte)), shape, data, sizeof(byte));
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(byte value, TF_DataType? dType = null)
{
- var v = (byte*)Marshal.AllocHGlobal(sizeof(byte));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(byte)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(byte), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(byte)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(byte));
+ *(byte*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(short[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(short)), new long[]{data.Length}, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(short)), new long[] {data.Length}, data, sizeof(short));
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(short[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(short)), shape, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(short)), shape, data, sizeof(short));
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(short value, TF_DataType? dType = null)
{
- var v = (short*)Marshal.AllocHGlobal(sizeof(short));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(short)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(short), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(short)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(short));
+ *(short*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(ushort[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(ushort)), new long[]{data.Length}, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(ushort)), new long[] {data.Length}, data, sizeof(ushort));
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(ushort[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(ushort)), shape, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(ushort)), shape, data, sizeof(ushort));
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(ushort value, TF_DataType? dType = null)
{
- var v = (ushort*)Marshal.AllocHGlobal(sizeof(ushort));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(ushort)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(ushort), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(ushort)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(ushort));
+ *(ushort*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(int[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(int)), new long[]{data.Length}, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(int)), new long[] {data.Length}, data, sizeof(int));
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(int[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(int)), shape, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(int)), shape, data, sizeof(int));
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(int value, TF_DataType? dType = null)
{
- var v = (int*)Marshal.AllocHGlobal(sizeof(int));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(int)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(int), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(int)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(int));
+ *(int*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(uint[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(uint)), new long[]{data.Length}, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(uint)), new long[] {data.Length}, data, sizeof(uint));
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(uint[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(uint)), shape, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(uint)), shape, data, sizeof(uint));
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(uint value, TF_DataType? dType = null)
{
- var v = (uint*)Marshal.AllocHGlobal(sizeof(uint));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(uint)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(uint), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(uint)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(uint));
+ *(uint*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(long[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(long)), new long[]{data.Length}, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(long)), new long[] {data.Length}, data, sizeof(long));
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(long[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(long)), shape, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(long)), shape, data, sizeof(long));
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(long value, TF_DataType? dType = null)
{
- var v = (long*)Marshal.AllocHGlobal(sizeof(long));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(long)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(long), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(long)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(long));
+ *(long*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(ulong[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(ulong)), new long[]{data.Length}, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(ulong)), new long[] {data.Length}, data, sizeof(ulong));
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(ulong[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(ulong)), shape, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(ulong)), shape, data, sizeof(ulong));
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(ulong value, TF_DataType? dType = null)
{
- var v = (ulong*)Marshal.AllocHGlobal(sizeof(ulong));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(ulong)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(ulong), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(ulong)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(ulong));
+ *(ulong*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(float[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(float)), new long[]{data.Length}, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(float)), new long[] {data.Length}, data, sizeof(float));
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(float[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(float)), shape, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(float)), shape, data, sizeof(float));
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(float value, TF_DataType? dType = null)
{
- var v = (float*)Marshal.AllocHGlobal(sizeof(float));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(float)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(float), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(float)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(float));
+ *(float*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(double[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(double)), new long[]{data.Length}, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(double)), new long[] {data.Length}, data, sizeof(double));
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(double[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(double)), shape, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(double)), shape, data, sizeof(double));
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(double value, TF_DataType? dType = null)
{
- var v = (double*)Marshal.AllocHGlobal(sizeof(double));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(double)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(double), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(double)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(double));
+ *(double*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
///
- /// Create a 1d Tensor from the given linear array and shape
+ /// Create a 1d Tensor from the given linear array and shape
///
public Tensor(Complex[] data, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(Complex)), new long[]{data.Length}, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(Complex)), new long[] {data.Length}, data, Marshal.SizeOf());
}
///
- /// Create a N-dimensional Tensor from the given array
+ /// Create a N-dimensional Tensor from the given array
///
public Tensor(Complex[] data, long[] shape, TF_DataType? dType = null)
{
- _handle = CreateTensorWithoutCopying(dType ?? dtypes.as_dtype(typeof(Complex)), shape, data, Marshal.SizeOf());
- IsMemoryOwner=true;
+ _handle = CreateTensorFromArray(dType ?? dtypes.as_dtype(typeof(Complex)), shape, data, Marshal.SizeOf());
}
///
- /// Create a scalar Tensor from the given value
+ /// Create a scalar Tensor from the given value
///
public unsafe Tensor(Complex value, TF_DataType? dType = null)
{
- var v = (Complex*)Marshal.AllocHGlobal(sizeof(Complex));
- *v = value;
- _handle = TF_NewTensor(dType ?? dtypes.as_dtype(typeof(Complex)), dims:new long[0], num_dims: 0, data: (IntPtr)v, len: (UIntPtr)sizeof(Complex), deallocator: _hGlobalDeallocator, ref _deallocatorArgs);
- IsMemoryOwner=true;
+ _handle = TF_AllocateTensor(dType ?? dtypes.as_dtype(typeof(Complex)), dims: new long[0], num_dims: 0, len: (UIntPtr) sizeof(Complex));
+ *(Complex*) TF_TensorData(_handle) = value;
+ AllocationType = AllocationType.Tensorflow;
}
#endif
@@ -474,13 +446,13 @@ public unsafe Tensor(string str)
{
var status = new Status();
var buffer = Encoding.UTF8.GetBytes(str);
- var size = c_api.TF_StringEncodedSize((UIntPtr)buffer.Length);
- var handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr)((ulong)size + 8));
+ var size = c_api.TF_StringEncodedSize((UIntPtr) buffer.Length);
+ var handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr) ((ulong) size + 8));
IntPtr tensor = c_api.TF_TensorData(handle);
Marshal.WriteInt64(tensor, 0);
fixed (byte* src = buffer)
- c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status);
+ c_api.TF_StringEncode(src, (UIntPtr) buffer.Length, (sbyte*) (tensor + sizeof(Int64)), size, status);
_handle = handle;
status.Check(true);
}
@@ -492,7 +464,7 @@ public unsafe Tensor(NDArray nd, TF_DataType? tensorDType = null)
{
if (nd.Unsafe.Storage.Shape.IsContiguous)
{
- var bytesLength = (UIntPtr)nd.size;
+ var bytesLength = (UIntPtr) nd.size;
var size = c_api.TF_StringEncodedSize(bytesLength);
var handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr) ((ulong) size + 8));
@@ -504,9 +476,7 @@ public unsafe Tensor(NDArray nd, TF_DataType? tensorDType = null)
status.Check(true);
_handle = handle;
- IsMemoryOwner = false;
- }
- else
+ } else
{
var buffer = nd.ToArray();
var size = c_api.TF_StringEncodedSize((UIntPtr) buffer.Length);
@@ -521,7 +491,6 @@ public unsafe Tensor(NDArray nd, TF_DataType? tensorDType = null)
status.Check(true);
_handle = handle;
- IsMemoryOwner = false;
}
return;
@@ -532,27 +501,27 @@ public unsafe Tensor(NDArray nd, TF_DataType? tensorDType = null)
private unsafe IntPtr CreateTensorFromNDArray(NDArray nd, TF_DataType? given_dtype)
{
- if (nd.dtype.Name == "String")
+ if (nd.typecode == NPTypeCode.String)
throw new NotImplementedException("Support for NDArray of type string not implemented yet");
- IArraySlice arraySlice;
- if (nd.Unsafe.Storage.Shape.IsContiguous == false)
- {
- // the memory is NOT contiguous, so we have to copy the view into a contiguous memory block.
- arraySlice = nd.CloneData();
- }
- else
+
+ var arraySlice = nd.Unsafe.Storage.Shape.IsContiguous ? nd.GetData() : nd.CloneData();
+
+ var handle = TF_NewTensor(
+ given_dtype ?? nd.dtype.as_dtype(),
+ dims: nd.shape.Select(i => (long) i).ToArray(),
+ num_dims: nd.ndim,
+ data: arraySlice.Address,
+ len: (UIntPtr) (nd.size * nd.dtypesize));
+
+ //if TF decided not to perform copy, hold reference for given NDArray.
+ if (TF_TensorData(handle).ToPointer() == nd.Unsafe.Address)
{
- // the memory is contiguous
- arraySlice = nd.GetData();
- }
- this.Tag = arraySlice; // keep a reference to the memory block to make sure it is not disposed while TF is using it
- var ptr = new IntPtr(arraySlice.Address);
- int num_bytes = (nd.size * nd.dtypesize);
- var dtype = given_dtype ?? nd.dtype.as_dtype();
- var handle = TF_NewTensor(dtype, dims: nd.shape.Select(i=>(long)i).ToArray(), num_dims: nd.ndim, data: ptr, len: (UIntPtr)num_bytes, deallocator: _nothingDeallocator, ref _deallocatorArgs);
- IsMemoryOwner = false;
- return handle;
+ AllocationType = AllocationType.FromPointer;
+ AllocationReferenceHolder = nd.Unsafe.Storage;
+ } else
+ AllocationType = AllocationType.Tensorflow;
+ return handle;
}
public unsafe Tensor(byte[][] buffer, long[] shape)
@@ -560,11 +529,12 @@ public unsafe Tensor(byte[][] buffer, long[] shape)
int size = 0;
foreach (var b in buffer)
{
- size += (int)TF_StringEncodedSize((UIntPtr)b.Length);
+ size += (int) TF_StringEncodedSize((UIntPtr) b.Length);
}
+
int totalSize = size + buffer.Length * 8;
ulong offset = 0;
- IntPtr handle = TF_AllocateTensor(TF_DataType.TF_STRING, shape, shape.Length, (UIntPtr)totalSize);
+ IntPtr handle = TF_AllocateTensor(TF_DataType.TF_STRING, shape, shape.Length, (UIntPtr) totalSize);
// Clear offset table
IntPtr pOffset = TF_TensorData(handle);
@@ -572,15 +542,15 @@ public unsafe Tensor(byte[][] buffer, long[] shape)
IntPtr dstLimit = pOffset + totalSize;
for (int i = 0; i < buffer.Length; i++)
{
- Marshal.WriteInt64(pOffset, (long)offset);
+ Marshal.WriteInt64(pOffset, (long) offset);
using (var status = new Status())
{
fixed (byte* src = &buffer[i][0])
{
- var written = TF_StringEncode(src, (UIntPtr)buffer[i].Length, (sbyte*)dst, (UIntPtr)(dstLimit.ToInt64() - dst.ToInt64()), status);
+ var written = TF_StringEncode(src, (UIntPtr) buffer[i].Length, (sbyte*) dst, (UIntPtr) (dstLimit.ToInt64() - dst.ToInt64()), status);
status.Check(true);
pOffset += 8;
- dst += (int)written;
+ dst += (int) written;
offset += written;
}
}
@@ -612,24 +582,26 @@ public Tensor(Operation op, int value_index, TF_DataType dtype)
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[SuppressMessage("ReSharper", "LocalVariableHidesMember")]
- protected unsafe IntPtr CreateTensorWithoutCopying(TF_DataType dt, long[] shape, Array data, int element_size)
+ protected unsafe IntPtr CreateTensorFromArray(TF_DataType dt, long[] shape, Array data, int element_size)
{
if (dt == TF_DataType.TF_STRING && data is byte[] buffer)
{
- var size = c_api.TF_StringEncodedSize((UIntPtr)buffer.Length);
- var handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr)((ulong)size + 8));
+ var size = c_api.TF_StringEncodedSize((UIntPtr) buffer.Length);
+ var handle = TF_AllocateTensor(TF_DataType.TF_STRING, IntPtr.Zero, 0, (UIntPtr) ((ulong) size + 8));
+ AllocationType = AllocationType.Tensorflow;
IntPtr tensor = c_api.TF_TensorData(handle);
Marshal.WriteInt64(tensor, 0);
var status = new Status();
fixed (byte* src = buffer)
- c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status);
+ c_api.TF_StringEncode(src, (UIntPtr) buffer.Length, (sbyte*) (tensor + sizeof(Int64)), size, status);
status.Check(true);
return handle;
}
- return CreateTensorWithoutCopying(dt, shape, data, 0, data.Length, element_size);
+
+ return CreateTensorFromArray(dt, shape, data, 0, data.Length, element_size);
}
///
@@ -647,67 +619,19 @@ protected unsafe IntPtr CreateTensorWithoutCopying(TF_DataType dt, long[] shape,
/// specified dimensions.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- protected unsafe IntPtr CreateTensorWithoutCopying(TF_DataType dt, long[] shape, Array data, int start, int count, int element_size)
+ protected IntPtr CreateTensorFromArray(TF_DataType dt, long[] shape, Array data, int start, int count, int element_size)
{
if (start < 0 || start > data.Length - count)
throw new ArgumentException($"Array length {data.Length} does not match the given shape {new Shape(shape.Cast().ToArray())}");
// get a handle to the pinned array which we will pass on to the tensor computation engine to use
var gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
- _deallocatorArgs = new DeallocatorArgs() { gc_handle = GCHandle.ToIntPtr(gcHandle) };
- if (shape == null || shape.Length == 0)
- return TF_NewTensor(dt, new long[0], 0, gcHandle.AddrOfPinnedObject() + start * element_size, (UIntPtr)(count * element_size), _gcHandleDeallocator, ref _deallocatorArgs);
- else
- return TF_NewTensor(dt, shape, shape.Length, gcHandle.AddrOfPinnedObject() + start * element_size, (UIntPtr)(count * element_size), _gcHandleDeallocator, ref _deallocatorArgs);
- }
-
- [MonoPInvokeCallback(typeof(Deallocator))]
- internal static void FreeHGlobalMemory(IntPtr dataPtr, IntPtr len, ref DeallocatorArgs args)
- {
- if (args.deallocator_called || dataPtr == IntPtr.Zero)
- return;
-
- // NumSharp will dispose
- Marshal.FreeHGlobal(dataPtr);
- args.deallocator_called = true;
- }
+ AllocationType = AllocationType.GCHandle;
+ AllocationHandle = gcHandle;
- [MonoPInvokeCallback(typeof(Deallocator))]
- internal static void FreeGCHandle(IntPtr dataPtr, IntPtr len, ref DeallocatorArgs args)
- {
- if (args.deallocator_called || args.gc_handle == IntPtr.Zero)
- return;
- // note: since the ptr given to tensorflow is just the addr of the pinned object we can not directly free it! we need to free the gcHandle instead
- GCHandle.FromIntPtr(args.gc_handle).Free();
- args.deallocator_called = true;
- }
-
- [MonoPInvokeCallback(typeof(Deallocator))]
- internal static void FreeNothing(IntPtr dataPtr, IntPtr len, ref DeallocatorArgs args)
- {
- args.deallocator_called = true;
+ if (shape == null || shape.Length == 0)
+ return TF_NewTensor(dt, new long[0], 0, gcHandle.AddrOfPinnedObject() + start * element_size, (UIntPtr) (count * element_size));
+ return TF_NewTensor(dt, shape, shape.Length, gcHandle.AddrOfPinnedObject() + start * element_size, (UIntPtr) (count * element_size));
}
-
}
-
- ///
- /// This attribute can be applied to callback functions that will be invoked
- /// from unmanaged code to managed code.
- ///
- ///
- ///
- /// [TensorFlow.MonoPInvokeCallback (typeof (BufferReleaseFunc))]
- /// internal static void MyFreeFunc (IntPtr data, IntPtr length){..}
- ///
- ///
- public sealed class MonoPInvokeCallbackAttribute : Attribute
- {
- ///
- /// Use this constructor to annotate the type of the callback function that
- /// will be invoked from unmanaged code.
- ///
- /// T.
- public MonoPInvokeCallbackAttribute(Type t) { }
- }
-
-}
+}
\ No newline at end of file
diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs
index 75cba69eb..aa2dc6d5f 100644
--- a/src/TensorFlowNET.Core/Tensors/Tensor.cs
+++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs
@@ -555,9 +555,35 @@ public override string ToString()
return $"tf.Tensor '{name}' shape=({string.Join(",", shape)}) dtype={dtype}";
}
+ ///
+ /// Dispose any managed resources.
+ ///
+ /// Equivalent to what you would perform inside
+ protected override void DisposeManagedResources()
+ {
+ AllocationReferenceHolder = null;
+ }
+
+ [SuppressMessage("ReSharper", "ConvertIfStatementToSwitchStatement")]
protected override void DisposeUnmanagedResources(IntPtr handle)
{
c_api.TF_DeleteTensor(handle);
+
+ if (AllocationHandle == null)
+ return;
+
+ if (AllocationType == AllocationType.GCHandle)
+ {
+ ((GCHandle) AllocationHandle).Free();
+ AllocationHandle = null;
+ AllocationType = AllocationType.None;
+ } else if (AllocationType == AllocationType.Marshal)
+ {
+ Marshal.FreeHGlobal((IntPtr) AllocationHandle);
+ AllocationHandle = null;
+ AllocationType = AllocationType.None;
+ } else
+ throw new InvalidOperationException($"Tensor.AllocationHandle is not null ({AllocationHandle}) but AllocationType is not matched to a C# allocation type ({AllocationType}).");
}
public bool IsDisposed => _disposed;
diff --git a/src/TensorFlowNET.Core/Tensors/c_api.tensor.cs b/src/TensorFlowNET.Core/Tensors/c_api.tensor.cs
index 6b20b34ff..be5f39326 100644
--- a/src/TensorFlowNET.Core/Tensors/c_api.tensor.cs
+++ b/src/TensorFlowNET.Core/Tensors/c_api.tensor.cs
@@ -15,6 +15,7 @@ limitations under the License.
******************************************************************************/
using System;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Tensorflow
@@ -77,6 +78,51 @@ public partial class c_api
[DllImport(TensorFlowLibName)]
public static extern IntPtr TF_NewTensor(TF_DataType dataType, long[] dims, int num_dims, IntPtr data, UIntPtr len, Deallocator deallocator, ref DeallocatorArgs deallocator_arg);
+ ///
+ /// Return a new tensor that holds the bytes data[0,len-1]
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// num_bytes, ex: 6 * sizeof(float)
+ ///
+ ///
+ ///
+ [DllImport(TensorFlowLibName)]
+ public static extern IntPtr TF_NewTensor(TF_DataType dataType, long[] dims, int num_dims, IntPtr data, UIntPtr len, Deallocator deallocator, IntPtr deallocator_arg);
+
+ ///
+ /// Return a new tensor that holds the bytes data[0,len-1]
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// num_bytes, ex: 6 * sizeof(float)
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe IntPtr TF_NewTensor(TF_DataType dataType, long[] dims, int num_dims, IntPtr data, UIntPtr len)
+ {
+ return TF_NewTensor(dataType, dims, num_dims, data, len, EmptyDeallocator, DeallocatorArgs.Empty);
+ }
+ ///
+ /// Return a new tensor that holds the bytes data[0,len-1]
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// num_bytes, ex: 6 * sizeof(float)
+ ///
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static unsafe IntPtr TF_NewTensor(TF_DataType dataType, long[] dims, int num_dims, void* data, UIntPtr len)
+ {
+ return TF_NewTensor(dataType, dims, num_dims, new IntPtr(data), len);
+ }
+
///
/// Return the number of dimensions that the tensor has.
///
@@ -159,5 +205,32 @@ public partial class c_api
[DllImport(TensorFlowLibName)]
public static extern unsafe UIntPtr TF_StringDecode(byte* src, UIntPtr src_len, byte** dst, UIntPtr* dst_len, IntPtr status);
+
+
+ public static c_api.Deallocator EmptyDeallocator = FreeNothingDeallocator;
+
+ [MonoPInvokeCallback(typeof(c_api.Deallocator))]
+ private static void FreeNothingDeallocator(IntPtr dataPtr, IntPtr len, ref c_api.DeallocatorArgs args)
+ { }
+
+ ///
+ /// This attribute can be applied to callback functions that will be invoked
+ /// from unmanaged code to managed code.
+ ///
+ ///
+ ///
+ /// [TensorFlow.MonoPInvokeCallback (typeof (BufferReleaseFunc))]
+ /// internal static void MyFreeFunc (IntPtr data, IntPtr length){..}
+ ///
+ ///
+ public sealed class MonoPInvokeCallbackAttribute : Attribute
+ {
+ ///
+ /// Use this constructor to annotate the type of the callback function that
+ /// will be invoked from unmanaged code.
+ ///
+ /// T.
+ public MonoPInvokeCallbackAttribute(Type t) { }
+ }
}
}
diff --git a/test/TensorFlowNET.UnitTest/TensorTest.cs b/test/TensorFlowNET.UnitTest/TensorTest.cs
index 11557f148..42e03a1ea 100644
--- a/test/TensorFlowNET.UnitTest/TensorTest.cs
+++ b/test/TensorFlowNET.UnitTest/TensorTest.cs
@@ -4,6 +4,7 @@
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
+using FluentAssertions;
using Tensorflow;
using static Tensorflow.Binding;
@@ -12,77 +13,63 @@ namespace TensorFlowNET.UnitTest
[TestClass]
public class TensorTest : CApiTest
{
- [Ignore("Not for mult-thread")]
- public void TensorDeallocationThreadSafety()
- {
- var tensors = new Tensor[1000];
- foreach (var i in range(1000))
- {
- tensors[i] = new Tensor(new int[1000]);
- }
- SemaphoreSlim s = new SemaphoreSlim(0, 2);
- SemaphoreSlim s_done = new SemaphoreSlim(0, 2);
-
- var t1 = new Thread(() =>
- {
- s.Wait();
- foreach (var t in tensors)
- t.Dispose();
- s_done.Release();
- });
-
- var t2 = new Thread(() =>
- {
- s.Wait();
- foreach (var t in tensors)
- t.Dispose();
- s_done.Release();
- });
-
- t1.Start();
- t2.Start();
- s.Release(2);
- s_done.Wait();
- s_done.Wait();
-
- foreach (var t in tensors)
- Assert.IsTrue(t.IsDisposed);
- }
-
[TestMethod]
public unsafe void TensorFromFixed()
{
var array = new float[1000];
var span = new Span(array, 100, 500);
- fixed (float* ptr=&MemoryMarshal.GetReference(span))
+ fixed (float* ptr = &MemoryMarshal.GetReference(span))
{
- using (var t = new Tensor((IntPtr)ptr, new long[] {span.Length}, tf.float32, 4*span.Length))
+ using (var t = new Tensor((IntPtr) ptr, new long[] {span.Length}, tf.float32, 4 * span.Length))
{
Assert.IsFalse(t.IsDisposed);
- Assert.IsFalse(t.IsMemoryOwner);
Assert.AreEqual(2000, (int) t.bytesize);
}
}
+
fixed (float* ptr = &array[0])
{
- using (var t = new Tensor((IntPtr)ptr, new long[] { array.Length }, tf.float32, 4 * array.Length))
+ using (var t = new Tensor((IntPtr) ptr, new long[] {array.Length}, tf.float32, 4 * array.Length))
{
Assert.IsFalse(t.IsDisposed);
- Assert.IsFalse(t.IsMemoryOwner);
- Assert.AreEqual(4000, (int)t.bytesize);
+ Assert.AreEqual(4000, (int) t.bytesize);
}
}
}
+ [TestMethod]
+ public unsafe void TensorFromArray()
+ {
+ var array = new float[1000];
+ using (var t = new Tensor(array, new long[] {array.Length}, tf.float32))
+ {
+ Assert.IsFalse(t.IsDisposed);
+ Assert.AreEqual(1000 * sizeof(float), (int) t.bytesize);
+ }
+
+ using (var t = new Tensor(new float[] {1}, new long[] {1}, tf.float32))
+ {
+ Assert.IsFalse(t.IsDisposed);
+ Assert.AreEqual(1 * sizeof(float), (int) t.bytesize);
+ }
+
+ using (var t = new Tensor(new float[] {1}, null, tf.float32))
+ {
+ Assert.IsFalse(t.IsDisposed);
+ Assert.AreEqual(1 * sizeof(float), (int) t.bytesize);
+ t.shape.Should().BeEmpty();
+ }
+ }
+
[TestMethod]
public void AllocateTensor()
{
ulong num_bytes = 6 * sizeof(float);
- long[] dims = { 2, 3 };
+ long[] dims = {2, 3};
Tensor t = c_api.TF_AllocateTensor(TF_DataType.TF_FLOAT, dims, 2, num_bytes);
EXPECT_EQ(TF_DataType.TF_FLOAT, t.dtype);
EXPECT_EQ(2, t.NDims);
- EXPECT_EQ((int)dims[0], t.shape[0]);
+ EXPECT_EQ((int) dims[0], t.shape[0]);
EXPECT_EQ(num_bytes, t.bytesize);
t.Dispose();
}
@@ -98,7 +85,7 @@ public void MaybeMove()
NDArray nd = np.array(2, 3);
Tensor t = new Tensor(nd);
Tensor o = t.MaybeMove();
- ASSERT_TRUE(o == IntPtr.Zero); // It is unsafe to move memory TF might not own.
+ ASSERT_TRUE(o == IntPtr.Zero); // It is unsafe to move memory TF might not own.
t.Dispose();
}
@@ -116,10 +103,10 @@ public void Tensor()
EXPECT_EQ(tensor.dtype, TF_DataType.TF_FLOAT);
EXPECT_EQ(tensor.rank, nd.ndim);
- EXPECT_EQ((int)tensor.shape[0], nd.shape[0]);
- EXPECT_EQ((int)tensor.shape[1], nd.shape[1]);
- EXPECT_EQ(tensor.bytesize, (ulong)nd.size * sizeof(float));
- Assert.IsTrue(Enumerable.SequenceEqual(nd.Data(), new float[] { 1, 2, 3, 4, 5, 6 }));
+ EXPECT_EQ((int) tensor.shape[0], nd.shape[0]);
+ EXPECT_EQ((int) tensor.shape[1], nd.shape[1]);
+ EXPECT_EQ(tensor.bytesize, (ulong) nd.size * sizeof(float));
+ Assert.IsTrue(Enumerable.SequenceEqual(nd.Data(), new float[] {1, 2, 3, 4, 5, 6}));
}
///
@@ -148,7 +135,7 @@ public void SetShape()
EXPECT_EQ(-1, num_dims);
// Set the shape to be 2 x Unknown
- long[] dims = { 2, -1 };
+ long[] dims = {2, -1};
c_api.TF_GraphSetTensorShape(graph, feed_out_0, dims, dims.Length, s);
Assert.IsTrue(s.Code == TF_Code.TF_OK);
num_dims = c_api.TF_GraphGetTensorNumDims(graph, feed_out_0, s);
@@ -177,8 +164,8 @@ public void SetShape()
c_api.TF_GraphGetTensorShape(graph, feed_out_0, returned_dims, num_dims, s);
Assert.IsTrue(s.Code == TF_Code.TF_OK);
EXPECT_EQ(2, num_dims);
- EXPECT_EQ(2, (int)returned_dims[0]);
- EXPECT_EQ(3, (int)returned_dims[1]);
+ EXPECT_EQ(2, (int) returned_dims[0]);
+ EXPECT_EQ(3, (int) returned_dims[1]);
// Try to set 'unknown' with same rank on the shape and see that
// it doesn't change.
@@ -189,8 +176,8 @@ public void SetShape()
c_api.TF_GraphGetTensorShape(graph, feed_out_0, returned_dims, num_dims, s);
Assert.IsTrue(s.Code == TF_Code.TF_OK);
EXPECT_EQ(2, num_dims);
- EXPECT_EQ(2, (int)returned_dims[0]);
- EXPECT_EQ(3, (int)returned_dims[1]);
+ EXPECT_EQ(2, (int) returned_dims[0]);
+ EXPECT_EQ(3, (int) returned_dims[1]);
// Try to fetch a shape with the wrong num_dims
c_api.TF_GraphGetTensorShape(graph, feed_out_0, returned_dims, 5, s);
@@ -216,4 +203,4 @@ public void SetShape()
s.Dispose();
}
}
-}
+}
\ No newline at end of file