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


  • Mod

    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? 😕


  • Mod

    ++++++++++++++ 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.


Anmelden zum Antworten