From 33d82e8817b31950e054c17dfb425899a2fd5bd2 Mon Sep 17 00:00:00 2001 From: Oceania2018 Date: Thu, 1 Aug 2019 13:46:08 -0500 Subject: [PATCH] GC.SuppressFinalize for TF object. --- src/TensorFlowNET.Core/Graphs/Graph.Export.cs | 5 +- src/TensorFlowNET.Core/Graphs/Graph.Import.cs | 14 +- src/TensorFlowNET.Core/Graphs/Graph.cs | 12 +- .../Sessions/BaseSession.cs | 5 +- src/TensorFlowNET.Core/Sessions/Session.cs | 17 +- .../TensorFlowNET.Core.csproj | 11 +- .../Tensors/Tensor.Creation.cs | 1355 +++++++++-------- src/TensorFlowNET.Core/Tensors/Tensor.cs | 44 +- .../Tensors/c_api.tensor.cs | 6 + 9 files changed, 801 insertions(+), 668 deletions(-) diff --git a/src/TensorFlowNET.Core/Graphs/Graph.Export.cs b/src/TensorFlowNET.Core/Graphs/Graph.Export.cs index 67b93191..60657038 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.Export.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.Export.cs @@ -31,8 +31,9 @@ namespace Tensorflow private GraphDef _as_graph_def(bool add_shapes = false) { - var buffer = ToGraphDef(Status); - Status.Check(); + var status = new Status(); + var buffer = ToGraphDef(status); + status.Check(); var def = GraphDef.Parser.ParseFrom(buffer); buffer.Dispose(); diff --git a/src/TensorFlowNET.Core/Graphs/Graph.Import.cs b/src/TensorFlowNET.Core/Graphs/Graph.Import.cs index 7fcfdbd7..af7ebfd1 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.Import.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.Import.cs @@ -43,16 +43,20 @@ namespace Tensorflow var bytes = File.ReadAllBytes(file_path); var graph_def = new Tensorflow.Buffer(bytes); var opts = c_api.TF_NewImportGraphDefOptions(); - c_api.TF_GraphImportGraphDef(_handle, graph_def, opts, Status); - return Status; + var status = new Status(); + c_api.TF_GraphImportGraphDef(_handle, graph_def, opts, status); + return status; } - public Status Import(byte[] bytes) + public Status Import(byte[] bytes, string prefix = "") { var graph_def = new Tensorflow.Buffer(bytes); var opts = c_api.TF_NewImportGraphDefOptions(); - c_api.TF_GraphImportGraphDef(_handle, graph_def, opts, Status); - return Status; + c_api.TF_ImportGraphDefOptionsSetPrefix(opts, prefix); + var status = new Status(); + c_api.TF_GraphImportGraphDef(_handle, graph_def, opts, status); + c_api.TF_DeleteImportGraphDefOptions(opts); + return status; } public static Graph ImportFromPB(string file_path, string name = null) diff --git a/src/TensorFlowNET.Core/Graphs/Graph.cs b/src/TensorFlowNET.Core/Graphs/Graph.cs index 08ed95af..2acc0bb7 100644 --- a/src/TensorFlowNET.Core/Graphs/Graph.cs +++ b/src/TensorFlowNET.Core/Graphs/Graph.cs @@ -88,8 +88,7 @@ namespace Tensorflow private string _graph_key; public string graph_key => _graph_key; public string _last_loss_reduction; - public bool _is_loss_scaled_by_optimizer { get; set; } - public Status Status { get; } + public bool _is_loss_scaled_by_optimizer { get; set; } /// /// True if the graph is considered "finalized". In that case no @@ -107,7 +106,6 @@ namespace Tensorflow public Graph() { _handle = c_api.TF_NewGraph(); - Status = new Status(); _nodes_by_id = new Dictionary(); _nodes_by_name = new Dictionary(); _names_in_use = new Dictionary(); @@ -117,7 +115,6 @@ namespace Tensorflow public Graph(IntPtr handle) { _handle = handle; - Status = new Status(); _nodes_by_id = new Dictionary(); _nodes_by_name = new Dictionary(); _names_in_use = new Dictionary(); @@ -448,7 +445,12 @@ namespace Tensorflow public void Dispose() { - // c_api.TF_DeleteGraph(_handle); + if (_handle != IntPtr.Zero) + c_api.TF_DeleteGraph(_handle); + + _handle = IntPtr.Zero; + + GC.SuppressFinalize(this); } /// diff --git a/src/TensorFlowNET.Core/Sessions/BaseSession.cs b/src/TensorFlowNET.Core/Sessions/BaseSession.cs index deb82b51..47a891d6 100644 --- a/src/TensorFlowNET.Core/Sessions/BaseSession.cs +++ b/src/TensorFlowNET.Core/Sessions/BaseSession.cs @@ -32,20 +32,19 @@ namespace Tensorflow protected int _current_version; protected byte[] _target; protected IntPtr _session; - public Status Status; public Graph graph => _graph; public BaseSession(string target = "", Graph g = null, SessionOptions opts = null) { _graph = g is null ? ops.get_default_graph() : g; - + _graph.as_default(); _target = UTF8Encoding.UTF8.GetBytes(target); SessionOptions newOpts = null; if (opts == null) newOpts = c_api.TF_NewSessionOptions(); - Status = new Status(); + var Status = new Status(); _session = c_api.TF_NewSession(_graph, opts ?? newOpts, Status); diff --git a/src/TensorFlowNET.Core/Sessions/Session.cs b/src/TensorFlowNET.Core/Sessions/Session.cs index 191730d0..3e7dca84 100644 --- a/src/TensorFlowNET.Core/Sessions/Session.cs +++ b/src/TensorFlowNET.Core/Sessions/Session.cs @@ -37,7 +37,7 @@ namespace Tensorflow : base("", g, opts) { if (s == null) - s = Status; + s = new Status(); } public Session as_default() @@ -83,8 +83,19 @@ namespace Tensorflow public void Dispose() { - c_api.TF_DeleteSession(_session, Status); - Status.Dispose(); + if (_session != IntPtr.Zero) + { + var status = new Status(); + c_api.TF_DeleteSession(_session, status); + } + + _session = IntPtr.Zero; + GC.SuppressFinalize(this); + } + + ~Session() + { + Dispose(); } public void __enter__() diff --git a/src/TensorFlowNET.Core/TensorFlowNET.Core.csproj b/src/TensorFlowNET.Core/TensorFlowNET.Core.csproj index 7374f82f..36ff24ac 100644 --- a/src/TensorFlowNET.Core/TensorFlowNET.Core.csproj +++ b/src/TensorFlowNET.Core/TensorFlowNET.Core.csproj @@ -5,7 +5,7 @@ TensorFlow.NET Tensorflow 1.14.0 - 0.10.4 + 0.10.7 Haiping Chen, Meinrad Recheis SciSharp STACK true @@ -17,7 +17,7 @@ TensorFlow, NumSharp, SciSharp, MachineLearning, TensorFlow.NET, C# Google's TensorFlow full binding in .NET Standard. Docs: https://tensorflownet.readthedocs.io - 0.10.4.0 + 0.10.7.0 Changes since v0.9.0: 1. Added full connected Convolution Neural Network example. @@ -31,9 +31,12 @@ Docs: https://tensorflownet.readthedocs.io 9. Fix strided_slice_grad type convention error. 10. Add AbsGrad. 11. Fix Session.LoadFromSavedModel(string). -12. Add Tensor operator overloads. +12. Add Tensor operator overloads. +13. Fix default graph and operation issue when import model. +14. Fix TF_String endcode and decode. +15. Fix Tensor memory leak. 7.2 - 0.10.4.0 + 0.10.7.0 LICENSE true true diff --git a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs index f5ac5f77..a104f066 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.Creation.cs @@ -1,648 +1,717 @@ -/***************************************************************************** - Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved. - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -******************************************************************************/ - -using NumSharp; -using System; -using System.Linq; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Text; -using static Tensorflow.c_api; - -namespace Tensorflow -{ - public partial class Tensor - { - /// - /// true if unmanaged buffer has been freed. - /// - private bool _deallocator_called => _deallocatorArgs.deallocator_called; - - /// - /// true if the Tensor was created from a managed array - /// - private bool _isPinnedArray => _deallocatorArgs.gc_handle != IntPtr.Zero; - - /// - /// 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 - /// - public bool IsMemoryOwner { get; private set; } - - /// - /// This holds values that are used by the unmanaged deallocator callback - /// - 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 - static Deallocator _hGlobalDeallocator = FreeHGlobalMemory; - static Deallocator _gcHandleDeallocator = FreeGCHandle; - private static Deallocator _nothingDeallocator = FreeNothing; - - /// - /// Create a Tensor object from an existing TF handle - /// - /// - public Tensor(IntPtr handle) - { - _handle = handle; - IsMemoryOwner = false; - } - - /// - /// 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 Tensor(IntPtr 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; - } - -#if _REGEN - %types=["sbyte", "byte", "short", "ushort", "int", "uint", "long", "ulong", "float", "double", "Complex"] - %foreach types% - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - % -#else - - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } - - /// - /// 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; - } -#endif - - /// - /// Create a string Tensor from the given string - /// - public unsafe Tensor(string str) - { - 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)); - - IntPtr tensor = c_api.TF_TensorData(handle); - Marshal.WriteInt64(tensor, 0); - fixed (byte* src = &buffer[0]) - c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status); - _handle = handle; - status.Check(true); - } - - public unsafe Tensor(NDArray nd, TF_DataType? tensorDType = null) - { - if (tensorDType == TF_DataType.TF_STRING && nd.dtype.Name == "Byte") - { - var buffer = nd.Data(); - 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[0]) - c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status); - - status.Check(true); - _handle=handle; - IsMemoryOwner = false; - return; - } - _handle = Allocate(nd, tensorDType: tensorDType); - IsMemoryOwner = true; - } - - private unsafe IntPtr Allocate(NDArray nd, TF_DataType? tensorDType = null) - { - IntPtr dotHandle = IntPtr.Zero; - int buffersize = 0; - - if (nd.dtype.Name != "String") +/***************************************************************************** + Copyright 2018 The TensorFlow.NET Authors. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +******************************************************************************/ + +using NumSharp; +using System; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using static Tensorflow.c_api; + +namespace Tensorflow +{ + public partial class Tensor + { + /// + /// true if unmanaged buffer has been freed. + /// + private bool _deallocator_called => _deallocatorArgs.deallocator_called; + + /// + /// true if the Tensor was created from a managed array + /// + private bool _isPinnedArray => _deallocatorArgs.gc_handle != IntPtr.Zero; + + /// + /// 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 + /// + public bool IsMemoryOwner { get; private set; } + + /// + /// This holds values that are used by the unmanaged deallocator callback + /// + 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 + static Deallocator _hGlobalDeallocator = FreeHGlobalMemory; + static Deallocator _gcHandleDeallocator = FreeGCHandle; + private static Deallocator _nothingDeallocator = FreeNothing; + + /// + /// Create a Tensor object from an existing TF handle + /// + /// + public Tensor(IntPtr handle) + { + _handle = handle; + IsMemoryOwner = false; + } + + /// + /// 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 Tensor(IntPtr 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; + } + +#if _REGEN + %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 + /// + 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + % +#else + + + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } + + /// + /// 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; + } +#endif + + /// + /// Create a string Tensor from the given string + /// + 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)); + + IntPtr tensor = c_api.TF_TensorData(handle); + Marshal.WriteInt64(tensor, 0); + fixed (byte* src = &buffer[0]) + c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status); + _handle = handle; + status.Check(true); + } + + public unsafe Tensor(NDArray nd, TF_DataType? tensorDType = null) + { + if (tensorDType == TF_DataType.TF_STRING && nd.dtype.Name == "Byte") + { + var buffer = nd.Data(); + 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); + + var status = new Status(); + fixed (byte* src = &buffer[0]) + c_api.TF_StringEncode(src, (UIntPtr)buffer.Length, (sbyte*)(tensor + sizeof(Int64)), size, status); + + status.Check(true); + _handle=handle; + IsMemoryOwner = false; + return; + } + _handle = Allocate(nd, tensorDType: tensorDType); + IsMemoryOwner = true; + } + + private unsafe IntPtr Allocate(NDArray nd, TF_DataType? tensorDType = null) + { + IntPtr dotHandle = IntPtr.Zero; + int buffersize = 0; + + if (nd.dtype.Name != "String") + { + buffersize = (nd.size * nd.dtypesize); + dotHandle = Marshal.AllocHGlobal(buffersize); + } + + var dataType = ToTFDataType(nd.dtype); + // shape + var dims = nd.shape.Select(x => (long)x).ToArray(); + var nd1 = nd.ravel(); + switch (nd.dtype.Name) + { + case "Boolean": + var boolVals = Array.ConvertAll(nd1.Data(), x => Convert.ToByte(x)); + Marshal.Copy(boolVals, 0, dotHandle, nd.size); + break; + case "Int16": + Marshal.Copy(nd1.Data(), 0, dotHandle, nd.size); + break; + case "Int32": + Marshal.Copy(nd1.Data(), 0, dotHandle, nd.size); + break; + case "Int64": + Marshal.Copy(nd1.Data(), 0, dotHandle, nd.size); + break; + case "Single": + Marshal.Copy(nd1.Data(), 0, dotHandle, nd.size); + break; + case "Double": + Marshal.Copy(nd1.Data(), 0, dotHandle, nd.size); + break; + case "Byte": + Marshal.Copy(nd1.Data(), 0, dotHandle, nd.size); + break; + case "String": + return new Tensor(UTF8Encoding.UTF8.GetBytes(nd.Data(0)), TF_DataType.TF_STRING); + default: + throw new NotImplementedException($"Marshal.Copy failed for {nd.dtype.Name}."); + } + var tfHandle = c_api.TF_NewTensor(dataType, + dims, + dims.Length, + dotHandle, + (UIntPtr)buffersize, + _hGlobalDeallocator, + ref _deallocatorArgs); + + return tfHandle; + } + + public unsafe Tensor(byte[][] buffer, long[] shape) + { + int size = 0; + foreach (var b in buffer) { - buffersize = (nd.size * nd.dtypesize); - dotHandle = Marshal.AllocHGlobal(buffersize); + size += (int)TF_StringEncodedSize((UIntPtr)b.Length); } - - var dataType = ToTFDataType(nd.dtype); - // shape - var dims = nd.shape.Select(x => (long)x).ToArray(); - var nd1 = nd.ravel(); - switch (nd.dtype.Name) + int totalSize = size + buffer.Length * 8; + ulong offset = 0; + IntPtr handle = TF_AllocateTensor(TF_DataType.TF_STRING, shape, shape.Length, (UIntPtr)totalSize); + + // Clear offset table + IntPtr pOffset = TF_TensorData(handle); + IntPtr dst = pOffset + buffer.Length * 8; + IntPtr dstLimit = pOffset + totalSize; + for (int i = 0; i < buffer.Length; i++) { - case "Boolean": - var boolVals = Array.ConvertAll(nd1.Data(), x => Convert.ToByte(x)); - Marshal.Copy(boolVals, 0, dotHandle, nd.size); - break; - case "Int16": - Marshal.Copy(nd1.Data(), 0, dotHandle, nd.size); - break; - case "Int32": - Marshal.Copy(nd1.Data(), 0, dotHandle, nd.size); - break; - case "Int64": - Marshal.Copy(nd1.Data(), 0, dotHandle, nd.size); - break; - case "Single": - Marshal.Copy(nd1.Data(), 0, dotHandle, nd.size); - break; - case "Double": - Marshal.Copy(nd1.Data(), 0, dotHandle, nd.size); - break; - case "Byte": - Marshal.Copy(nd1.Data(), 0, dotHandle, nd.size); - break; - case "String": - return new Tensor(UTF8Encoding.UTF8.GetBytes(nd.Data(0)), TF_DataType.TF_STRING); - default: - throw new NotImplementedException($"Marshal.Copy failed for {nd.dtype.Name}."); + 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); + status.Check(true); + pOffset += 8; + dst += (int)written; + offset += written; + } + } } - var tfHandle = c_api.TF_NewTensor(dataType, - dims, - dims.Length, - dotHandle, - (UIntPtr)buffersize, - _hGlobalDeallocator, - ref _deallocatorArgs); - - return tfHandle; - } - - public Tensor(Operation op, int value_index, TF_DataType dtype) - { - _op = op; - _value_index = value_index; - _dtype = dtype; - _id = ops.uid(); - } - - - /// - /// Creates a new tensor from the given array without copying memory. The array is pinned down and the pointer passed on. - /// - /// Represents the tensor shape. - /// The linear array of data, the data must fit in the tensor with the specified dimensions. - /// The number of bytes in memory of a single array element - /// - /// Use the FromBuffer method to create a tensor that has the specified dimensions - /// and is initialized with data from the data array. The data is copied starting - /// at the start offset, for count bytes and is laid out into the tensor following the - /// specified dimensions. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected unsafe IntPtr CreateTensorWithoutCopying(TF_DataType dt, long[] shape, Array data, int element_size) - { - if (dt == TF_DataType.TF_STRING && data is byte[]) - { - var buffer = (byte[])data; - 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[0]) - 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); - } - - /// - /// Creates a new tensor from a subsection of the given array without copying memory. The array is pinned down and the pointer passed on. - /// - /// Represents the tensor shape. - /// The linear array of data, the data must fit in the tensor with the specified dimensions. - /// The offset into the provided data array where the data resides. - /// The number of elements to copy from data. - /// The number of bytes in memory of a single array element - /// - /// Use the FromBuffer method to create a tensor that has the specified dimensions - /// and is initialized with data from the data array. The data is copied starting - /// at the start offset, for count bytes and is laid out into the tensor following the - /// specified dimensions. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - protected unsafe IntPtr CreateTensorWithoutCopying(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) - return; - Marshal.FreeHGlobal(dataPtr); - args.deallocator_called = true; - } - - [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; - } - - } - - /// - /// 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) { } - } - -} + _handle = handle; + } + + public Tensor(Operation op, int value_index, TF_DataType dtype) + { + _op = op; + _value_index = value_index; + _dtype = dtype; + _id = ops.uid(); + } + + + /// + /// Creates a new tensor from the given array without copying memory. The array is pinned down and the pointer passed on. + /// + /// Represents the tensor shape. + /// The linear array of data, the data must fit in the tensor with the specified dimensions. + /// The number of bytes in memory of a single array element + /// + /// Use the FromBuffer method to create a tensor that has the specified dimensions + /// and is initialized with data from the data array. The data is copied starting + /// at the start offset, for count bytes and is laid out into the tensor following the + /// specified dimensions. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected unsafe IntPtr CreateTensorWithoutCopying(TF_DataType dt, long[] shape, Array data, int element_size) + { + if (dt == TF_DataType.TF_STRING && data is byte[]) + { + var buffer = (byte[])data; + 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); + + var status = new Status(); + fixed (byte* src = &buffer[0]) + 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); + } + + /// + /// Creates a new tensor from a subsection of the given array without copying memory. The array is pinned down and the pointer passed on. + /// + /// Represents the tensor shape. + /// The linear array of data, the data must fit in the tensor with the specified dimensions. + /// The offset into the provided data array where the data resides. + /// The number of elements to copy from data. + /// The number of bytes in memory of a single array element + /// + /// Use the FromBuffer method to create a tensor that has the specified dimensions + /// and is initialized with data from the data array. The data is copied starting + /// at the start offset, for count bytes and is laid out into the tensor following the + /// specified dimensions. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + protected unsafe IntPtr CreateTensorWithoutCopying(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) + return; + Marshal.FreeHGlobal(dataPtr); + args.deallocator_called = true; + } + + [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; + } + + } + + /// + /// 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/src/TensorFlowNET.Core/Tensors/Tensor.cs b/src/TensorFlowNET.Core/Tensors/Tensor.cs index 54b58122..700673b7 100644 --- a/src/TensorFlowNET.Core/Tensors/Tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/Tensor.cs @@ -19,6 +19,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; +using System.Text; using Tensorflow.Framework; using static Tensorflow.Python; @@ -48,8 +49,6 @@ namespace Tensorflow private int _value_index; public int value_index => _value_index; - private Status status = new Status(); - private TF_DataType _dtype = TF_DataType.DtInvalid; public TF_DataType dtype => _handle == IntPtr.Zero ? _dtype : c_api.TF_TensorType(_handle); @@ -76,6 +75,7 @@ namespace Tensorflow if (_handle == IntPtr.Zero) { + var status = new Status(); c_api.TF_GraphGetTensorShape(op.graph, _as_tf_output(), dims, rank, status); status.Check(); } @@ -90,6 +90,8 @@ namespace Tensorflow set { + var status = new Status(); + if (value == null) c_api.TF_GraphSetTensorShape(this.graph, this._as_tf_output(), null, -1, status); else @@ -131,6 +133,7 @@ namespace Tensorflow { if (_handle == IntPtr.Zero) { + var status = new Status(); var output = _as_tf_output(); return c_api.TF_GraphGetTensorNumDims(op.graph, output, status); } @@ -184,6 +187,41 @@ namespace Tensorflow return data; } + public unsafe string[] StringData() + { + // + // TF_STRING tensors are encoded with a table of 8-byte offsets followed by TF_StringEncode-encoded bytes. + // [offset1, offset2,...,offsetn, s1size, s1bytes, s2size, s2bytes,...,snsize,snbytes] + // + long size = 1; + foreach (var s in TensorShape.Dimensions) + size *= s; + + var buffer = new byte[size][]; + var src = c_api.TF_TensorData(_handle); + var srcLen = (IntPtr)(src.ToInt64() + (long)bytesize); + src += (int)(size * 8); + for (int i = 0; i < buffer.Length; i++) + { + using (var status = new Status()) + { + IntPtr dst = IntPtr.Zero; + UIntPtr dstLen = UIntPtr.Zero; + var read = c_api.TF_StringDecode((byte*)src, (UIntPtr)(srcLen.ToInt64() - src.ToInt64()), (byte**)&dst, &dstLen, status); + status.Check(true); + buffer[i] = new byte[(int)dstLen]; + Marshal.Copy(dst, buffer[i], 0, buffer[i].Length); + src += (int)read; + } + } + + var _str = new string[buffer.Length]; + for (int i = 0; i < _str.Length; i++) + _str[i] = Encoding.UTF8.GetString(buffer[i]); + + return _str; + } + public Tensor MaybeMove() { var tensor = c_api.TF_TensorMaybeMove(_handle); @@ -364,7 +402,7 @@ namespace Tensorflow } if (h != IntPtr.Zero) c_api.TF_DeleteTensor(h); - status.Dispose(); + GC.SuppressFinalize(this); } diff --git a/src/TensorFlowNET.Core/Tensors/c_api.tensor.cs b/src/TensorFlowNET.Core/Tensors/c_api.tensor.cs index cf208ed2..fd240ee7 100644 --- a/src/TensorFlowNET.Core/Tensors/c_api.tensor.cs +++ b/src/TensorFlowNET.Core/Tensors/c_api.tensor.cs @@ -32,6 +32,9 @@ namespace Tensorflow [DllImport(TensorFlowLibName)] public static extern IntPtr TF_AllocateTensor(TF_DataType dtype, IntPtr dims, int num_dims, UIntPtr len); + [DllImport(TensorFlowLibName)] + public static extern IntPtr TF_AllocateTensor(TF_DataType dtype, long[] dims, int num_dims, UIntPtr len); + /// /// returns the sizeof() for the underlying type corresponding to the given TF_DataType enum value. /// @@ -150,5 +153,8 @@ namespace Tensorflow /// [DllImport(TensorFlowLibName)] public static extern ulong TF_StringDecode(IntPtr src, ulong src_len, IntPtr dst, ref ulong dst_len, IntPtr status); + + [DllImport(TensorFlowLibName)] + public static extern unsafe UIntPtr TF_StringDecode(byte* src, UIntPtr src_len, byte** dst, UIntPtr* dst_len, IntPtr status); } }