博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#+无unsafe的非托管大数组(large unmanaged array in c# without 'unsafe' keyword)
阅读量:5861 次
发布时间:2019-06-19

本文共 17789 字,大约阅读时间需要 59 分钟。

C#+无unsafe的非托管大数组(large unmanaged array in c# without 'unsafe' keyword)

+BIT祝威+悄悄在此留下版了个权的信息说:

C#申请一个大数组(Use a large array in C#)

在C#里,有时候我需要能够申请一个很大的数组、使用之、然后立即释放其占用的内存。

Sometimes I need to allocate a large array, use it and then release its memory space immediately.

由于在C#里提供的 int[] array = new int[1000000]; 这样的数组,其内存释放很难由程序员完全控制,在申请一个大数组后,程序可能会变得很慢。

If I use something like  int[] array = new int[1000000]; , it will be difficult to release its memory space by programmer and the app probably runs slower and slower.

特别是在C#+OpenGL编程中,我在使用VAO/VBO时十分需要设计一个非托管的数组,比如在glBufferData时我希望可以使用下面的glBufferData:

Specially in C#+OpenGL routines when I'm using VAO/VBO, I need an unmanaged array for glBufferData:

1         ///  2         /// 设置当前VBO的数据。 3         ///  4         ///  5         ///  6         ///  7         public static void glBufferData(uint target, UnmanagedArrayBase data, uint usage) 8         { 9             GetDelegateFor
()((uint)target,10 data.ByteLength, // 使用非托管数组11 data.Header, // 使用非托管数组12 (uint)usage);13 }14 // ...15 // glBufferData的声明16 private delegate void glBufferData(uint target, int size, IntPtr data, uint usage);

而在指定VBO的数据时,可能是float、vec3等等类型:

And the content in VBO can be float, vec3 and any other structs.

1         ///  2         /// 金字塔的posotion array. 3         ///  4         static vec3[] positions = new vec3[] 5         { 6             new vec3(0.0f, 1.0f, 0.0f), 7             new vec3(-1.0f, -1.0f, 1.0f), 8             // ... 9             new vec3(-1.0f, -1.0f, 1.0f),   10         };            11 //  Create a vertex buffer for the vertex data.12             {13                 uint[] ids = new uint[1];14                 GL.GenBuffers(1, ids);15                 GL.BindBuffer(GL.GL_ARRAY_BUFFER, ids[0]);16                 // 使用vec3作为泛型的非托管数组的参数17                 UnmanagedArray
positionArray = new UnmanagedArray
(positions.Length);18 for (int i = 0; i < positions.Length; i++)19 {20 // 使用this[i]这样的索引方式来读写非托管数组的元素21 positionArray[i] = positions[i];22 }23 GL.BufferData(BufferDataTarget.ArrayBuffer, positionArray, BufferDataUsage.StaticDraw);24 GL.VertexAttribPointer(positionLocation, 3, GL.GL_FLOAT, false, 0, IntPtr.Zero);25 GL.EnableVertexAttribArray(positionLocation);26 }

 

+BIT祝威+悄悄在此留下版了个权的信息说:

UnmanagedArray<T>

所以我设计了这样一个非托管的数组类型:无unsafe,可接收任何struct类型作为泛型参数,可随时释放内存。

So I designed this UnmangedArray<T> : no 'unsafe' keyword, takes any struct as generic parameter, can be released anytime you want.

1     ///   2     /// 元素类型为sbyte, byte, char, short, ushort, int, uint, long, ulong, float, double, decimal, bool或其它struct的非托管数组。  3     /// 
不能使用enum类型作为T。
4 ///
5 ///
sbyte, byte, char, short, ushort, int, uint, long, ulong, float, double, decimal, bool或其它struct, 不能使用enum类型作为T。
6 public class UnmanagedArray
: UnmanagedArrayBase where T : struct 7 { 8 9 ///
10 ///元素类型为sbyte, byte, char, short, ushort, int, uint, long, ulong, float, double, decimal, bool或其它struct的非托管数组。 11 /// 12 ///
13 [MethodImpl(MethodImplOptions.Synchronized)] 14 public UnmanagedArray(int count) 15 : base(count, Marshal.SizeOf(typeof(T))) 16 { 17 } 18 19 ///
20 /// 获取或设置索引为
的元素。 21 ///
22 ///
23 ///
24 public T this[int index] 25 { 26 get 27 { 28 if (index < 0 || index >= this.Count) 29 throw new IndexOutOfRangeException("index of UnmanagedArray is out of range"); 30 31 var pItem = this.Header + (index * elementSize); 32 //var obj = Marshal.PtrToStructure(pItem, typeof(T)); 33 //T result = (T)obj; 34 T result = Marshal.PtrToStructure
(pItem);// works in .net 4.5.1 35 return result; 36 } 37 set 38 { 39 if (index < 0 || index >= this.Count) 40 throw new IndexOutOfRangeException("index of UnmanagedArray is out of range"); 41 42 var pItem = this.Header + (index * elementSize); 43 //Marshal.StructureToPtr(value, pItem, true); 44 Marshal.StructureToPtr
(value, pItem, true);// works in .net 4.5.1 45 } 46 } 47 48 ///
49 /// 按索引顺序依次获取各个元素。 50 /// 51 ///
52 public IEnumerable
GetElements() 53 { 54 if (!this.disposed) 55 { 56 for (int i = 0; i < this.Count; i++) 57 { 58 yield return this[i]; 59 } 60 } 61 } 62 } 63 64 ///
65 /// 非托管数组的基类。 66 /// 67 public abstract class UnmanagedArrayBase : IDisposable 68 { 69 70 ///
71 /// 数组指针。 72 /// 73 public IntPtr Header { get; private set; } 74 75 ///
76 /// 元素数目。 77 /// 78 public int Count { get; private set; } 79 80 ///
81 /// 单个元素的字节数。 82 /// 83 protected int elementSize; 84 85 ///
86 /// 申请到的字节数。(元素数目 * 单个元素的字节数)。 87 /// 88 public int ByteLength 89 { 90 get { return this.Count * this.elementSize; } 91 } 92 93 94 ///
95 /// 非托管数组。 96 /// 97 ///
元素数目。 98 ///
单个元素的字节数。 99 [MethodImpl(MethodImplOptions.Synchronized)]100 protected UnmanagedArrayBase(int elementCount, int elementSize)101 {102 this.Count = elementCount;103 this.elementSize = elementSize;104 105 int memSize = elementCount * elementSize;106 this.Header = Marshal.AllocHGlobal(memSize);107 108 allocatedArrays.Add(this);109 }110 111 private static readonly List
allocatedArrays = new List
();112 113 ///
114 /// 立即释放所有
。115 ///
116 [MethodImpl(MethodImplOptions.Synchronized)]117 public static void FreeAll()118 {119 foreach (var item in allocatedArrays)120 {121 item.Dispose();122 }123 allocatedArrays.Clear();124 }125 126 ~UnmanagedArrayBase()127 {128 Dispose();129 }130 131 #region IDisposable Members132 133 ///
134 /// Internal variable which checks if Dispose has already been called135 /// 136 protected Boolean disposed;137 138 ///
139 /// Releases unmanaged and - optionally - managed resources140 /// 141 ///
true
to release both managed and unmanaged resources;
false
to release only unmanaged resources.142 protected void Dispose(Boolean disposing)143 {144 if (disposed)145 {146 return;147 }148 149 if (disposing)150 {151 //Managed cleanup code here, while managed refs still valid152 }153 //Unmanaged cleanup code here154 IntPtr ptr = this.Header;155 156 if (ptr != IntPtr.Zero)157 {158 this.Count = 0;159 this.Header = IntPtr.Zero;160 Marshal.FreeHGlobal(ptr);161 }162 163 disposed = true;164 }165 166 ///
167 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.168 /// 169 public void Dispose()170 {171 this.Dispose(true);172 GC.SuppressFinalize(this);173 }174 175 #endregion176 177 }
UnmanagedArray

 

+BIT祝威+悄悄在此留下版了个权的信息说:

如何使用(How to use)

UnmanagedArray<T>使用方式十分简单,就像一个普通的数组一样:

Using UnamangedAray<T> is just like a normal array(int[], vec3[], etc.):

1         internal static void TypicalScene() 2         { 3             const int count = 100; 4  5             // 测试float类型 6             var floatArray = new UnmanagedArray
(count); 7 for (int i = 0; i < count; i++) 8 { 9 floatArray[i] = i;10 }11 for (int i = 0; i < count; i++)12 {13 var item = floatArray[i];14 if (item != i)15 { throw new Exception(); }16 }17 18 // 测试int类型19 var intArray = new UnmanagedArray
(count);20 for (int i = 0; i < count; i++)21 {22 intArray[i] = i;23 }24 for (int i = 0; i < count; i++)25 {26 var item = intArray[i];27 if (item != i)28 { throw new Exception(); }29 }30 31 // 测试bool类型32 var boolArray = new UnmanagedArray
(count);33 for (int i = 0; i < count; i++)34 {35 boolArray[i] = i % 2 == 0;36 }37 for (int i = 0; i < count; i++)38 {39 var item = boolArray[i];40 if (item != (i % 2 == 0))41 { throw new Exception(); }42 }43 44 // 测试vec3类型45 var vec3Array = new UnmanagedArray
(count);46 for (int i = 0; i < count; i++)47 {48 vec3Array[i] = new vec3(i * 3 + 0, i * 3 + 1, i * 3 + 2);49 }50 for (int i = 0; i < count; i++)51 {52 var item = vec3Array[i];53 var old = new vec3(i * 3 + 0, i * 3 + 1, i * 3 + 2);54 if (item.x != old.x || item.y != old.y || item.z != old.z)55 { throw new Exception(); }56 }57 58 // 测试foreach59 foreach (var item in vec3Array.GetElements())60 {61 Console.WriteLine(item);62 }63 64 // 释放此数组占用的内存,这之后就不能再使用vec3Array了。65 vec3Array.Dispose();66 67 // 立即释放所有非托管数组占用的内存,这之后就不能再使用上面申请的数组了。68 UnmanagedArrayBase.FreeAll();69 }

快速读写UnmanagedArray<T>

UnmanagedArrayHelper

由于很多时候需要申请和使用很大的UnmanagedArray<T>,直接使用this[index]索引方式速度会偏慢,所以我添加了几个辅助方法,专门解决快速读写UnmanagedArray<T>的问题。

1     public static class UnmanagedArrayHelper 2     { 3         /////  4         ///// 错误    1    无法获取托管类型(“T”)的地址和大小,或无法声明指向它的指针 5         /////  6         ///// 
7 ///// 8 /////
9 //public static unsafe T* FirstElement
(this UnmanagedArray
array) where T : struct10 //{11 // var header = (void*)array.Header;12 // return (T*)header;13 //}14 15 ///
16 /// 获取非托管数组的第一个元素的地址。17 /// 18 ///
19 ///
20 public static unsafe void* FirstElement(this UnmanagedArrayBase array)21 {22 var header = (void*)array.Header;23 24 return header;25 }26 27 public static unsafe void* LastElement(this UnmanagedArrayBase array)28 {29 var last = (void*)(array.Header + (array.ByteLength - array.ByteLength / array.Length));30 31 return last;32 }33 34 ///
35 /// 获取非托管数组的最后一个元素的地址再向后一个单位的地址。36 /// 37 ///
38 ///
39 public static unsafe void* TailAddress(this UnmanagedArrayBase array)40 {41 var tail = (void*)(array.Header + array.ByteLength);42 43 return tail;44 }45 }

如何使用

这个类型实现了3个扩展方法,可以获取UnmanagedArray<T>的第一个元素的位置、最后一个元素的位置、最后一个元素+1的位置。用这种unsafe的方法可以实现C语言一样的读写速度。

下面是一个例子。用unsafe的方式读写UnmanagedArray<T>,速度比this[index]方式快10到70倍。

1         public static void TypicalScene() 2         { 3             int length = 1000000; 4             UnmanagedArray
array = new UnmanagedArray
(length); 5 UnmanagedArray
array2 = new UnmanagedArray
(length); 6 7 long tick = DateTime.Now.Ticks; 8 for (int i = 0; i < length; i++) 9 {10 array[i] = i;11 }12 long totalTicks = DateTime.Now.Ticks - tick;13 14 tick = DateTime.Now.Ticks;15 unsafe16 {17 int* header = (int*)array2.FirstElement();18 int* last = (int*)array2.LastElement();19 int* tailAddress = (int*)array2.TailAddress();20 int value = 0;21 for (int* ptr = header; ptr <= last/*or: ptr < tailAddress*/; ptr++)22 {23 *ptr = value++;24 }25 }26 long totalTicks2 = DateTime.Now.Ticks - tick;27 Console.WriteLine("ticks: {0}, {1}", totalTicks, totalTicks2);// unsafe method works faster.28 29 for (int i = 0; i < length; i++)30 {31 if (array[i] != i)32 {33 Console.WriteLine("something wrong here");34 }35 if (array2[i] != i)36 {37 Console.WriteLine("something wrong here");38 }39 }40 41 array.Dispose();42 array2.Dispose();43 }

 

1                 unsafe 2                 { 3                     vec3* header = (vec3*)vec3Array.FirstElement(); 4                     vec3* last = (vec3*)vec3Array.LastElement(); 5                     vec3* tailAddress = (vec3*)vec3Array.TailAddress(); 6                     int i = 0; 7                     for (vec3* ptr = header; ptr <= last/*or: ptr < tailAddress*/; ptr++) 8                     { 9                         *ptr = new vec3(i * 3 + 0, i * 3 + 1, i * 3 + 2);10                         i++;11                     }12                     i = 0;13                     for (vec3* ptr = header; ptr <= last/*or: ptr < tailAddress*/; ptr++, i++)14                     {15                         var item = *ptr;16                         var old = new vec3(i * 3 + 0, i * 3 + 1, i * 3 + 2);17                         if (item.x != old.x || item.y != old.y || item.z != old.z)18                         { throw new Exception(); }19                     }20                 }

2015-08-25

用StructLayout和MarshalAs支持复杂的struct

在OpenGL中我需要用UnmanagedArray<mat4>,其中mat4定义如下:

1     ///  2     /// Represents a 4x4 matrix. 3     ///  4     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = 4 * 4 * 4)] 5     public struct mat4 6     { 7         ///  8         /// Gets or sets the 
column at the specified index. 9 ///
10 ///
11 /// The
column.12 ///
13 /// The column index.14 ///
The column at index
.
15 public vec4 this[int column]16 {17 get { return cols[column]; }18 set { cols[column] = value; }19 }20 21 /// 22 /// Gets or sets the element at
and
.23 ///
24 ///
25 /// The element at
and
.26 ///
27 /// The column index.28 /// The row index.29 ///
30 /// The element at
and
.31 ///
32 public float this[int column, int row]33 {34 get { return cols[column][row]; }35 set { cols[column][row] = value; }36 }37 38 /// 39 /// The columms of the matrix.40 /// 41 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]42 private vec4[] cols;43 }44 45 /// 46 /// Represents a four dimensional vector.47 /// 48 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = 4 * 4)]49 public struct vec450 {51 public float x;52 public float y;53 public float z;54 public float w;55 56 public float this[int index]57 {58 get59 {60 if (index == 0) return x;61 else if (index == 1) return y;62 else if (index == 2) return z;63 else if (index == 3) return w;64 else throw new Exception("Out of range.");65 }66 set67 {68 if (index == 0) x = value;69 else if (index == 1) y = value;70 else if (index == 2) z = value;71 else if (index == 3) w = value;72 else throw new Exception("Out of range.");73 }74 }75 }
mat4

注意:UnmanagedArray<T>支持的struct,T的大小必须是确定的。所以在mat4里我们用 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = 4 * 4 * 4)] 指定mat4的大小为4个 vec4 * 4个 float * 4个字节(每个float) = 64字节,并且在 private vec4[] cols; 上用 [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] 规定了cols的元素数必须是4。之后在 vec4 上的 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = 4 * 4)] 不写也可以,因为vec4只有4个简单的float字段,不含复杂类型。

下面是测试用例。

1             mat4 matrix = glm.scale(mat4.identity(), new vec3(2, 3, 4)); 2  3             var size = Marshal.SizeOf(typeof(mat4)); 4             size = Marshal.SizeOf(matrix); 5  6             UnmanagedArray
array = new UnmanagedArray
(1); 7 array[0] = matrix; 8 9 mat4 newMatirx = array[0]; // newMatrix should be equal to matrix10 11 array.Dispose();

如果matrix和newMatrix相等,就说明上述Attribute配置正确了。

转载地址:http://jkrjx.baihongyu.com/

你可能感兴趣的文章
我的友情链接
查看>>
hexo博客解决不蒜子统计无法显示问题
查看>>
python实现链表
查看>>
java查找string1和string2是不是含有相同的字母种类和数量(string1是否是string2的重新组合)...
查看>>
Android TabActivity使用方法
查看>>
java ShutdownHook介绍与使用
查看>>
Eclipse的 window-->preferences里面没有Android选项
查看>>
《麦田里的守望者》--[美]杰罗姆·大卫·塞林格
查看>>
[置顶] 深入探析Java线程锁机制
查看>>
ORACLE 日期函数[转载]
查看>>
遇到的那些坑
查看>>
央行下属的上海资信网络金融征信系统(NFCS)签约机构数量突破800家
查看>>
[转] Lazy evaluation
查看>>
常用查找算法总结
查看>>
grep 零宽断言
查看>>
被神话的大数据——从大数据(big data)到深度数据(deep data)思维转变
查看>>
修改校准申请遇到的问题
查看>>
【DL-CV】浅谈GoogLeNet(咕咕net)
查看>>
python大佬养成计划----win下对数据库的操作
查看>>
Mysql 中创建索引和索引的使用问题
查看>>