Bin mit Funktion unzufrieden
-
Du meinst das so?
inline unsigned long rgba2rgb (unsigned long value) { return ((((value & 0x000000ff) >> 0)%32) + ((((value & 0x0000ff00) >> 8)%64) << 6) + ((((value & 0x00ff0000) >> 16)%32) << 11)); }
-
ich würd's so bauen:
inline unsigned long rgba2rgb (unsigned long value) { unsigned long r = (value & 0x00ff0000) >> 19; unsigned long g = (value & 0x0000ff00) >> 10; unsigned long b = (value & 0x000000ff) >> 3; return b | (g << 6) | (r << 11); }
-
Find das aber schöner und es werden keine 4 temp-variabeln angelegt
inline unsigned long rgba2rgb (unsigned long value) { return ((value & 0x000000ff) >> 3) | (((value & 0x0000ff00) >> 10) << 6) | (((value & 0x00ff0000) >> 19) << 11); }
Aber ich frag mich ob man da noch was optimieren kann?
-
ich wette, daß die temps wegoptimiert werden.
Und einfacher zu lesen isses auch. Aber das ist wohl geschmacksache.
-
Ich wette gleich kommt Volkard - "Der Pate" und gibt auch seinen Senf dazu ab
-
inline unsigned long rgba2rgb (unsigned long value) { unsigned char a = (unsigned char)((value & 0xff000000) >> 24); unsigned char r = (unsigned char)((value & 0x00ff0000) >> 16); unsigned char g = (unsigned char)((value & 0x0000ff00) >> 8); unsigned char b = (unsigned char)((value & 0x000000ff) >> 0); return (((b%32) + ((g%64) << 6) + ((r%32) << 11))); }
kannt versuchen, mit
unsigned char &r = ((unsigned char*)&value)[1];
und so zu tricksen.
oh, eins fällt mir auf.
return (((b%32) + ((g%64) << 6) + ((r%32) << 11)));
evtl ist folgendes besser
return (((b%32u) + ((g%64u) << 6) + ((r%32u) << 11)));
weil beim modulo-rechnen mit negativen zahlen immer was seltsam ist.
vielleicht war's das schon.mal schauen, was die funktion machen soll.
also reinkommen tut ein int mit
aaaaaaaarrrrrrrrggggggggbbbbbbbb
und rauskommen soll
rrrrrggggggbbbbb
wenn ich recht sehe.und zwar die low bits von r, b und g.
komisch, aber worde ja schon angefragtm, ob wirklich low gemeint ist.also aus ***********rrrrr**gggggg***bbbbb soll werden 0000000000000000rrrrrggggggbbbbb mal schauen. ich stopfe 10987654321098765432109876543210 //bitpositionen ***********rrrrr**gggggg***bbbbb in eax rein. dann shifte ich ax (nicht eax) um zwei nach links 10987654321098765432109876543210 //bitpositionen ***********rrrrrgggggg***bbbbb00 dann shifte ich eax um 6 nach links 10987654321098765432109876543210 //bitpositionen *****rrrrrgggggg***bbbbb00000000 dann shifte ich ax um 3 nach links 10987654321098765432109876543210 //bitpositionen *****rrrrrggggggbbbbb00000000000 dann shifte ich eax um 11 nach rechts 10987654321098765432109876543210 //bitpositionen 00000000000*****rrrrrggggggbbbbb und gebe ax zurück.
inline unsigned short rgba2rgb (unsigned long value){ //value wird hoffentlich in eax landen *grins* *(unsigned short)&value<<=2; value<<=6; *(unsigned short)&value<<=3; value>>=11; return value; }
klingt nach 4 assemblerbefehlen zu je einem takt, falls der compiler so lieb ist, meine gedanken zu lesen.
wenn die high-bits gemeint sind, siehts ein wenig gemeiner aus.
also aus ********rrrrr***gggggg**bbbbb*** soll werden 0000000000000000rrrrrggggggbbbbb mal schauen. ich stopfe 10987654321098765432109876543210 //bitpositionen ********rrrrr***gggggg**bbbbb*** in eax rein. eax um 3 nach rechts 10987654321098765432109876543210 //bitpositionen 000********rrrrr***gggggg**bbbbb ax um 3 nach links 10987654321098765432109876543210 //bitpositionen 000********rrrrrgggggg**bbbbb000 eax um 6 nach links 10987654321098765432109876543210 //bitpositionen *****rrrrrgggggg**bbbbb000000000 ax um 2 nach links 10987654321098765432109876543210 //bitpositionen *****rrrrrggggggbbbbb00000000000 eax um 11 nach rechts 10987654321098765432109876543210 //bitpositionen 00000000000*****rrrrrggggggbbbbb
das sind jetzt 5 schifts.
leider hängt jede berechnung von den ergebnissen der vorherigen ab. das sieht danach aus, als könne hier nur eine ALU verwendet werden. nix mit voll parallel abarbeiten im prozessor.
vielleicht isses besser, sich erstmal möglichst temp-variablen zu holen, damit die unabhängig voneinander bearbeitet werden können.
also aus //v == value //e == ergebnis v: ********rrrrr***gggggg**bbbbb*** soll werden e: 0000000000000000rrrrrggggggbbbbb r = v g = v b = v r >>= 19 //ALU1 g >>= 11 //ALU2 b >>= 3 //ALU3 r &= 0x1f //ALU1 g &= 0x3f //ALU2 b &= 0x1f //ALU3 e = ups, sollte ich echt wieder zurückschieben wie jester? nee, bin ich zu faul zu. r = v g = v b = v r >>= 6 //ALU1 g >>= 4 //ALU2 b >>= 3 //ALU3 r &= 0x1f<<11 //ALU1 g &= 0x3f<<6 //ALU2 b &= 0x1f //ALU3 r |= g //ALU1 r |= b //ALU1 return r;
naja, so ähnlich halt. normalerweise kann der compiler echt prime den code so umsortieren, daß der prozessor die ressourcen total klasse parallel benutzen darf.
-
volkard schrieb:
inline unsigned short rgba2rgb (unsigned long value){ //value wird hoffentlich in eax landen *grins* *(unsigned short)&value<<=2; value<<=6; *(unsigned short)&value<<=3; value>>=11; return value; }
Lässt sich nicht compilieren:
main.cpp(39) : error C2100: illegal indirection
main.cpp(39) : error C2106: '<<=' : left operand must be l-value
main.cpp(41) : error C2100: illegal indirection
main.cpp(41) : error C2106: '<<=' : left operand must be l-value
-
volkard schrieb:
oh, eins fällt mir auf.
return (((b%32) + ((g%64) << 6) + ((r%32) << 11)));
evtl ist folgendes besser
return (((b%32u) + ((g%64u) << 6) + ((r%32u) << 11)));
weil beim modulo-rechnen mit negativen zahlen immer was seltsam ist.Blöde aber für mich interessante Frage: Wie kann mit negativen Zahlen gerechnet werden wenn 'value' ein unsigned ist? *kratz und grübel*
-
''' schrieb:
volkard schrieb:
oh, eins fällt mir auf.
return (((b%32) + ((g%64) << 6) + ((r%32) << 11)));
evtl ist folgendes besser
return (((b%32u) + ((g%64u) << 6) + ((r%32u) << 11)));
weil beim modulo-rechnen mit negativen zahlen immer was seltsam ist.Blöde aber für mich interessante Frage: Wie kann mit negativen Zahlen gerechnet werden wenn 'value' ein unsigned ist? *kratz und grübel*
b war ein unsigned char. und 32 ein int.
dann macht er den b auch erstmal zu nem int.
du weißt zwar, daß nu in dem neuen int, der den wert von b hat, was nicht-negatives stehen sollte. aber der compiler verfolgt gewöhnlich sowas nicht.
also erzeugt er den asm-code für int opertator%(int,int).
-
''' schrieb:
Lässt sich nicht compilieren:
main.cpp(39) : error C2100: illegal indirection*(unsigned short*)&value<<=2;
-
verstehe! vielen dank
Und was ist mit dem Code der nicht compiliert werden kann wegen l-value? (was immer das auch ist?)
-
volkard schrieb:
inline unsigned short rgba2rgb (unsigned long value){ //value wird hoffentlich in eax landen *grins* *(unsigned short)&value<<=2; value<<=6; *(unsigned short)&value<<=3; value>>=11; return value; }
vielleicht sollte man stattdessen schreiben
inline unsigned short rgba2rgb (unsigned long value){ union{ struct{ unsigned short ax; unsigned short unused; }; unsigned int eax; } data; //data wird hoffentlich in eax landen *grins* data.eax=value; data.ax<<=2; data.eax<<=6; data.ax<<=3; data.eax>>=11; return data.eax; }
und hoffen, daß er die beiden zuweisungen wegoptimiert. falls er das tut, wäre der code wenigstens einigermaßen hübsch zu lesen.
-
volkard schrieb:
vielleicht sollte man stattdessen schreiben
inline unsigned short rgba2rgb (unsigned long value){ union{ struct{ unsigned short ax; unsigned short unused; }; unsigned int eax; } data; //data wird hoffentlich in eax landen *grins* data.eax=value; data.ax<<=2; data.eax<<=6; data.ax<<=3; data.eax>>=11; return data.eax; }
... oder gleich Inline-Assembler verwenden
-
''' schrieb:
Und was ist mit dem Code der nicht compiliert werden kann wegen l-value? (was immer das auch ist?)
Mit einem reinterpret_cast geht es:
inline unsigned short rgba2rgb (unsigned long value) { *(reinterpret_cast <unsigned short*> (&value)) <<= 2; value<<=6; *(reinterpret_cast <unsigned short*> (&value)) <<= 3; value>>=11; return (value); }
Moritz
-
wozu eigentlich die obsession mit eax ? inline funktionen haben keine bestimmte parameterkonvention. ausserdem sind unnamed structs kein standard
andererseits ist der code auf big endian systemen nat. sowieso kaputt.
hier noch eine eher esoterische variante:inline unsigned short rgba2rgb (unsigned value){ unsigned g = value; value &= 0x00f800f8u; g &= 0x0000fc00u; value >>= 3; g <<= 11; value *= 0x04000001u; value |= g; value >>= 16; return value; }
allerdings zerlegt gcc die multiplikation wieder, so dass der maschinencode im prinzip mit dem vorherigen ansatz übereinstimmt.
-
audacia schrieb:
... oder gleich Inline-Assembler verwenden
wie soll das denn gehen?
-
++++++++++++++ schrieb:
audacia schrieb:
... oder gleich Inline-Assembler verwenden
wie soll das denn gehen?
z.b. so (das dürfte allerdings schlechter als der compiler-code sein, insbes. auf willamette/northwood):
inline unsigned short rgba2rgb (unsigned value){ asm( "shrl $3,%[value]\n\t" "shlw $2,%[value]\n\t" "shll $6,%[value]\n\t" "shlw $3,%[value]\n\t" "shrl $11,%[value]" : [value] "+r" ( value ) ); return (value); }
der assembler wird hier zwei warnungen wegen falscher operandengrösse liefern, die man ignorieren kann.
bleibt noch anzumerken, dass man aus der funktion selbst wahrscheinlich nicht viel herausholen kann, allerdings wird sie wohl in der regel nicht mit einzelwerten sondern mit vielen werten eines bildes gefüttert werden, und dann sind andere möglichkeiten zur optimierung gegeben. diese funktion lässt sich ja sehr leicht und bequem vektorisieren.