CSharpGL(36)通用的非托管数组排序方法

如果OpenGL要渲染半透明物体,一个方法是根据顶点到窗口的距离排序,按照从远到近的顺序依次渲染。所以本篇介绍对 UnmanagedArray<T> 进行排序的几种方法。

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

UnmanagedArray<T>

首先重新介绍一下非托管数组这个东西。一个 UnmanagedArray<float> 与一个 float[] 是一样的用处,只不过 UnmanagedArray<float> 是用 Marshal.AllocHGlobal(memSize); 在非托管内存上申请的空间。

 1 namespace CSharpGL
 2 {
 3     /// <summary>
 4     /// unmanaged huge array.
 5     /// <para>Check http://www.cnblogs.com/bitzhuwei/p/huge-unmanged-array-in-csharp.html </para>
 6     /// </summary>
 7     /// <typeparam name="T">sbyte, byte, char, short, ushort, int, uint, long, ulong, float, double, decimal, bool or other struct types. enum not supported.</typeparam>
 8     public sealed unsafe class UnmanagedArray<T> : UnmanagedArrayBase where T : struct
 9     {
10         public UnmanagedArray(int count)
11             : base(count, Marshal.SizeOf(typeof(T)))
12         {
13         }
14     }
15
16     /// <summary>
17     /// Base type of unmanaged array.
18     /// <para>Similar to array in <code>int array[Length];</code></para>
19     /// </summary>
20     public abstract class UnmanagedArrayBase : IDisposable
21     {
22         /// <summary>
23         /// 此非托管数组中的数据在内存中的起始地址
24         /// Start position of array; Head of array; first element‘s position of array.
25         /// <para>Similar to <code>array</code> in <code>int array[Length];</code></para>
26         /// </summary>
27         public IntPtr Header { get; private set; }
28
29         /// <summary>
30         /// How many elements?
31         /// <para>Similar to <code>Length</code> in <code>int array[Length];</code></para>
32         /// </summary>
33         public int Length { get; private set; }
34
35         /// <summary>
36         /// 单个元素的字节数。
37         /// <para>How manay bytes for one element of array?</para>
38         /// </summary>
39         protected int elementSize;
40
41         /// <summary>
42         /// 申请到的字节数。(元素的总数 * 单个元素的字节数)。
43         /// <para>How many bytes for total array?</para>
44         /// <para>Length * elementSize</para>
45         /// </summary>
46         public int ByteLength
47         {
48             get { return (this.Length * this.elementSize); }
49         }
50
51         protected UnmanagedArrayBase(int elementCount, int elementSize)
52         {
53             this.Length = elementCount;
54             this.elementSize = elementSize;
55             int memSize = this.Length * this.elementSize;
56             this.Header = Marshal.AllocHGlobal(memSize);
57         }
58     }
59 }

UnmanagedArray<T>

UnmanagedArray<float>

下面我们以 UnmanagedArray<float> 为例,实现对非托管数组的排序。(下面是快速排序算法)

 1         public static void Sort(this UnmanagedArray<float> array, bool descending)
 2         {
 3             QuickSort(array, 0, array.Length - 1, descending);
 4         }
 5
 6         public static void Sort(UnmanagedArray<float> array, int start, int length, bool descending)
 7         {
 8             QuickSort(array, start, start + length - 1, descending);
 9         }
10
11         private static void QuickSort(UnmanagedArray<float> array, int start, int end, bool descending)
12         {
13             if (start >= end) { return; }
14
15             Stack<int> stack = new Stack<int>();
16             stack.Push(end);
17             stack.Push(start);
18             QuickSort(array, descending, stack);
19         }
20
21         private static void QuickSort(UnmanagedArray<float> array, bool descending, Stack<int> stack)
22         {
23             while (stack.Count > 0)
24             {
25                 int start = stack.Pop();
26                 int end = stack.Pop();
27                 int index = QuickSortPartion(array, start, end, descending);
28                 if (start < index - 1)
29                 {
30                     stack.Push(index - 1); stack.Push(start);
31                 }
32                 if (index + 1 < end)
33                 {
34                     stack.Push(end); stack.Push(index + 1);
35                 }
36             }
37         }
38
39         private static unsafe int QuickSortPartion(UnmanagedArray<float> array, int start, int end, bool descending)
40         {
41             float* pointer = (float*)array.Header.ToPointer();
42             float pivot, startValue, endValue;
43             pivot = pointer[start];
44             while (start < end)
45             {
46                 startValue = pointer[start];
47                 while ((start < end)
48                     && ((descending && (startValue.CompareTo(pivot) > 0))
49                         || (!descending) && (startValue.CompareTo(pivot) < 0)))
50                 {
51                     start++;
52                     startValue = pointer[start];
53                 }
54
55                 endValue = pointer[end];
56                 while ((start < end)
57                     && ((descending && (endValue.CompareTo(pivot) < 0))
58                         || (!descending) && (endValue.CompareTo(pivot) > 0)))
59                 {
60                     end--;
61                     endValue = pointer[end];
62                 }
63
64                 if (start < end)
65                 {
66                     pointer[end] = startValue;
67                     pointer[start] = endValue;
68                 }
69             }
70
71             return start;
72         }

public static void Sort(this UnmanagedArray array, bool descending)

原本我以为把这个方法改写一下,用 T代替具体的 float ,就可以实现对任意 T where T : struct,IComparable<T> 的排序,但是VS报了这样的编译错误:无法获取托管类型(“T”)的地址和大小,或无法声明指向它的指针

看来C#不允许将泛型T作为unsafe里的数组的类型。那么只能用下面的几种方式变通一下。

Marshal

Marshal 有这样2个方法:

1 // 从非托管数组中读取一个元素
2 public static object PtrToStructure(IntPtr ptr, Type structureType);
3 // 向非托管数组写入一个元素
4 public static void StructureToPtr(object structure, IntPtr ptr, bool fDeleteOld);

用这2个方法就可以实现对非托管数组的读写操作。于是泛型版本( UnmagedArray<T> )的排序算法就实现了。

 1         public static void Sort<T>(this UnmanagedArray<T> array, bool descending) where T : struct, IComparable<T>
 2         {
 3             QuickSort(array, 0, array.Length - 1, descending);
 4         }
 5
 6         public static void Sort<T>(this UnmanagedArray<T> array, int start, int length, bool descending) where T : struct, IComparable<T>
 7         {
 8             QuickSort(array, start, start + length - 1, descending);
 9         }
10
11         private static void QuickSort<T>(UnmanagedArray<T> array, int start, int end, bool descending) where T : struct, IComparable<T>
12         {
13             if (start >= end) { return; }
14
15             var stack = new Stack<int>();
16             stack.Push(end);
17             stack.Push(start);
18             QuickSort(array, descending, stack);
19         }
20
21         private static void QuickSort<T>(UnmanagedArray<T> array, bool descending, Stack<int> stack) where T : struct, IComparable<T>
22         {
23             IntPtr pointer = array.Header;
24             Type type = typeof(T);
25             int elementSize = Marshal.SizeOf(type);
26
27             while (stack.Count > 0)
28             {
29                 int start = stack.Pop();
30                 int end = stack.Pop();
31                 int index = QuickSortPartion(array, start, end, descending, type, elementSize);
32                 if (start < index - 1)
33                 {
34                     stack.Push(index - 1); stack.Push(start);
35                 }
36                 if (index + 1 < end)
37                 {
38                     stack.Push(end); stack.Push(index + 1);
39                 }
40             }
41         }
42
43         private static int QuickSortPartion<T>(UnmanagedArray<T> array, int start, int end, bool descending, Type type, int elementSize) where T : struct, IComparable<T>
44         {
45             IntPtr pointer = array.Header;
46             IntPtr pivotIndex, startIndex, endIndex;
47             T pivot, startValue, endValue;
48             pivotIndex = new IntPtr((int)pointer + start * elementSize);
49             pivot = (T)Marshal.PtrToStructure(pivotIndex, type);
50             while (start < end)
51             {
52                 startIndex = new IntPtr((int)pointer + start * elementSize);
53                 startValue = (T)Marshal.PtrToStructure(startIndex, type);
54                 while ((start < end)
55                     && ((descending && (startValue.CompareTo(pivot) > 0))
56                         || ((!descending) && (startValue.CompareTo(pivot) < 0))))
57                 {
58                     start++;
59                     startIndex = new IntPtr((int)pointer + start * elementSize);
60                     startValue = (T)Marshal.PtrToStructure(startIndex, type);
61                 }
62
63                 endIndex = new IntPtr((int)pointer + end * elementSize);
64                 endValue = (T)Marshal.PtrToStructure(endIndex, type);
65                 while ((start < end)
66                  && ((descending && (endValue.CompareTo(pivot) < 0))
67                         || ((!descending) && (endValue.CompareTo(pivot) > 0))))
68                 {
69                     end--;
70                     endIndex = new IntPtr((int)pointer + end * elementSize);
71                     endValue = (T)Marshal.PtrToStructure(endIndex, type);
72                 }
73
74                 if (start < end)
75                 {
76                     Marshal.StructureToPtr(endValue, startIndex, true);
77                     Marshal.StructureToPtr(startValue, endIndex, true);
78                 }
79             }
80
81             return start;
82         }

public static void Sort(this UnmanagedArray array, bool descending) where T : struct, IComparable

虽然可用,但是用Marshal读写非托管数组效率比较低(使用了装箱拆箱操作)。于是我有了下面这个思路。

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

CodeProvider

我们知道C#现有的类库是支持动态生成和调用C#代码的。因此,我可以在调用 Sort<T>(this UnmanagedArray<T> array, bool descending) 时,临时为具体的T生成一个排序方法,然后去调用这个方法。这就不需要使用 Marshal 了。

具体步骤如下。

准备模板

为了便于编码、维护,我们先准备一个排序代码的模板。模板里的TemplateStructType纯属一个占位符,在使用的时候我们先用具体的类型把它替换掉,就成了我们需要的源代码。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Runtime.InteropServices;
 4
 5 namespace CSharpGL
 6 {
 7     /// <summary>
 8     /// Helper class for sorting unmanaged array.
 9     /// </summary>
10     public static partial class SortingHelper
11     {
12         ///// <summary>
13         ///// Sort unmanaged array specified with <paramref name="array"/> at specified area.
14         ///// </summary>
15         ///// <param name="array"></param>
16         ///// <param name="descending">true for descending sort; otherwise false.</param>
17         //public static void Sort(this UnmanagedArray<TemplateStructType> array, bool descending)
18         //{
19         //    QuickSort(array, 0, array.Length - 1, descending);
20         //}
21
22         /// <summary>
23         /// Sort unmanaged array specified with <paramref name="array"/> at specified area.
24         /// </summary>
25         /// <param name="array"></param>
26         /// <param name="start">index of first value to be sorted.</param>
27         /// <param name="length">length of <paramref name="array"/> to bo sorted.</param>
28         /// <param name="descending">true for descending sort; otherwise false.</param>
29         public static void Sort(UnmanagedArray<TemplateStructType> array, int start, int length, bool descending)
30         {
31             QuickSort(array, start, start + length - 1, descending);
32         }
33
34         private static void QuickSort(UnmanagedArray<TemplateStructType> array, int start, int end, bool descending)
35         {
36             if (start >= end) { return; }
37
38             Stack<int> stack = new Stack<int>();
39             stack.Push(end);
40             stack.Push(start);
41             QuickSort(array, descending, stack);
42         }
43
44         private static void QuickSort(UnmanagedArray<TemplateStructType> array, bool descending, Stack<int> stack)
45         {
46             while (stack.Count > 0)
47             {
48                 int start = stack.Pop();
49                 int end = stack.Pop();
50                 int index = QuickSortPartion(array, start, end, descending);
51                 if (start < index - 1)
52                 {
53                     stack.Push(index - 1); stack.Push(start);
54                 }
55                 if (index + 1 < end)
56                 {
57                     stack.Push(end); stack.Push(index + 1);
58                 }
59             }
60         }
61
62         private static unsafe int QuickSortPartion(UnmanagedArray<TemplateStructType> array, int start, int end, bool descending)
63         {
64             TemplateStructType* pointer = (TemplateStructType*)array.Header.ToPointer();
65             TemplateStructType pivot, startValue, endValue;
66             pivot = pointer[start];
67             while (start < end)
68             {
69                 startValue = pointer[start];
70                 while ((start < end)
71                     && ((descending && (startValue.CompareTo(pivot) > 0))
72                         || (!descending) && (startValue.CompareTo(pivot) < 0)))
73                 {
74                     start++;
75                     startValue = pointer[start];
76                 }
77
78                 endValue = pointer[end];
79                 while ((start < end)
80                     && ((descending && (endValue.CompareTo(pivot) < 0))
81                         || (!descending) && (endValue.CompareTo(pivot) > 0)))
82                 {
83                     end--;
84                     endValue = pointer[end];
85                 }
86
87                 if (start < end)
88                 {
89                     pointer[end] = startValue;
90                     pointer[start] = endValue;
91                 }
92             }
93
94             return start;
95         }
96     }
97 }

Teamplate method

嵌入的资源

然后把这个模板文件设置为嵌入的资源,以便于调用。

动态生成和调用代码

利用CSharpCodeProvider来把源代码编译为Assembly并调用。

 1         public static void Sort<T>(this UnmanagedArray<T> array, bool descending) where T : struct, IComparable<T>
 2         {
 3             Type type = typeof(T);
 4             string order = ManifestResourceLoader.LoadTextFile(@"Resources\SortingHelper.Order`1.cs");
 5             order = order.Replace("TemplateStructType", type.FullName);
 6             var codeProvider = new CSharpCodeProvider();
 7             var option = new CompilerParameters();
 8             option.GenerateInMemory = true;
 9             option.CompilerOptions = "/unsafe";
10             option.ReferencedAssemblies.Add("System.dll");
11             option.ReferencedAssemblies.Add("CSharpGL.dll");
12             CompilerResults result = codeProvider.CompileAssemblyFromSource(option,
13                 order);
14             Assembly asm = result.CompiledAssembly;
15             Type sortingHelper = asm.GetType("CSharpGL.SortingHelper");
16             Type unmanagedArrayGeneric = typeof(UnmanagedArray<>);
17             Type unmanagedArray = unmanagedArrayGeneric.MakeGenericType(type);
18             MethodInfo method = sortingHelper.GetMethod("Sort", new Type[] { unmanagedArray, typeof(int), typeof(int), typeof(bool) });
19             method.Invoke(null, new object[] { array, 0, array.Length, descending });
20         }

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

总结

还有一种利用emit的方法,我就暂时不研究了。

时间: 2024-10-17 06:41:35

CSharpGL(36)通用的非托管数组排序方法的相关文章

在C#调用C++的DLL方法(一)生成非托管dll

C#与C/C++相比,前者的优势在于UI,后者的优势在于算法,C++下的指针虽然恶心,若使用得当还是相当方便的,最重要的问题是,市面上很多流行的开发工具库,几乎没有不支持C++的,但全面支持C#只能说是难得.在CPU发展到今天,若说C#的执行效率跟C++相比有很大的差距,并不是那么靠谱,若非万不得已我还是宁愿用C#来写代码,调试什么的也很方便. 不得已的情况下,要在C#下使用C++的函数或类,最好的方式就是使用动态链接库(dll),至于COM什么的我是至今没弄明白其原理,也许主要是因为使用起来太

.Net 程序在自定义位置查找托管/非托管 dll 的几种方法

一.自定义托管 dll 程序集的查找位置 目前(.Net4.7)能用的有2种: 1 #define DEFAULT_IMPLEMENT 2 //#define DEFAULT_IMPLEMENT2 3 //#define HACK_UPDATECONTEXTPROPERTY 4 5 namespace X.Utility 6 { 7 using System; 8 using System.Collections.Generic; 9 using System.IO; 10 using Syst

将WinForm程序(含多个非托管Dll)合并成一个exe的方法

开发程序的时候经常会引用一些第三方的DLL,然后编译生成的exe文件就不能脱离这些DLL独立运行了. ILMerge能把托管dll跟exe合并起来生成一个新的exe,但是当我们在项目中使用了非托管的dll,也就是使用了第三方dll时,合并虽然成功但是却无法运行,提示“不是有效的win32应用程序“ 这时候我们需要用到一款名为Fody.Costura的工具.Fody.Costura是一个Fody框架下的插件,可通过Nuget安装到VS工程中.安装之后,就可以将项目所依赖的DLL(甚至PDB)文件全

C#调用非托管C++DLL的两种方法

C#编写的代码属于跨平台的托管代码,C++语言可以编写托管(managed)和非托管(native)代码.在C#与C++的混合编程中,经常会使用C#来调用native C++的DLL,下面有两种常用的调用方法供大家参考. 使用P/Invoke直接调用native C++ Dll里面的函数.(注:此方法只能调用函数,不能调用class). C#通过C++ CLR(托管的C++)来调用nativeC++ DLL, C++ CLR作为链接C#与native之间的纽带. 方法一的详细过程如下: (1)打

托管和非托管转换新方法:Marshaling Library(zz) 【转】

托管和非托管转换新方法:Marshaling Library(zz) 托管和非托管转换新方法:Marshaling Library(zz) http://hi.baidu.com/superql/blog/item/38e9c8073202fcc37a8947ac.html 1.VC++2008中新增加的库:Marshaling Library 我们一起讨论一下VC++2008中引入的新库——Marshaling Library.在这个类库之前我们使用的传统方法是固定指针(pin_ptr).要使

C#调用非托管动态库中的函数方法

C#如何调用一个非托管动态库中的函数呢,比如用VC6写的动态库,总之C#调用动态库的过程是比Java调用DLL动态库方便快捷多了,下面举例说明这个过程. 1.创建一个非托管动态库 代码如下: 代码如下: //这一句是声明动态库输出一个可供外不调用的函数原型. extern   "C"  __declspec(dllexport)  int  add( int ,  int ); int  add( int  a, int  b) { //实现这个函数returna+b; } 除声明外,

在C#调用C++的DLL简析(一)——生成非托管dll

经过一晚上的折腾,还是下点决心将些许的心得写下来,以免以后重复劳动. C#与C/C++相 比,前者的优势在于UI,后者的优势在于算法,C++下的指针虽然恶心,若使用得当还是相当方便的,最重要的问题是,市面上很多流行的开发工具库,几乎没 有不支持C++的,但全面支持C#只能说是难得,在CPU发展到今天,若说C#的执行效率跟C++相比有很大的差距并不是那么靠谱,若非万不得已我还是宁 愿用C#来写代码,调试什么的也很方便. 不得已的情况下,要在C#下使用C++的函数或类,最好的方式就是使用动态 链接库

.NET对象的创建、垃圾回收、非托管资源的手动处理

本篇用来梳理对象的创建.垃圾的回收,以及非托管资源的手动处理. →首先运行应用程序,创建一个Windows进程. →CLR创建一块连续的虚拟地址空间,这个地址空间就是托管堆.而且,这个地址空间最初并没有对应的物理存储空间. 虚拟地址空间分成2段.一个区段是普通堆,也叫GC堆,大小小于85000字节的引用类型对象的实例被分配在这里:另一个是大对象堆,大小大于等于85000字节的引用类型对象的实例被分配在这里. 对于客户端应用程序,每个区段的大小大致是16MB:对于服务端应用程序,每个区段的大小大致

托管程序与非托管程序的区别

介绍托管程序与非托管程序的区别 本文主要介绍托管程序与非托管程序的概念,以及两者之间的区别.希望对你有帮助,一起来看. AD:[线下活动]三大新锐HTML 5企业汇聚51CTO—大话移动前端技术 托管代码是一microsoft的中间语言,他主要的作用是在.NET FRAMEWORK的CLR执行代码前去编译源代码,也就是说托管代码充当着翻译的作用.下面介绍托管代码和非托管代码. 什么是托管代码? 托管代码就是Visual Basic .NET和C#编译器编译出来的代码.编译器把代码编译成中间语言(