Laden...

Concate byte[]

Erstellt von Robertico vor 15 Jahren Letzter Beitrag vor 15 Jahren 7.809 Views
R
Robertico Themenstarter:in
344 Beiträge seit 2006
vor 15 Jahren
Concate byte[]

Beschreibung:

Lange habe ich gesucht um etwas zu finden, das byte-Arrays zusammenfügt.
Schnell musste es sein und absolut flexibel. Nichts gefunden, also musste ich etwas machen.

Das schnellste ist das mit dem MemoryStream.

Die Sekunden wurden erreicht bei 1.000.000 Durchläufen mit jeweils 22 Arrays a´2-8 byte.


        public static byte[] ConcateByte(params object[] _Arrays) // 2,9 Sec
        {
            MemoryStream m = new MemoryStream();
            for (int i = 0; i < _Arrays.Length; i++)
            {
                byte[] tmp = (byte[])_Arrays[i];
                m.Write(tmp, 0, tmp.Length);
            }
            return m.ToArray();
        }
        public static byte[] ConcateByte1(params object[] _Arrays) // 3,14 Sec
        {
            MemoryStream m = new MemoryStream();
            BinaryWriter bw = new BinaryWriter(m);
            for (int i = 0; i < _Arrays.Length; i++)
            {
                bw.Write((byte[])_Arrays[i]);
            }
            return m.ToArray();
        }
        public static byte[] ConcateByte2(params object[] _Arrays) // 4,5 Sec
        {
            int[] laengen = new int[_Arrays.Length + 1];
            int sum = 0;
            for (int i = 0; i < _Arrays.Length; i++)
            {
                laengen[i + 1] = ((byte[])_Arrays[i]).Length + sum;
                sum += laengen[i + 1] - sum;
            }
            byte[] ArrayReturn = new byte[sum];
            for (int i = 0; i < _Arrays.Length; i++)
            {
                ((byte[])_Arrays[i]).CopyTo(ArrayReturn, laengen[i]);
            }
            return ArrayReturn;
        }


Schlagwörter: byte-Arrays zusammenfügen concate

0
767 Beiträge seit 2005
vor 15 Jahren

Hier mal die generische Variante.

        
public static T[] Concat<T>(params T[][] arrays)
        {
            int length = 0;
            foreach (T[] array in arrays)
            {
                length += array.Length;
            }

            T[] result = new T[length];

            int start = 0;
            foreach (T[] array in arrays)
            {
                Array.Copy(array, 0, result, start, array.Length);
                start += array.Length;
            }

            return result;
        }

Könnte schneller sein weil

  1. keine casts
  2. kein boxing (verpacken von value types in reference types - das ist nötig wenn zB ein int an eine methode übergeben wird die ein object erwartet.)

Beispiel Aufruf (könnten aber auch Bytes sein)


            int[] a1 = new int[] { 1, 2, 3 };
            int[] a2 = new int[] { 6, 7, 8 };
            int[] a3 = new int[] { 11, 22, 33 };

            int[] result = Concat(a1, a2, a3); //   Concat<int>(...) kann man sich sparen, das checkt der compiler

Würd mich wundern, wenn der MemoryStream, der mitwachsen und daher immer wieder umkopieren muss schneller wäre als wenn man die Länge des Resultats vorberechnet.

loop:
btst #6,$bfe001
bne.s loop
rts

R
Robertico Themenstarter:in
344 Beiträge seit 2006
vor 15 Jahren

Danke 0815Coder,

Würd mich wundern, wenn der MemoryStream, der mitwachsen und daher immer wieder umkopieren muss schneller wäre als wenn man die Länge des Resultats vorberechnet.

Scheinbar doch.
Mit gleichen Parametern 3,6 Sekunden. Habe aber wieder etwas gelernt und werde damit "rumprobieren".

Gruß Robert

R
Robertico Themenstarter:in
344 Beiträge seit 2006
vor 15 Jahren

Angeregt durch obiges Beispiel war ich absolut überzeugt, dass dies hier noch schneller ist:


        public static byte[] ConcateByteArrays(params byte[][] _Arrays) // 3,1 Sec
        {
            MemoryStream m = new MemoryStream();
            foreach (byte[] array in _Arrays)
            {
                m.Write(array, 0, array.Length);
            }
            return m.ToArray();
        }

🙁 Nachher nicht mehr.

😁 Aber schön kurz

0
767 Beiträge seit 2005
vor 15 Jahren

Wenn du die Performance Tests machst, versuchs mit längeren Arrays. 2-8 Byte sind weniger als ein Klacks, 100-1000 dürfen es schon sein, und schraub stattdessen die Durchläufe runter. Um Ausreisser zu vermeiden kann man da ruhig aucuh GC.Collect() aufrufen (aber nur für die Tests).

Siehe auchStrings performant verketten

loop:
btst #6,$bfe001
bne.s loop
rts

3.971 Beiträge seit 2006
vor 15 Jahren

public static byte[] ConcateByteArrays(params byte[][] _Arrays) // 3,1 Sec
        {
            MemoryStream m = new MemoryStream();
            m.SetLength = _Arrays.Length * _Arrays[0].Length; //Erspart dir lästiges umkopieren beim Hinzufügen.
            foreach (byte[] array in _Arrays)
            {
                m.Write(array, 0, array.Length);
            }
            return m.ToArray();
        }

Besser wäre wenn du aber statt einem Jagged-Array (Baum) mit mehrdimensionalen Arrays arbeitest, da die Größe eines Jagged-Arrays erst beim Durchlaufen bekannt wird.

Wie berechnest bzw. misst du die Zeiten?

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

R
Robertico Themenstarter:in
344 Beiträge seit 2006
vor 15 Jahren

@0815Coder

Der Test mit 12 * 1240 byte entscheidet eindeutig für deine Version. Sie ist dann drei mal schneller.

Von daher hat sich der Threat schon gelohnt. Und wenn Google zu Besuch war ist es auch zu finden.

Interessant ist, dass offensichtlich ein Array mit objects schneller an eine Methode übergeben wird als eins mit byte[].

@kleines_eichhoernchen

m.SetLength = _Arrays.Length * _Arrays[0].Length;

geht nicht, da die Arrays nicht die gleiche Größe haben.

Die Zeitvergleiche sind ja nur relativ, d.h. für meinen PC. Andere sind schneller oder langsamer.

Der Test läuft so ab:


            System.Diagnostics.Stopwatch stw = new System.Diagnostics.Stopwatch();
Beginn:
            int anzahlTests= 20000;
            stw.Reset();
            stw.Start();
// beginn Test
            //-----------------------------------------------------------------
            for (int i = 0; i < anzahlTests; i++)
            {
                byte[] uInt16231 = Umwandlungen.ConcateByteArrays(uInt1623, uInt1623, uInt1623, uInt1623, uInt1623, uInt1623, uInt1623, uInt1623, uInt1623, uInt1623, uInt1623, uInt1623);
            }
            //-----------------------------------------------------------------
// ende Test
            stw.Stop();
            double timePro = ((double)stw.ElapsedMilliseconds / (double)anzahlTests )*1000;// == 3,541 e-09 Sekunden (Dies braucht eine leere For-i-Schleife
            System.Diagnostics.Debugger.Break();
goto Beginn;

Das, was in der Schleife steht ändert sich dann.

Um den Versuch zu dokumentieren habe ich mir noch etwas gebaut. Allerdings muss bei Änderungen dafür gesorgt werden, dass abgespeichert ist.


            string result = "";
            StreamReader sr = new StreamReader("..\\..\\Form1.cs", System.Text.Encoding.Default);
            while (!sr.ReadLine().Trim().StartsWith("// beginn Test"));
            string line = sr.ReadLine() + Environment.NewLine + "// " + timePro.ToString() + " Sekunden pro 1.000.000";
            while (!line.Trim().StartsWith("// ende Test"))
            {
                if (line.Trim().StartsWith("//"))
                {
                    line = line.Trim();
                }
                result += line+Environment.NewLine;
                line = sr.ReadLine();
            }
            sr.Close();
            Clipboard.SetText(result);

Gruß Robert

3.971 Beiträge seit 2006
vor 15 Jahren

Ich würde zusätzlich


GC.Collect(2); //Alles ordentlich aufräumen
int startCol = GC.CollectionCount(2); //Bis zur 3. Generation
//Zeitmessung start
  ...
//Zeitmessung ende
int endCol = GC.CollectionCount(2);
Console.WriteLine("Anzahl der GCs: {0}", endCol - startCol);

noch einbauen, um auch die Anzahl der GC-Durchläufe miteinzubeziehen und auch darauf entsprechend zu optimieren (wenn Bedarf besteht).

Es gibt 3 Arten von Menschen, die die bis 3 zählen können und die, die es nicht können...

0
767 Beiträge seit 2005
vor 15 Jahren

Besser wäre wenn du aber statt einem Jagged-Array (Baum) mit mehrdimensionalen Arrays arbeitest, da die Größe eines Jagged-Arrays erst beim Durchlaufen bekannt wird.

Performancetechnisch ist das richtig.

aber mit params kann man keine mehrdimensionalen Arrays verwenden. ausserdem können ja auch alle arrays die er übergibt unterschiedlich lang sein.

loop:
btst #6,$bfe001
bne.s loop
rts