C#+无unsafe的非托管大数组(large unmanaged array in c# without 'unsafe' keyword)
C#+无unsafe的非托管大数组(large unmanaged array in c# without 'unsafe' keyword)
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[]; 这样的数组,其内存释放很难由程序员完全控制,在申请一个大数组后,程序可能会变得很慢。
If I use something like int[] array = new int[]; , 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:
/// <summary>
/// 设置当前VBO的数据。
/// </summary>
/// <param name="target"></param>
/// <param name="data"></param>
/// <param name="usage"></param>
public static void glBufferData(uint target, UnmanagedArrayBase data, uint usage)
{
GetDelegateFor<glBufferData>()((uint)target,
data.ByteLength, // 使用非托管数组
data.Header, // 使用非托管数组
(uint)usage);
}
// ...
// glBufferData的声明
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.
/// <summary>
/// 金字塔的posotion array.
/// </summary>
static vec3[] positions = new vec3[]
{
new vec3(0.0f, 1.0f, 0.0f),
new vec3(-1.0f, -1.0f, 1.0f),
// ...
new vec3(-1.0f, -1.0f, 1.0f),
};
// Create a vertex buffer for the vertex data.
{
uint[] ids = new uint[];
GL.GenBuffers(, ids);
GL.BindBuffer(GL.GL_ARRAY_BUFFER, ids[]);
// 使用vec3作为泛型的非托管数组的参数
UnmanagedArray<vec3> positionArray = new UnmanagedArray<vec3>(positions.Length);
for (int i = ; i < positions.Length; i++)
{
// 使用this[i]这样的索引方式来读写非托管数组的元素
positionArray[i] = positions[i];
}
GL.BufferData(BufferDataTarget.ArrayBuffer, positionArray, BufferDataUsage.StaticDraw);
GL.VertexAttribPointer(positionLocation, , GL.GL_FLOAT, false, , IntPtr.Zero);
GL.EnableVertexAttribArray(positionLocation);
}
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.
/// <summary>
/// 元素类型为sbyte, byte, char, short, ushort, int, uint, long, ulong, float, double, decimal, bool或其它struct的非托管数组。
/// <para>不能使用enum类型作为T。</para>
/// </summary>
/// <typeparam name="T">sbyte, byte, char, short, ushort, int, uint, long, ulong, float, double, decimal, bool或其它struct, 不能使用enum类型作为T。</typeparam>
public class UnmanagedArray<T> : UnmanagedArrayBase where T : struct
{ /// <summary>
///元素类型为sbyte, byte, char, short, ushort, int, uint, long, ulong, float, double, decimal, bool或其它struct的非托管数组。
/// </summary>
/// <param name="count"></param>
[MethodImpl(MethodImplOptions.Synchronized)]
public UnmanagedArray(int count)
: base(count, Marshal.SizeOf(typeof(T)))
{
} /// <summary>
/// 获取或设置索引为<paramref name="index"/>的元素。
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T this[int index]
{
get
{
if (index < || index >= this.Count)
throw new IndexOutOfRangeException("index of UnmanagedArray is out of range"); var pItem = this.Header + (index * elementSize);
//var obj = Marshal.PtrToStructure(pItem, typeof(T));
//T result = (T)obj;
T result = Marshal.PtrToStructure<T>(pItem);// works in .net 4.5.1
return result;
}
set
{
if (index < || index >= this.Count)
throw new IndexOutOfRangeException("index of UnmanagedArray is out of range"); var pItem = this.Header + (index * elementSize);
//Marshal.StructureToPtr(value, pItem, true);
Marshal.StructureToPtr<T>(value, pItem, true);// works in .net 4.5.1
}
} /// <summary>
/// 按索引顺序依次获取各个元素。
/// </summary>
/// <returns></returns>
public IEnumerable<T> GetElements()
{
if (!this.disposed)
{
for (int i = ; i < this.Count; i++)
{
yield return this[i];
}
}
}
} /// <summary>
/// 非托管数组的基类。
/// </summary>
public abstract class UnmanagedArrayBase : IDisposable
{ /// <summary>
/// 数组指针。
/// </summary>
public IntPtr Header { get; private set; } /// <summary>
/// 元素数目。
/// </summary>
public int Count { get; private set; } /// <summary>
/// 单个元素的字节数。
/// </summary>
protected int elementSize; /// <summary>
/// 申请到的字节数。(元素数目 * 单个元素的字节数)。
/// </summary>
public int ByteLength
{
get { return this.Count * this.elementSize; }
} /// <summary>
/// 非托管数组。
/// </summary>
/// <param name="elementCount">元素数目。</param>
/// <param name="elementSize">单个元素的字节数。</param>
[MethodImpl(MethodImplOptions.Synchronized)]
protected UnmanagedArrayBase(int elementCount, int elementSize)
{
this.Count = elementCount;
this.elementSize = elementSize; int memSize = elementCount * elementSize;
this.Header = Marshal.AllocHGlobal(memSize); allocatedArrays.Add(this);
} private static readonly List<IDisposable> allocatedArrays = new List<IDisposable>(); /// <summary>
/// 立即释放所有<see cref="UnmanagedArray"/>。
/// </summary>
[MethodImpl(MethodImplOptions.Synchronized)]
public static void FreeAll()
{
foreach (var item in allocatedArrays)
{
item.Dispose();
}
allocatedArrays.Clear();
} ~UnmanagedArrayBase()
{
Dispose();
} #region IDisposable Members /// <summary>
/// Internal variable which checks if Dispose has already been called
/// </summary>
protected Boolean disposed; /// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected void Dispose(Boolean disposing)
{
if (disposed)
{
return;
} if (disposing)
{
//Managed cleanup code here, while managed refs still valid
}
//Unmanaged cleanup code here
IntPtr ptr = this.Header; if (ptr != IntPtr.Zero)
{
this.Count = ;
this.Header = IntPtr.Zero;
Marshal.FreeHGlobal(ptr);
} disposed = true;
} /// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
} #endregion }
UnmanagedArray
如何使用(How to use)
UnmanagedArray<T>使用方式十分简单,就像一个普通的数组一样:
Using UnamangedAray<T> is just like a normal array(int[], vec3[], etc.):
internal static void TypicalScene()
{
const int count = ; // 测试float类型
var floatArray = new UnmanagedArray<float>(count);
for (int i = ; i < count; i++)
{
floatArray[i] = i;
}
for (int i = ; i < count; i++)
{
var item = floatArray[i];
if (item != i)
{ throw new Exception(); }
} // 测试int类型
var intArray = new UnmanagedArray<int>(count);
for (int i = ; i < count; i++)
{
intArray[i] = i;
}
for (int i = ; i < count; i++)
{
var item = intArray[i];
if (item != i)
{ throw new Exception(); }
} // 测试bool类型
var boolArray = new UnmanagedArray<bool>(count);
for (int i = ; i < count; i++)
{
boolArray[i] = i % == ;
}
for (int i = ; i < count; i++)
{
var item = boolArray[i];
if (item != (i % == ))
{ throw new Exception(); }
} // 测试vec3类型
var vec3Array = new UnmanagedArray<vec3>(count);
for (int i = ; i < count; i++)
{
vec3Array[i] = new vec3(i * + , i * + , i * + );
}
for (int i = ; i < count; i++)
{
var item = vec3Array[i];
var old = new vec3(i * + , i * + , i * + );
if (item.x != old.x || item.y != old.y || item.z != old.z)
{ throw new Exception(); }
} // 测试foreach
foreach (var item in vec3Array.GetElements())
{
Console.WriteLine(item);
} // 释放此数组占用的内存,这之后就不能再使用vec3Array了。
vec3Array.Dispose(); // 立即释放所有非托管数组占用的内存,这之后就不能再使用上面申请的数组了。
UnmanagedArrayBase.FreeAll();
}
快速读写UnmanagedArray<T>
UnmanagedArrayHelper
由于很多时候需要申请和使用很大的UnmanagedArray<T>,直接使用this[index]索引方式速度会偏慢,所以我添加了几个辅助方法,专门解决快速读写UnmanagedArray<T>的问题。
public static class UnmanagedArrayHelper
{
///// <summary>
///// 错误 1 无法获取托管类型(“T”)的地址和大小,或无法声明指向它的指针
///// </summary>
///// <typeparam name="T"></typeparam>
///// <param name="array"></param>
///// <returns></returns>
//public static unsafe T* FirstElement<T>(this UnmanagedArray<T> array) where T : struct
//{
// var header = (void*)array.Header;
// return (T*)header;
//} /// <summary>
/// 获取非托管数组的第一个元素的地址。
/// </summary>
/// <param name="array"></param>
/// <returns></returns>
public static unsafe void* FirstElement(this UnmanagedArrayBase array)
{
var header = (void*)array.Header; return header;
} public static unsafe void* LastElement(this UnmanagedArrayBase array)
{
var last = (void*)(array.Header + (array.ByteLength - array.ByteLength / array.Length)); return last;
} /// <summary>
/// 获取非托管数组的最后一个元素的地址再向后一个单位的地址。
/// </summary>
/// <param name="array"></param>
/// <returns></returns>
public static unsafe void* TailAddress(this UnmanagedArrayBase array)
{
var tail = (void*)(array.Header + array.ByteLength); return tail;
}
}
如何使用
这个类型实现了3个扩展方法,可以获取UnmanagedArray<T>的第一个元素的位置、最后一个元素的位置、最后一个元素+1的位置。用这种unsafe的方法可以实现C语言一样的读写速度。
下面是一个例子。用unsafe的方式读写UnmanagedArray<T>,速度比this[index]方式快10到70倍。
public static void TypicalScene()
{
int length = ;
UnmanagedArray<int> array = new UnmanagedArray<int>(length);
UnmanagedArray<int> array2 = new UnmanagedArray<int>(length); long tick = DateTime.Now.Ticks;
for (int i = ; i < length; i++)
{
array[i] = i;
}
long totalTicks = DateTime.Now.Ticks - tick; tick = DateTime.Now.Ticks;
unsafe
{
int* header = (int*)array2.FirstElement();
int* last = (int*)array2.LastElement();
int* tailAddress = (int*)array2.TailAddress();
int value = ;
for (int* ptr = header; ptr <= last/*or: ptr < tailAddress*/; ptr++)
{
*ptr = value++;
}
}
long totalTicks2 = DateTime.Now.Ticks - tick;
Console.WriteLine("ticks: {0}, {1}", totalTicks, totalTicks2);// unsafe method works faster. for (int i = ; i < length; i++)
{
if (array[i] != i)
{
Console.WriteLine("something wrong here");
}
if (array2[i] != i)
{
Console.WriteLine("something wrong here");
}
} array.Dispose();
array2.Dispose();
}
unsafe
{
vec3* header = (vec3*)vec3Array.FirstElement();
vec3* last = (vec3*)vec3Array.LastElement();
vec3* tailAddress = (vec3*)vec3Array.TailAddress();
int i = ;
for (vec3* ptr = header; ptr <= last/*or: ptr < tailAddress*/; ptr++)
{
*ptr = new vec3(i * + , i * + , i * + );
i++;
}
i = ;
for (vec3* ptr = header; ptr <= last/*or: ptr < tailAddress*/; ptr++, i++)
{
var item = *ptr;
var old = new vec3(i * + , i * + , i * + );
if (item.x != old.x || item.y != old.y || item.z != old.z)
{ throw new Exception(); }
}
}
2015-08-25
用StructLayout和MarshalAs支持复杂的struct
在OpenGL中我需要用UnmanagedArray<mat4>,其中mat4定义如下:
/// <summary>
/// Represents a 4x4 matrix.
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = * * )]
public struct mat4
{
/// <summary>
/// Gets or sets the <see cref="vec4"/> column at the specified index.
/// </summary>
/// <value>
/// The <see cref="vec4"/> column.
/// </value>
/// <param name="column">The column index.</param>
/// <returns>The column at index <paramref name="column"/>.</returns>
public vec4 this[int column]
{
get { return cols[column]; }
set { cols[column] = value; }
} /// <summary>
/// Gets or sets the element at <paramref name="column"/> and <paramref name="row"/>.
/// </summary>
/// <value>
/// The element at <paramref name="column"/> and <paramref name="row"/>.
/// </value>
/// <param name="column">The column index.</param>
/// <param name="row">The row index.</param>
/// <returns>
/// The element at <paramref name="column"/> and <paramref name="row"/>.
/// </returns>
public float this[int column, int row]
{
get { return cols[column][row]; }
set { cols[column][row] = value; }
} /// <summary>
/// The columms of the matrix.
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = )]
private vec4[] cols;
} /// <summary>
/// Represents a four dimensional vector.
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = * )]
public struct vec4
{
public float x;
public float y;
public float z;
public float w; public float this[int index]
{
get
{
if (index == ) return x;
else if (index == ) return y;
else if (index == ) return z;
else if (index == ) return w;
else throw new Exception("Out of range.");
}
set
{
if (index == ) x = value;
else if (index == ) y = value;
else if (index == ) z = value;
else if (index == ) w = value;
else throw new Exception("Out of range.");
}
}
}
mat4
注意:UnmanagedArray<T>支持的struct,T的大小必须是确定的。所以在mat4里我们用 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = * * )] 指定mat4的大小为4个 vec4 * 4个 float * 4个字节(每个float) = 64字节,并且在 private vec4[] cols; 上用 [MarshalAs(UnmanagedType.ByValArray, SizeConst = )] 规定了cols的元素数必须是4。之后在 vec4 上的 [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Size = * )] 不写也可以,因为vec4只有4个简单的float字段,不含复杂类型。
下面是测试用例。
mat4 matrix = glm.scale(mat4.identity(), new vec3(, , )); var size = Marshal.SizeOf(typeof(mat4));
size = Marshal.SizeOf(matrix); UnmanagedArray<mat4> array = new UnmanagedArray<mat4>();
array[] = matrix; mat4 newMatirx = array[]; // newMatrix should be equal to matrix array.Dispose();
如果matrix和newMatrix相等,就说明上述Attribute配置正确了。
C#+无unsafe的非托管大数组(large unmanaged array in c# without 'unsafe' keyword)的更多相关文章
- C#的托管与非托管大难点
托管代码与非托管代码 众所周知,我们正常编程所用的高级语言,是无法被计算机识别的.需要先将高级语言翻译为机器语言,才能被机器理解和运行.在标准C/C++中,编译过程是这样的:源代码首先经过预处理器,对 ...
- C# 快速释放内存的大数组
本文告诉大家如何使用 Marshal 做出可以快速释放内存的大数组. 最近在做 3D ,需要不断申请一段大内存数组,然后就释放他,但是 C# 对于大内存不是立刻释放,所以就存在一定的性能问题. 在博客 ...
- 2018-8-10-C#-快速释放内存的大数组
title author date CreateTime categories C# 快速释放内存的大数组 lindexi 2018-08-10 19:16:52 +0800 2018-2-13 17 ...
- CSharpGL(36)通用的非托管数组排序方法
CSharpGL(36)通用的非托管数组排序方法 如果OpenGL要渲染半透明物体,一个方法是根据顶点到窗口的距离排序,按照从远到近的顺序依次渲染.所以本篇介绍对 UnmanagedArray< ...
- C# 托管和非托管混合编程
在非托管模块中实现你比较重要的算法,然后通过 CLR 的平台互操作,来使托管代码调用它,这样程序仍然能够正常工作,但对非托管的本地代码进行反编译,就很困难. 最直接的实现托管与非托管编程的方法就是 ...
- .NET对象的创建、垃圾回收、非托管资源的手动处理
本篇用来梳理对象的创建.垃圾的回收,以及非托管资源的手动处理. →首先运行应用程序,创建一个Windows进程. →CLR创建一块连续的虚拟地址空间,这个地址空间就是托管堆.而且,这个地址空间最初并没 ...
- 浅谈 .NET 中的对象引用、非托管指针和托管指针 理解C#中的闭包
浅谈 .NET 中的对象引用.非托管指针和托管指针 目录 前言 一.对象引用 二.值传递和引用传递 三.初识托管指针和非托管指针 四.非托管指针 1.非托管指针不能指向对象引用 2.类成员指针 五 ...
- C#调用非托管dll
以C#开发周立功CAN举例,在官网下载了周立功的demo 一.C++头文件样子 //接口卡类型定义#define VCI_PCI5121 1 //一些结构体定义 typedef struct tagR ...
- 浅谈 .NET 中的对象引用、非托管指针和托管指针
目录 前言 一.对象引用 二.值传递和引用传递 三.初识托管指针和非托管指针 四.非托管指针 1.非托管指针不能指向对象引用 2.类成员指针 五.托管指针 前言 本文主要是以 C# 为例介绍 .NET ...
随机推荐
- MySQL开发规范
字段设计 (1)建议使用UNSIGNED存储非负数值. (2)建议使用INT UNSIGNED存储IPV4. (4)INT类型固定占用4字节存储,例如INT(4)仅代表显示字符宽度为4位,不代表存储长 ...
- VS2012 还原默认设置
恢复默认设置的2种方法 如果VS出现问题或设置变乱,可以通过恢复默认设置使之回到安装成功时的状态,从而解决出现的问题.VS恢复默认设置的方法有2种,分别是:通过"导入和导出设置"实 ...
- 历年NOIP水题泛做
快noip了就乱做一下历年的noip题目咯.. noip2014 飞扬的小鸟 其实这道题并不是很难,但是就有点难搞 听说男神错了一个小时.. 就是$f_{i,j}$表示在第$i$个位置高度为$j$的时 ...
- BZOJ2908: 又是nand
Description 首先知道A nand B=not(A and B) (运算操作限制了数位位数为K)比如2 nand 3,K=3,则2 nand 3=not (2 and 3)=not 2=5. ...
- 谷歌浏览器如何查看或获取Cookie字符串
注:此博客仅供非web开发人员查看,以下内容都基于谷歌浏览器. 在网页空白处点击鼠标右键,在弹出菜单中选择[审查元素],可以看到网页下方出现审查元素相关界面. 在审查元素相关界面,点击[Network ...
- WPF-系统托盘
WPFSystemTray.cs public class WPFSystemTray { /// <summary> /// 设置系统托盘 /// </summary> // ...
- C++ Primer Plus 第六版笔记
C++ Primer Plus 第六版笔记 关于对象声明的思考 转自:http://www.cnblogs.com/weiqubo/archive/2009/11/02/1930042.html C+ ...
- struts2 框架处理流程
struts2 框架处理流程 流程图如下: 注意:StrutsPrepareAndExecuteFilter替代了2.1.3以前的FilterDispatcher过滤器,使得在执行Action之前可以 ...
- IE6 的兼容相关问题
因为在实习公司要求兼容IE6+,所以将IE6相关的样式兼容问题列出,及解决方案. 1.让页面变丑的透明背景图片问题: HTML都为以下代码: <div class="img-png&q ...
- 理解MVC,MVP和MVVM设计模式
有3个非常受欢迎的MV-*系列设计模式:MVC,MVP,MVVM.他们被广泛应用于不多种结束.这篇文章我回阐述我自己对这3个设计模式的看法. MVC模式: MVC即Model-VIew-Control ...