zoukankan      html  css  js  c++  java
  • CSharpGL(36)通用的非托管数组排序方法

    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(@"ResourcesSortingHelper.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的方法,我就暂时不研究了。

  • 相关阅读:
    奶牛编号(Cowids) [NOIP模拟]
    天天爱跑步 [NOIP2016]
    部落冲突
    狡猾的商人 [HNOI2005] [一题双解]
    最长公共子序列-LCS问题 (LCS与LIS在特殊条件下的转换) [洛谷1439]
    LeetCode 2. Add Two Numbers
    LeetCode 1. Two Sum
    LeetCode 91. Decode Ways
    LeetCode 516. Longest Palindromic Subsequence
    什么是马拉车算法(Manacher's Algorithm)?
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/CSharpGL-36-generic-unmanaged-array-sorting.html
Copyright © 2011-2022 走看看