Autor Beitrag
Christian S.
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Mo 24.01.11 11:43 
Hallo!

Ich suche nach der schnellsten Methode, um Bytes in einem UInt16 umzudrehen.

Der Hintergrund ist folgender:

  1. Ich lese 16bit-Daten als Byte-Array (_Bytes) von einer Kamera.
  2. Dann wird das ganze in ein Array von UInt16 kopiert.
    Das Array heißt _BytereversedLinearData, dazu gibt es noch einen Pin _LinearDataPin, kopiert wird dann so: Marshal.Copy(_Bytes, 0, _LinearDataPin.AddrOfPinnedObject(), _Bytes.Length);

Jetzt möchte ich gerne die korrekten UInt16 haben, also im Prinzip in jedem Element von _BytereversedLinearData die Bytes rumdrehen. Das in jedem Element über das Shiften und "verodern" der Bits zu machen, ist einfach richtig langsam.

Kennt jemand eine schnellere Methode?

Grüße
Christian

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
Christian S. Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Mo 24.01.11 12:10 
Diese Lösung scheint recht schnell zu sein:
ausblenden C#-Quelltext
1:
2:
3:
Array.Reverse(_Bytes);
Marshal.Copy(_Bytes, 0, _LinearDataPin.AddrOfPinnedObject(), _Bytes.Length);
Array.Reverse(_BytereversedLinearData);

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mo 24.01.11 12:29 
Hehe, interessante Lösung; schneller gehen dürfte es trotzdem noch :) . Wie genau hängen denn _LinearDataPin und _BytereversedLinearData zusammen :gruebel: ? Bzw. kannst du die Bytes nicht schon in _Bytes umdrehen?

_________________
>λ=
Christian S. Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 20451
Erhaltene Danke: 2264

Win 10
C# (VS 2019)
BeitragVerfasst: Mo 24.01.11 12:33 
user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Hehe, interessante Lösung; schneller gehen dürfte es trotzdem noch :) .

Das wäre schön :-)

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Wie genau hängen denn _LinearDataPin und _BytereversedLinearData zusammen :gruebel: ?

Oh, ich dachte das mit dem Pin wäre eindeutig: _LinearDataPin = GCHandle.Alloc(_BytereversedLinearData , GCHandleType.Pinned);

user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Bzw. kannst du die Bytes nicht schon in _Bytes umdrehen?

Ja, schon, aber ich bezweifle, dass das performanter ist, weil ich dann ja doch wieder jedes Element anfassen muss. Werde es aber nach der Mittagspause mal ausprobieren :-)

_________________
Zwei Worte werden Dir im Leben viele Türen öffnen - "ziehen" und "drücken".
Kha
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 3803
Erhaltene Danke: 176

Arch Linux
Python, C, C++ (vim)
BeitragVerfasst: Mo 24.01.11 14:07 
user profile iconChristian S. hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Wie genau hängen denn _LinearDataPin und _BytereversedLinearData zusammen :gruebel: ?

Oh, ich dachte das mit dem Pin wäre eindeutig: _LinearDataPin = GCHandle.Alloc(_BytereversedLinearData , GCHandleType.Pinned);
Ah, im Zusammenhang mit Hardware hat es mein Hirn nicht ganz von "Pin" nach "Pinned" geschafft :lol: .



user profile iconChristian S. hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconKha hat folgendes geschrieben Zum zitierten Posting springen:
Bzw. kannst du die Bytes nicht schon in _Bytes umdrehen?

Ja, schon, aber ich bezweifle, dass das performanter ist
Ich muss mich wohl geschlagen geben - zumindest unter x86 ;) .

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
x86 --
TotalReversal: 1850 ms
Unsafe: 2331 ms
UnsafeInline: 2026 ms // Das Pinnen allein scheint nicht viel auszumachen...
Safe: 2026 ms // ...und die Bounds Checks überhaupt nichts

x64 --
TotalReversal: 2042 ms
Unsafe: 1416 ms
UnsafeInline: 1034 ms
Safe: 1354 ms


ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
    static ushort[] TotalReversal(byte[] bytes)
    {
      Array.Reverse(bytes);
      var ret = new ushort[bytes.Length / 2];

      var h = GCHandle.Alloc(ret, GCHandleType.Pinned);
      Marshal.Copy(bytes, 0, h.AddrOfPinnedObject(), bytes.Length);
      h.Free();

      Array.Reverse(ret);
      return ret;
    }

    static unsafe ushort[] Unsafe(byte[] bytes)
    {
      var ret = new ushort[bytes.Length / 2];
      fixed (byte* p = &bytes[0])
      fixed (ushort* q = &ret[0])
      {
        byte* p2 = p;
        ushort* q2 = q;
        for (int i = 0; i < ret.Length; i++)
          *q2++ = (ushort)(*p2++ + *p2++ << 8);
      }

      return ret;
    }

    static unsafe void UnsafeInline(byte* p2, ushort* q2, int length)
    {
      for (int i = 0; i < length; i++)
        *q2++ = (ushort)(*p2++ + *p2++ << 8);
    }

    static ushort[] Safe(byte[] bytes)
    {
      var ret = new ushort[bytes.Length / 2];
      for (int i = 0; i < ret.Length; i++)
        ret[i] = (ushort)(bytes[2 * i] + bytes[2 * i + 1] << 8);

      return ret;
    }

    static unsafe void Main(string[] args)
    {
      var bytes = Enumerable.Range(0, (int)100e6).Select(i => (byte)i).ToArray();
      var sw = Stopwatch.StartNew();
      var ret = new ushort[bytes.Length / 2];
      fixed (byte* p = &bytes[0])
      fixed (ushort* q = &ret[0])
      {
        for (int i = 0; i < 10; i++)
          TotalReversal(bytes);
        Console.WriteLine("TotalReversal: " + sw.ElapsedMilliseconds + " ms");

        sw.Restart();
        for (int i = 0; i < 10; i++)
          Unsafe(bytes);
        Console.WriteLine("Unsafe: " + sw.ElapsedMilliseconds + " ms");

        sw.Restart();
        for (int i = 0; i < 10; i++)
          UnsafeInline(p, q, ret.Length);
        Console.WriteLine("UnsafeInline: " + sw.ElapsedMilliseconds + " ms");

        sw.Restart();
        for (int i = 0; i < 10; i++)
          Safe(bytes);
        Console.WriteLine("Safe: " + sw.ElapsedMilliseconds + " ms");
      }


Einen Moment, das sollte sich doch sogar linear parallelisieren lassen :D ...

_________________
>λ=