Put-Pixel-Routine beschleunigen



  • Ich benutze unter C/C++ die Fkt.

    void put_pixel(int x, int y, int color, unsigned char *adr)
    {
    (adr+x+(y320))=color;
    }

    zum Darstellen von Punkten im Videospeicher oder in einem Vorpuffer.
    Wäre eine Assembler-Funktion schneller ?
    Wie genau müßte die aussehen ?
    Vielen Dank im ******* !



  • eine Assembler Version ist wahrscheinlich nicht schneller, da dein Algortihmus nicht sehr kompliziert ist. Schau dir doch mal dei Assembler Ausgabe deines Programmes an.



  • Also ich denke schon, dass man da mit assembler ein paar Ticks rausholen koennte:
    Statt y320
    koennte man zb. auch
    (y shl 8)+(y shl 6) (y*256)+(y*64) machen.
    Ist auf jeden Fall schneller, als eine Multiplikation.
    13 clocks fuer eine unsigned 16Bit-Multiplikation mit Registern.
    2
    2 clocks fuer shl + 1 clock fuer add => 5 clocks 😉

    [ Dieser Beitrag wurde am 05.06.2002 um 18:37 Uhr von Nobuo T editiert. ]



  • shiften kann er aber auch in c/c++ 😉 :p



  • Sicher. Nur ist dabei nicht sichergestellt, dass Register geshifted werden und nicht gleich die Variablen im Speicher/Stack. Das dauert bis zu 3clocks laenger. Falls ich mich in diesem Punkt irren sollte bitte ich um Aufklaerung 😃



  • Genau in diese Richtung zielte meine Frage, vielen Dank Nubuo T.
    Könntest du mir den genauen nötigen Code dafür schreiben, wie müsste das in der C-Fkt aussehen ?



  • hm. also die c++-Variante lass ich besser erstmal sein. Dabei ist mir als c++-laie noch einiges unklar. zB. welches Betriebssystem wird benutzt? Real oder Protected mode? Welche Form hat dieser *adr-pointer genau, etc. Also beschraenke ich mich hier mal auf die reine Real mode Assembler version.

    ;bl=color
    mov di,[x]
    mov ax,[y]
    shl ax,06h ;(*64)
    add di,ax
    shl ax,02h ;(*64*4=*256)
    add di,ax
    mov ax,0A000h ;segment der VGA
    mov ds,ax
    mov [ds:di],bl
    das sind insgesamt (auf 486+CPU) 13 clocks...
    So viel, wie sonst allein die Multiplikation verbraucht haette... 😮



  • Original erstellt von Nobuo T:
    **
    13 clocks fuer eine unsigned 16Bit-Multiplikation mit Registern.
    **

    ich hab hier n buch wo für so eine multiplikation 11 clocks drinn steht... und das auf pentium bezogen... ich nehmen an ein athlon oder P3/P4 dürfte da noch massiv schneller sein



  • Mag sein. Fakt ist aber, dass shift dann immernoch schneller sein wird 😉 Diese Werte (fuer 486CPU) sollen eigentlich nur zum Vergleich dienen.



  • @Nobuo T
    Wenn man weis wie der compiler optimiert kann man mit "const" angaben die variablen in register "lenken". Und ohnehin sollte der compiler alles was reinpasst in registern aufbewahren.

    @Steffen Vogel:
    So kleine funktionen durch asm zu optimieren halte ich für falsch. Wenn das prog. schneller laufen soll, dann solltest Du Dein konzept ändern.



  • Was bitte ist an Optimierungen - egal welcher Art - falsch?? Gerade eine so elementare Funktion wie die putPixel sollte möglichst schnell sein, besonders dann, wenn sie oft verwendet wird.
    Klar, ist ein gutes Konzept wichtiger, aber gerade da soviele auf Feintuning verzichten, sind die Spiele von heute viel langsamer als sie sein müßten.

    Übrigens gabs hier schonmal eine vergleichbare Diskussion um putPixel.



  • Original erstellt von <Steffen Vogel>:
    Könntest du mir den genauen nötigen Code dafür schreiben, wie müsste das in der C-Fkt aussehen ?

    void fast_put_pixel(int x, int y, int color, unsigned char *adr)
    {
      *(adr+x+((y<<8)+(y<<6)))=color;
    }
    

    so würde das in C aussehen.
    Aber dazu noch was interessantes, dass macht der gcc 3.0.4 aus dem put_pixel

    put_pixel:
        pushl   %ebp
        movl    %esp, %ebp
        movl    12(%ebp), %eax
        leal    (%eax,%eax,4), %eax
        sall    $6, %eax
        addl    20(%ebp), %eax
        movl    16(%ebp), %ecx
        movl    8(%ebp), %edx
        movb    %cl, (%edx,%eax)
        popl    %ebp
        ret
    

    und das aus fast_put_pixel (!)

    fast_put_pixel:
        pushl   %ebp
        movl    %esp, %ebp
        movl    12(%ebp), %edx
        movl    %edx, %eax
        movl    20(%ebp), %ecx
        sall    $6, %eax
        sall    $8, %edx
        addl    8(%ebp), %ecx
        addl    %eax, %edx
        movl    16(%ebp), %eax
        movb    %al, (%edx,%ecx)
        popl    %ebp
        ret
    

    (noch mein Senf zur Welt:

    In der Regel optimieren Compiler schon verdammt gut, nur bei wirklich harten Dingen sollte man Assembler einsetzen, aber da fällt mir spontan nichts ein ;))



  • hi,

    wie ruft man die funktion:
    void fast_put_pixel(int x, int y, int color, unsigned char *adr)
    {
    *(adr+x+((y<<8)+(y<<6)))=color;
    }
    dann auf?
    die ersten drei parameter sind klar, aber was soll ich da für nen pointer übergeben?



  • Hi!

    Ich bin zwar schon ne ganze Weile weg vom Assembler, aber damals habe ich das etwas anders gemacht. Ich habe nämlich ein Zeilen-Array reserviert, in dem die Start-Adresse jeder Grafikzeile stand.
    Also war z.B.:

    grRowArr[0] == 0
    grRowArr[1] == 320
    grRowArr[0] == 640

    usw. Das geht dann ein bissl schneller als Multiplikation und auch als Shifts. Wollt ich nur mal so ansprechen, weil diese Möglichkeit hier noch niemand erwähnt hat 🙂 ... und der Gra-Speed hat da rult 😃

    @BlockBuster: adr dürfte hier 0xA000h bzw Start des GrafikScreens sein.



  • Wohl kaum schneller als Shifts. Ein shl eines 16-Bit-Registers brauchte schon auf dem 8086 nur 2 Takte, wobei ein add eines 16-Bit-Registers mit einer Speicherstelle darauf mehr als 9 Takte brauchte. Auf nem Pentium war das also höchstens gleich schnell (je 1 Takt 😉 ), wobei allerdings nach deiner Methode 200 Bytes fürs Array verbraten werden. 🙄

    [ Dieser Beitrag wurde am 06.06.2002 um 18:47 Uhr von Cocaine editiert. ]



  • Bleiben wir doch mal realistisch: 8086 😕
    Fangen wir mal mit 486er an. Ich poste mal eben PseudoCode und zwar für PM. RM kann ja umsetzen wer will:

    ; =============================
    @Putpixel:
    ; =============================
    ; IN: EAX= X-Position
    ;     ECX= Y-Position
    ;     BL = Color
    
       add   eax, [ecx*2+arrOfRows]; Oder vorher shl ecx, 2 ... ich kenn jetzt die clocks net
       add   eax, [videoAdress]    ; On Screen
       mov   [eax], bl             ; set color
       ret
    

    Hier gehe ich natürlich von einem FlatSegment und CS=DS aus. Ich spare hier übrigens nicht nur ein Shift sondern 2. Und zusätzlich noch Additionen. Ausserdem sche|ss ich erlich gesagt prinzipiell auf extra reservierten Speicher wenn man Speed dafür rausholen kann 🙂 .

    Falls es trotzdem Blödsinn ist, dann sorry und belehr mich - bin schon zu lang raus 😃 . Aber ich errinnere mich gut, daß ich das Tage lang abgewogen und mich dann dafür entschieden habe 🕶 .



  • @Nubuo T: Hast du eine Liste der Taktzyklen der einzelnen Befehle ?



  • @blockbuster: Die Funktion stellt einen Punkt an der Stelle eines Speichers dar, dessen Adresse du ihr mit adr übergibst. Somit kannst du entweder direkt in den Videospeicher schreiben oder in einen Puffer, den man dann später ins Videoram kopiert. Sowas macht zum Beispiel Sinn, wenn man mehrere Punkte zeichnen lassen will, aber das Bild auf einmal erscheinen soll und nicht punktweise.
    Ich hoffe, du verstehst jetzt wie du diese Fkt benutzt.



  • Ich habe eine Befehlsreferenz, bei der uA. auch die benoetigte Anzahl von Tacktzyklen pro Befehl drinstehen. Leider ist das ein Buch, also wirds schwer mit dem Posten o.ae.
    Kauf dir den TASM von Borland. Da ist diese Befehlsreferenz und noch ein Haufen anderes Zeug mit bei 😉



  • @Steffen Vogel achso... naja und welche adresse muss ich jetzt übergeben wenn ich was direkt auf den bildschirm malen will?


Anmelden zum Antworten