Laden...

Zeiger auf generics+ Struktur<->bytes 4x schneller

Erstellt von Floste vor 14 Jahren Letzter Beitrag vor 14 Jahren 7.234 Views
Floste Themenstarter:in
1.130 Beiträge seit 2007
vor 14 Jahren
Zeiger auf generics+ Struktur<->bytes 4x schneller

Beschreibung:

Normalerweise kann man keine zeiger auf Variablen generischer Typen erstellen. Der folgende Code macht aber genau das. Man muss aber aufpassen, dass man nur mit Zeigern weiterarbeitet, während das Objekt als ref-Parameter an eine belibige Methode übergeben wurde, denn das entspricht von den Opcodes exakt einem fixed-statement oder das Objekt direkt auf dem Stack liegt.

Man kann mit der Methode gut strukturen in bytearrays kopieren (und umgekehrt). Das geht sogar 4x soschnell wie mit den marschal-methoden.

using System;
using System.Reflection;
using System.Reflection.Emit;

using BF = System.Reflection.BindingFlags;
using System.Runtime.InteropServices;

namespace Krimskrams
{
    public static class UnsafeHelper
    {
        /// <see cref="UnsafeHelper<T>.GetPtr"/>
        public static unsafe void* GetPtr<T>(ref T arg) where T : struct
        {
            return UnsafeHelper<T>.GetPtr(ref arg);
        }

        
        public static unsafe void MoveMemory<T1, T2>(ref T1 dest, ref T2 src, int size)
            where T1 : struct
            where T2 : struct
        {
            MoveMemory(UnsafeHelper<T1>.GetPtr(ref dest), UnsafeHelper<T2>.GetPtr(ref src), size);
        }

        public static unsafe void MoveMemory<T>(ref T dest, void* src, int size) where T : struct
        {
            MoveMemory(UnsafeHelper<T>.GetPtr(ref dest), src, size);
        }

        public static unsafe void MoveMemory<T>(int numDestObjs,ref T dest, void* src) where T : struct
        {
            MoveMemory(UnsafeHelper<T>.GetPtr(ref dest), src, numDestObjs * UnsafeHelper<T>.Size);
        }

        public static unsafe void MoveMemory<T>(void* dest, ref T src, int size) where T : struct
        {
            MoveMemory(dest, UnsafeHelper<T>.GetPtr(ref src), size);
        }

        public static unsafe void MoveMemory<T>(void* dest,int numSrcObjs, ref T src) where T : struct
        {
            MoveMemory(dest, UnsafeHelper<T>.GetPtr(ref src), numSrcObjs * UnsafeHelper<T>.Size);
        }
            
        [DllImport("Kernel32.dll", EntryPoint="RtlMoveMemory", SetLastError=false)]
        public static unsafe extern void MoveMemory(void* dest, void* src, int size);

        /// <remarks>Schneller bei mehrmaligem Aufruf! Anders als bei Marshal.SizeOf wird char mit 2 bytes übersetzt!</remarks>
        /// <see cref="UnsafeHelper<T>.Size"/>
        public static int SizeOf<T>()
        {
            return UnsafeHelper<T>.Size;
        }

        /// <remarks>Langsamer bei mehrmaligem Aufruf. Anders als bei Marshal.SizeOf wird char mit 2 bytes übersetzt!</remarks>
        public static int SizeOf(Type type)
        {
            object[] attrs=type.GetCustomAttributes(typeof(StructLayoutAttribute), false);
            if (attrs.Length > 0)
            {
                StructLayoutAttribute attr = (StructLayoutAttribute)attrs[0];
                if (attr.Size > 0) return attr.Size;
            }

            if (type.IsPrimitive)
            {
                if (type == typeof(IntPtr))
                    return IntPtr.Size;

                if (type == typeof(double) ||
                    type == typeof(long) ||
                    type == typeof(ulong))
                    return 8;

                if (type == typeof(float) ||
                    type == typeof(int) ||
                    type == typeof(uint))
                    return 4;

                if (type == typeof(char) ||
                    type == typeof(short) ||
                    type == typeof(ushort))
                    return 2;

                if (type == typeof(bool) ||
                    type == typeof(sbyte) ||
                    type == typeof(byte))
                    return 1;
            }
            else if (type.IsPointer)
            {
                return IntPtr.Size;
            }
            else if (type.IsValueType)
            {
                int sum = 0;
                foreach (FieldInfo info in type.GetFields(BF.Instance | BF.Public | BF.NonPublic))
                {
                    sum += SizeOf(info.FieldType);
                }
                return sum;
            }
            throw new NotSupportedException(type.FullName);
        }
    }

    public static class UnsafeHelper<T>
    {
        public unsafe delegate void* getPtrFunc(ref T value);
        public unsafe readonly static getPtrFunc GetPtr = BuildFunction();
        public static readonly int Size = UnsafeHelper.SizeOf(typeof(T));
        private static DynamicMethod method;

        private unsafe static getPtrFunc BuildFunction()
        {
            method = new DynamicMethod("getPtr<" + typeof(T).FullName.Replace(".", "<>") + ">",
                typeof(void*), new Type[1] { typeof(T).MakeByRefType() }, typeof(UnsafeHelper).Module);

            ILGenerator generator = method.GetILGenerator();
            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Conv_U);
            generator.Emit(OpCodes.Ret);
            return (getPtrFunc)method.CreateDelegate(typeof(getPtrFunc));
        }
    }
}

Schlagwörter: pointer generic generics

[EDIT] Unsinnige Methode entfernt
[EDIT2]
Überarbeitet:
Man kann jetzt ganze Arrays umkopieren. (einfach MoveMemory(anzahlArrayElemente,ref meinArray[0],meinZeiger); )

  1. @Peter: Ich habe versucht einen bequemen und sehr schnellen "binarywriter" für nicht verwalteten speicher zu schreiben. (für Grafikdaten)

Projekte:Jade, HttpSaver
Zum Rechtschreiben gibts doch schon die Politiker. Aber die bauen auch nur mist!

5.941 Beiträge seit 2005
vor 14 Jahren

Salute Floste

Und wo braucht man das? 😃

Gruss Peter

--
Microsoft MVP - Visual Developer ASP / ASP.NET, Switzerland 2007 - 2011

F
10.010 Beiträge seit 2004
vor 14 Jahren

Wenn man viel Hackt und/oder mit Binärdaten von Anderen basteln will/muss.