[Visual C++] Zeiger auf Ausgerichtetes deklarieren

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
Krishty
Establishment
Beiträge: 8350
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

[Visual C++] Zeiger auf Ausgerichtetes deklarieren

Beitrag von Krishty »

Hi,

Wie deklariere ich in Visual C++ einen Zeiger auf ausgerichtete Daten?
__declspec(align(16)) int * x;
oder
int * __declspec(align(16)) x;
?

Gruß, Ky
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Sternmull
Establishment
Beiträge: 264
Registriert: 27.04.2007, 00:30
Echter Name: Til
Wohnort: Dresden

Re: [Visual C++] Zeiger auf Ausgerichtetes deklarieren

Beitrag von Sternmull »

Ist zwar nicht das was du hören willst aber... was soll das bringen? Willst du das der Compiler dich warnt (oder einen Fehler bringt) wenn du dem Zeiger eine Adresse zuweist für die das Alignment nicht statisch garantiert ist?

Abgesehen davon könntest du wie hier ein Typedef mit Alignment deklarieren und das dann als Typ verwenden auf den gezeigt wird:

Code: Alles auswählen

typedef __declspec(align(16)) int IntAligned16;
IntAligned16 * x;
Falls das häufig gebraucht wird könnte man es per Template noch ein bisschen hübscher machen (z.B. das man "Aligned<int, 16>::type" schreiben kann). Allerdings befürchte ich das es dem Zeiger egal ist ob nun auf einen aligned oder unaligned int gezeigt wird und er die Zuweisung aller int * ohne murren frisst. Ausprobieren kann ich das aber grad nicht.
Benutzeravatar
Krishty
Establishment
Beiträge: 8350
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [Visual C++] Zeiger auf Ausgerichtetes deklarieren

Beitrag von Krishty »

Sternmull hat geschrieben:Ist zwar nicht das was du hören willst aber... was soll das bringen? Willst du das der Compiler dich warnt (oder einen Fehler bringt) wenn du dem Zeiger eine Adresse zuweist für die das Alignment nicht statisch garantiert ist?
Japp, oder zumindest, dass er das in der Zukunft mal tut.
Sternmull hat geschrieben:Abgesehen davon könntest du wie hier ein Typedef mit Alignment deklarieren und das dann als Typ verwenden auf den gezeigt wird:
ARGH so einfach :D Klar, danke!
Sternmull hat geschrieben:Allerdings befürchte ich das es dem Zeiger egal ist ob nun auf einen aligned oder unaligned int gezeigt wird und er die Zuweisung aller int * ohne murren frisst. Ausprobieren kann ich das aber grad nicht.
Ja, sieht leider so aus. Wird sich das mit alignas() ändern?
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
joggel

Re: [Visual C++] Zeiger auf Ausgerichtetes deklarieren

Beitrag von joggel »

Eehhm... sorry für die Frage, aber was bedeudet das:
einen Zeiger auf ausgerichtete Daten
also, was bedeudet "ausgerichtete Daten"?

Gruß J...
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: [Visual C++] Zeiger auf Ausgerichtetes deklarieren

Beitrag von Aramis »

Das sind Daten, deren Startadresse durch ein bestimmtes Viefaches von 2, das Alignment, ohne Rest teilbar ist. Siehe Wikipedia.

Bestimmte Operationen, z.b Speicherzugriffe, erfordern bestimmtes Alignment. In der Praxis wird Alignment oft verdraengt weil z.b. x86 CPUs in der Lage sind auch auf nicht korrekt angeordneten Daten zu operieren, wenngleich dabei ein betraechtlicher Performanceverlust entsteht. Auf anderen Plattformen hagelt es stattdessen Alignment Faults.
joggel

Re: [Visual C++] Zeiger auf Ausgerichtetes deklarieren

Beitrag von joggel »

Aha, das ist ja interessant.
Danke für die Erklärung.
Würde mich mal interessieren, was das an Performance bringt...
Vlt. bastel ich mal was.

Da muss ich gleichnochmal nachfrage:
Angenommen, ich hätte Bildinformationen im Speicher, RGB, jeweils char (1Byte).
Also wäre es performanter, wenn ich die Bilddaten als jeweils 1Integer bzw. short pro Kanal speichere?
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: [Visual C++] Zeiger auf Ausgerichtetes deklarieren

Beitrag von Aramis »

Jein, es waere ggf. performanter sie als 4-Tupel (RGBA … ist ja egal ob das ’A’ benutzt wird oder nicht) zu speichern.

- Die Addressierung des n’ten Pixels kann dann ueber den Shift base+n<<2 erfolgen (bzw.: der Compiler hat zumindest die Option dazu, ich bin mir auswendig nicht sicher, ob es in der x86-Architektur nicht flotter waere die Addressierung ueber mov/lea zu erledigen).
- Der Zugriff auf ein individuelles Pixel wird flotter, weil es in jedem Fall 4-aligned ist.
Benutzeravatar
Schrompf
Moderator
Beiträge: 5161
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [Visual C++] Zeiger auf Ausgerichtetes deklarieren

Beitrag von Schrompf »

Für solche Fälle wird manchmal auch ein StructOfArrays anstatt ArrayOfStructs empfohlen. Also RRRRGGGGBBBB anstatt RGBRGBRGBRGB - alle Rotwerte nacheinander, dann alle Grünwerte, dann alle Blauwerte.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8350
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [Visual C++] Zeiger auf Ausgerichtetes deklarieren

Beitrag von Krishty »

joggel hat geschrieben:Da muss ich gleichnochmal nachfrage:
Angenommen, ich hätte Bildinformationen im Speicher, RGB, jeweils char (1Byte).
Lustig, weil ich die Frage nur stelle, weil ich auch Bilddaten möglichst performant durchboxen will.
joggel hat geschrieben:Also wäre es performanter, wenn ich die Bilddaten als jeweils 1Integer bzw. short pro Kanal speichere?
Performanter für was? Ein komplexer Kompressionsalgorithmus verhält sich völlig anders als die Konvertierung von Bilddaten von RGB zu BGR …

Generell gilt, dass die Datenmenge so klein wie möglich zu halten ist. Jedes Mal, wenn die CPU auf Daten zugreift, die noch nicht im Cache sind, muss sie leer laufen – heute zwischen 100 und 400 Takte lang, was im Vergleich zu der Verarbeitungszeit der meisten Daten gigantisch ist. Machst du deine Pixel ohne Nutzwert doppelt oder viermal so groß indem du für die Kanäle short oder int benutzt, läuft deine CPU auch ohne Nutzen doppelt bis viermal so lange leer, um all die Daten zu laden (auch, wenn der Verlust in der Realität schwächer ausfällt, weil moderne CPUs die Speicherlatenz einigermaßen verstecken bzw anderweitig nutzen (HyperThreading) können).

Jetzt weiter zu ausgerichteten Daten: die CPU kann Speicher am schnellsten laden, wenn die Adresse des Elements glatt durch seine Größe teilbar ist – vier Bytes große ints sollten also immer auf einer durch vier teilbaren Adresse liegen, 128-Bit-float4-Vektoren sollten immer auf einer durch 16 teilbaren Adresse liegen usw. Grund dafür ist der Aufbau der Speichereinheit.
Über den Performance-Nachteil nicht ausgerichteter Daten gibt es widersprüchliche Aussagen. Manche sagen, er falle nicht ins Gewicht; andere sprechen von einer Halbierung der Lade-Performance. Fakt ist: Lädt die CPU z.B. ein int, das auf einer Adresse mit 2 am Ende liegt, muss es die ints bei den Adressen 0 und 4 laden, per 0x0000FFFF und 0xFFFF0000 maskieren, die Bits herumschieben und per bitweisem OR zusammenführen, bevor die Zahl im Register landet. Allzweck-Architekturen wie x86 haben dafür oft eigene Schaltkreise; bei IA-64 muss die CPU das „manuell“ erledigen und dann dauert es schonmal viermal so lang wie ein ausgerichteter Zugriff.
Darüber musst du dir übrigens keine großen Sorgen machen: Der Compiler sorgt dafür, dass die meisten lokalen Variablen und Klassenattribute (durch Padding) ausgerichtet sind; außerdem ist der Speicher, den new und malloc() zurückgeben, immer 8-Byte-ausgerichtet.

Jetzt zu dem, was Aramis sagte – Alphakomponente (oder generell ein Füllbyte) hinzufügen. Vorher lagen deine Pixel so im Speicher:
RGBRGBRGBRGBRGBR
0123456789ABCDEF

Jetzt so:
RGBxRGBxRGBxRGBx
0123456789ABCDEF

Vorher haben deine RGB-Tripel an beliebigen Adressen gestartet (weil die Vielfachen von 3 durch so ziemlich jede andere Zahl teilbar sind). Jetzt starten sie ausschließlich an Adressen, die durch vier teilbar sind. Anstatt dass du drei Kanäle einzeln laden und zusammenfügen musst (oder das die CPU erledigen lässt) kannst du ein Tripel als int adressieren und schnell laden. Wenn ausgerichtetes Laden und die Logikvereinfachung 50 % bessere Performance bewirken, aber das Füllbyte 33 % mehr Cache-Misses provoziert, sind wir immernoch bei einer Verbesserung.

Es geht aber noch weiter: Wenn du weißt, dass das Bild eine durch vier teilbare Breite hat, kannst du immer vier Pixel auf einmal laden – und zwar als drei ints, die jeweils wieder ausgerichtet sind, weil du das Bild quasi in 12er-Pakete zerstückelst:
RGBRGBRGBRGB RGBRGBRGBRGB RGBRG…
0123456789AB CDEF01234567 89ABCDE…

(Beachte, dass ein Block jeweils bei 0, 4, 8 oder C – also einer durch vier teilbaren Adresse – anfängt.)
Du lädst also drei ausgerichtete Register aus dem Speicher in die CPU. Angenommen, wir sind immernoch bei der RGB-zu-BGR-Konvertierung, so kannst du die jetzt munter Bits dieser Register herumschieben, vertauschen oder die Daten auf vier Register mit jeweils RGB entpacken und wieder zusammenführen und letztendlich an die Zieladresse schreiben.
Hier haben wir jetzt 50 % bessere Performance dadurch, dass wir nur ausgerichtete Daten laden, und zwar ohne die zusätzlichen Cache-Misses, weil die Datenmenge genauso groß bleibt wie zuvor.

Es gibt aber immernoch Raum für Verbesserungen. Wie gesagt, Speicherbandbreite zum Cache ist ein Problem. Bei der Konvertierung von Bilddaten haben wir eine ganze besondere Charakteristik: Was wir einmal von der Quelladresse laden, packen wir nie wieder auch nur mit der Kneifzange an. Ebenso kehren wir niemals zu den Daten zurück, die wir an die Zieladresse geschrieben haben.
Der Cache verhält sich aber genau so: Zur Hälfte wird er mit dem gefüllt, was wir in letzter Zeit gelesen haben, und zur anderen Hälfte mit dem, was wir in letzter Zeit geschrieben haben. Das können wir komplett umkrempeln: Der Cache soll nichts beinhalten als die Daten, die wir demnächst verarbeiten werden, damit sich die Ladezeit vom Hauptspeicher auf null reduziert.
Leider bietet C++ keine Sprachmittel dafür an, aber: Der SSE-Anweisungssatz stellt uns die MOVNTI(MOVe Non-Temporal Integer)-Anweisung zur Verfügung. Die lädt Daten aus dem RAM, ohne, dass sie im Cache landen und kann umgekehrt auch CPU-Register in den Speicher schieben, ohne, dass sie den Cache passieren. Gleichzeitig bietet der Befehlssatz einige PREFETCH-Anweisungen, die die CPU anweisen, den Cache mit Daten an einer gegebenen Adresse zu füllen. Streuen wir diese Anweisung so ein, dass sie Speicher ein paar KiB vor der Adresse, die wir momentan verarbeiten, referenziert, liegen diese Bilddaten schon fertig im Cache, sobald sie unsere Konvertierung erreicht. Im besten Fall limitiert unsere Konvertierung dann nur noch der RAM-Durchsatz (das wären bei einem Core i7 12 GiB/s vom RAM zur Northbridge, wenn ich mich recht irre – oder eine Bitmap von 65536×65536 RGB-Pixeln pro Sekunde).

Auf Schrompfs Empfehlung bin ich auch gestern erst wieder gestoßen … für mich ist sie aber ungeeignet, weil die Daten irgendwann auch wieder auf dem Bildschirm oder in einer Datei landen müssen – und die erwarten RGB. Das Zusammenführen würde wieder bedeutend länger brauchen, als man durch das Trennen vorher gespart hat.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
joggel

Re: [Visual C++] Zeiger auf Ausgerichtetes deklarieren

Beitrag von joggel »

:shock:
Mehr kann ich im Moment dazu nicht sagen...
Ach ja, das mit dem "Vlt. bastel ich mal was." war jetzt nicht so 100%ig ernst gemeint!
Danke für die sehr gute Erklärung...
Benutzeravatar
Schrompf
Moderator
Beiträge: 5161
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas
Wohnort: Dresden
Kontaktdaten:

Re: [Visual C++] Zeiger auf Ausgerichtetes deklarieren

Beitrag von Schrompf »

Ich mag Krishtys Ausführungen... da lernt man immer noch was bei. Danke dafür!
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Antworten