Was ist der schnellste Weg, um einen Schwimmer [] zu einem byte [] zu konvertieren?

StackOverflow https://stackoverflow.com/questions/619041

  •  03-07-2019
  •  | 
  •  

Frage

Ich möchte eine byte[] von einem float[] erhalten so schnell wie möglich, ohne den Umweg über das gesamte Array Looping (über einen cast, wahrscheinlich). Unsicherer Code ist in Ordnung. Dank!

Ich bin für ein Byte-Array 4 Mal länger als der Schwimmer Array (die Dimension des Byte-Array wird 4 mal sein, dass der Schwimmer Array, da jeder Schwimmer aus 4 Bytes zusammengesetzt ist). Ich werde dies zu einem Binary passieren.

Bearbeiten : Um diese Kritiker „vorzeitige Optimierung“ zu schreien: Ich habe dies mit ANTS Profiler gebenchmarkt bevor ich optimiert. Es gab eine signifikante Geschwindigkeitssteigerung, da die Datei einen Write-Through-Cache und der Schwimmer Array ist genau bemessen auf der Platte die Sektorgröße anzupassen. Der binäre Schreiber wickelt eine Datei-Handle mit pinvoke'd win32 API erstellt. Die Optimierung erfolgt, da dies die Anzahl der Funktionsaufrufe verringert.

Und in Bezug auf Gedächtnis, diese Anwendung erstellt massiven Caches, die vielen Speicher belegt werden. Ich kann die Byte-Puffer einmal zuweisen und wieder verwenden es oft -. Die doppelte Speichernutzung in diesem speziellen Fall beläuft sich auf einen Rundungsfehler in der gesamten Speicherverbrauch der App

Also ich denke, die Lektion ist hier nicht verfrüht, Annahmen zu machen;)

War es hilfreich?

Lösung

Wenn Sie wollen keine Umwandlung geschehen, würde ich vorschlagen Buffer.BlockCopy ().

public static void BlockCopy(
    Array src,
    int srcOffset,
    Array dst,
    int dstOffset,
    int count
)

Zum Beispiel:

float[] floatArray = new float[1000];
byte[] byteArray = new byte[floatArray.Length * 4];

Buffer.BlockCopy(floatArray, 0, byteArray, 0, byteArray.Length);

Andere Tipps

Es ist eine schmutzige schnell (nicht unsicherer Code) Art und Weise, dies zu tun:

[StructLayout(LayoutKind.Explicit)]
struct BytetoDoubleConverter
{
    [FieldOffset(0)]
    public Byte[] Bytes;

    [FieldOffset(0)]
    public Double[] Doubles;
}
//...
static Double Sum(byte[] data)
{
    BytetoDoubleConverter convert = new BytetoDoubleConverter { Bytes = data };
    Double result = 0;
    for (int i = 0; i < convert.Doubles.Length / sizeof(Double); i++)
    {
        result += convert.Doubles[i];
    }
    return result;
}

Dies funktioniert, aber ich bin von der Unterstützung auf Mono nicht sicher oder neuere Versionen des CLR . Die einzige Merkwürdige ist, dass der array.Length ist die Bytes Länge. Dies kann erklärt werden, weil es an der Array-Länge mit dem Array gespeichert aussieht, und da dieses Array ein Byte-Array war, wird die Länge noch in Byte lang sein. Der Indexer denkt über die Doppel acht Bytes ist groß, so dass keine Berechnung ist es notwendig.

Ich habe es sah einige mehr, und es ist tatsächlich auf MSDN beschrieben, Gewusst wie: erstellen eines C / C ++ Union durch Attribute (C # und Visual Basic) verwenden , so stehen die Chancen, dies wird in zukünftigen Versionen unterstützt werden. Ich bin nicht sicher Mono though.

Vorzeitige Optimierung ist die Wurzel aller Übel! @ Vlad Vorschlag über jeden Schwimmer iterieren ist eine viel vernünftige Antwort als [] auf ein Byte wechseln. Nehmen Sie die folgende Tabelle von Laufzeiten für eine wachsende Zahl von Elementen (durchschnittlich 50 Läufen):

Elements      BinaryWriter(float)      BinaryWriter(byte[])
-----------------------------------------------------------
10               8.72ms                    8.76ms
100              8.94ms                    8.82ms
1000            10.32ms                    9.06ms
10000           32.56ms                   10.34ms
100000         213.28ms                  739.90ms
1000000       1955.92ms                10668.56ms

Es gibt kaum einen Unterschied zwischen den beiden für eine kleine Anzahl von Elementen. Sobald Sie in der großen Anzahl von Elementen bekommen reichen, verbrachte die Zeit von dem Schwimmer Kopieren [] auf die byte [] überwiegt bei weitem die Vorteile.

So geht mit dem, was ist einfach:

float[] data = new float[...];
foreach(float value in data)
{
    writer.Write(value);
}

Es gibt eine Möglichkeit, das Speicherkopieren und Iteration vermeidet.

Sie können eine wirklich hässliche Hack temporäre Änderung auf einen anderen Typ Array verwenden mit (unsicheren) Speichermanipulation.

Ich testete diesen Hack in 32 & 64-Bit-Betriebssystem, so sollte es tragbar sein.

Die Quelle + Probe Nutzung ist unter https://gist.github.com/1050703 , aber für Ihre Bequemlichkeit ich werde es füge auch hier:

public static unsafe class FastArraySerializer
{
    [StructLayout(LayoutKind.Explicit)]
    private struct Union
    {
        [FieldOffset(0)] public byte[] bytes;
        [FieldOffset(0)] public float[] floats;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct ArrayHeader
    {
        public UIntPtr type;
        public UIntPtr length;
    }

    private static readonly UIntPtr BYTE_ARRAY_TYPE;
    private static readonly UIntPtr FLOAT_ARRAY_TYPE;

    static FastArraySerializer()
    {
        fixed (void* pBytes = new byte[1])
        fixed (void* pFloats = new float[1])
        {
            BYTE_ARRAY_TYPE = getHeader(pBytes)->type;
            FLOAT_ARRAY_TYPE = getHeader(pFloats)->type;
        }
    }

    public static void AsByteArray(this float[] floats, Action<byte[]> action)
    {
        if (floats.handleNullOrEmptyArray(action)) 
            return;

        var union = new Union {floats = floats};
        union.floats.toByteArray();
        try
        {
            action(union.bytes);
        }
        finally
        {
            union.bytes.toFloatArray();
        }
    }

    public static void AsFloatArray(this byte[] bytes, Action<float[]> action)
    {
        if (bytes.handleNullOrEmptyArray(action)) 
            return;

        var union = new Union {bytes = bytes};
        union.bytes.toFloatArray();
        try
        {
            action(union.floats);
        }
        finally
        {
            union.floats.toByteArray();
        }
    }

    public static bool handleNullOrEmptyArray<TSrc,TDst>(this TSrc[] array, Action<TDst[]> action)
    {
        if (array == null)
        {
            action(null);
            return true;
        }

        if (array.Length == 0)
        {
            action(new TDst[0]);
            return true;
        }

        return false;
    }

    private static ArrayHeader* getHeader(void* pBytes)
    {
        return (ArrayHeader*)pBytes - 1;
    }

    private static void toFloatArray(this byte[] bytes)
    {
        fixed (void* pArray = bytes)
        {
            var pHeader = getHeader(pArray);

            pHeader->type = FLOAT_ARRAY_TYPE;
            pHeader->length = (UIntPtr)(bytes.Length / sizeof(float));
        }
    }

    private static void toByteArray(this float[] floats)
    {
        fixed(void* pArray = floats)
        {
            var pHeader = getHeader(pArray);

            pHeader->type = BYTE_ARRAY_TYPE;
            pHeader->length = (UIntPtr)(floats.Length * sizeof(float));
        }
    }
}

Und die Nutzung ist:

var floats = new float[] {0, 1, 0, 1};
floats.AsByteArray(bytes =>
{
    foreach (var b in bytes)
    {
        Console.WriteLine(b);
    }
});

Sie sind besser gestellter die Binary lassen tun dies für Sie . Es geht um Iteration über den gesamten Satz von Daten sein, unabhängig davon, welche Methode Sie verwenden, so dass es keinen Punkt mit Bytes zu spielen.

Auch wenn Sie einen byte* Zeiger mit unsafe und fixed erhalten können, können Sie die byte* nicht konvertieren können, um für den Schriftsteller byte[], um es als Parameter zu akzeptieren, ohne Datenkopie durchführt. Welche wollen Sie nicht tun, wie es Ihren Speicherbedarf verdoppeln und eine zusätzliche Iteration über die unvermeidliche Iteration hinzufügen, um die Ausgabe der Daten auf der Festplatte durchgeführt werden muss.

Stattdessen sind Sie noch besser dran über die Anordnung von Schwimmern Iterieren und Schreiben jedes float des Schreiber individuell , mit der Write(double) Methode. Es wird nach wie vor innerhalb des Schriftstellers wegen Pufferung schnell sein. Siehe sixlettervariables-Zahlen.

Wir haben eine Klasse namens LudicrousSpeedSerialization und es enthält folgende unsichere Methode:

    static public byte[] ConvertFloatsToBytes(float[] data)
    {
        int n = data.Length;
        byte[] ret = new byte[n * sizeof(float)];
        if (n == 0) return ret;

        unsafe
        {
            fixed (byte* pByteArray = &ret[0])
            {
                float* pFloatArray = (float*)pByteArray;
                for (int i = 0; i < n; i++)
                {
                    pFloatArray[i] = data[i];
                }
            }
        }

        return ret;
    }

Auch wenn es im Grunde ein hinter den Kulissen für Schleife tut, tut es in einer Linie um den Job tun

byte[] byteArray = floatArray.Select(
                    f=>System.BitConverter.GetBytes(f)).Aggregate(
                    (bytes, f) => {List<byte> temp = bytes.ToList(); temp.AddRange(f); return temp.ToArray(); });
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top