Seite 2 von 2

Re: Assembler IF->THEN

Verfasst: 02.09.2014 20:34
von _sivizius
Optminiert wäre zB sowas hier (auf x86_64):

Code: Alles auswählen

t = ElapsedMilliseconds()
! myLoop:
! xor   rax, rax
! xor   rbx, rbx
! mov   ebx, dword [v_m]
! mov   rcx, rbx
! .cycle:
!   add   rax, 0x02
!   xor   rdx, rdx
!   cmp   eax, ebx
!   jz    @f
!   inc   rdx
!   ja    @f
!   sub   rdx, 0x02
! @@:
! loop  .cycle
MessageRequester("ASM Time",Str(ElapsedMilliseconds()-t), #PB_MessageRequester_Ok)
ich hoffe, dass dieser Code hier auf x86_32 Maschinen läuft:

Code: Alles auswählen

t = ElapsedMilliseconds()
! myLoop:
! xor   eax, eax
! mov   ebx, dword [v_m]
! mov   ecx, ebx
! .cycle:
!   add   eax, 0x02
!   xor   edx, edx
!   cmp   eax, ebx
!   jz    @f
!   inc   edx
!   ja    @f
!   sub   edx, 0x02
! @@:
! loop  .cycle
MessageRequester("ASM Time",Str(ElapsedMilliseconds()-t), #PB_MessageRequester_Ok)
Die Zeiten:
für m = 2147483647 ; maximum für float
ASM Time = 5146ms
PB Time = 15630ms ; etwas das 3fache!
Debugger und allen Schnick-Schnack ausgeschaltet und auf x86_64!
Also mit Optimieren kann man schon viel rausholen. Der Grund, warum dein Code zu langsam war, ist, dass du n, i und j fein in den RAM wider geschrieben hast, aber das brauchst du nicht.

Re: Assembler IF->THEN

Verfasst: 02.09.2014 21:10
von Danilo
Natürlich sollten zum Vergleichen alle Variablen am Ende auch die Gleichen Werte enthalten, zur weiteren Bearbeitung.

Code: Alles auswählen

MessageRequester("Zeit",Str(ElapsedMilliseconds()-t) + " - i=" + i + " j=" + j + " d=" + d + " n=" + n, #PB_MessageRequester_Ok)
@_sivizius: Dein Code macht rein gar nichts, also könntest Du ihn optimieren zu: Ist aber ein Fehler, da die Variablen nach dem Loop nicht gleich sind wie mit der PB-Version.

Re: Assembler IF->THEN

Verfasst: 02.09.2014 21:18
von _sivizius
ist sinnvoll, hat aber auch alles seine richtigkeit, bis auf dass ich i auf quad vergrößern musste, weil der maximal-wert für long mal 2 nun mal nicht in long passt sondern ein quad braucht...
warum soll der code nichts machen? oder hast du den 32bit-Code getestet? Das kann sein, dass der nicht geht, den hab ich nicht testen können...

Re: Assembler IF->THEN

Verfasst: 02.09.2014 21:22
von Danilo
_sivizius hat geschrieben:warum soll der code nichts machen? oder hast du den 32bit-Code getestet? Das kann sein, dass der nicht geht, den hab ich nicht testen können...
Dein Code setzt i, j, n und d nicht auf die richtigen Werte. Er loopt nur sinnlos (ohne irgend eine Veränderung), und das kann man komplett weg optimieren.

Re: Assembler IF->THEN

Verfasst: 02.09.2014 21:27
von _sivizius
Denn ganzen Code kann man wegoptimieren, sowohl den ASM-Code, als auch den PB-Code, solange keine Ausgabe der Berrechnungen gemacht wird. Darum geht es nicht. Es ist mir klar, dass ich anschließend die Variablen in den RAM schreiben muss und den Code daher zu

Code: Alles auswählen

! myLoop:
! xor   rax, rax
! xor   rbx, rbx
! xor   rsi, rsi
! mov   ebx, dword [v_m]
! mov   rcx, rbx
! .cycle:
!   add   rax, 0x02
!   inc   rsi
!   xor   rdx, rdx
!   cmp   eax, ebx
!   jz    @f
!   inc   rdx
!   ja    @f
!   sub   rdx, 0x02
! @@:
! loop  .cycle
! mov   qword [v_i], rax
! mov   dword [v_d], edx
! mov   dword [v_j], ebx
! mov   dword [v_n], esi
abändern muss. Wenn ich aber schon auf Assembler optimiere, warum sollte ich nicht auch die Variablen im Register weiterverwenden. Wenn ich das Ergebnis zB einer Procedure übergebe, ist es sinnlos, den Wert vorher in eine Variable zu schreiben, die nie gelesen wird. Da reicht zB ein einfaches

Code: Alles auswählen

! pushd  ebx
Der Algorithmus dahinter läuft aber einwandfrei, aber das schreiben von Werten in den RAM kostet mich dann aber 50% mehr Zeit, das Verhältnis der Zeiten von FAsm- zu PB-Code beträgt dann nur noch etwa 1/2.

Re: Assembler IF->THEN

Verfasst: 03.09.2014 21:33
von Martin66119
Guten Abend,

ich habe den Codevorschlag nun mal mit meinen "langsames" Netbook getestet.

Hier der Code:

Code: Alles auswählen

Define.l d, n, m, i, j, t

m = 100000000

t = ElapsedMilliseconds()
! myLoop:
! xor   eax, eax
! mov   ebx, dword [v_m]
! mov   ecx, ebx
! .cycle:
!   add   eax, 0x02
!   xor   edx, edx
!   cmp   eax, ebx
!   jz    @f
!   inc   edx
!   ja    @f
!   sub   edx, 0x02
! @@:
! loop  .cycle
MessageRequester("ASM Time",Str(ElapsedMilliseconds()-t), #PB_MessageRequester_Ok)

t = ElapsedMilliseconds()

For n = 1 To m
  
  i = n*2
  j = m
  
  If i = j
    d = 0
  ElseIf i > j
    d = 1
  Else
    d = -1
  EndIf
  
Next
MessageRequester("Information", Str(ElapsedMilliseconds()-t), #PB_MessageRequester_Ok)

ASM = 1360ms
PB = 1570ms

Re: Assembler IF->THEN

Verfasst: 03.09.2014 21:49
von STARGÅTE
Worauf willst du hinaus Martin66119?
Zum Einen erkenne ich keinen Zusammenhang mehr zwischen den beiden Codes.
Zum andere erkenne ich immer noch nicht den Sinn dieses Test-Codes.

Kurz und knapp:
Selbstgeschriebener ASM-Code ist immer schneller als generierter ASM-Code.
Das trifft natürlich nur zu, wenn man mit der Architektur des Prozessors vertraut ist und die Laufzeiten einiger Befehle kennt.

Wenn du die Laufzeit eines Problems verbessern willst, dann fängst du zuerst mal damit an, dir zu überlegen, ob du nicht einfach ein besseres Verfahren/Algorithmus anwenden kannst. Danach kannst du deinen Code in PB verbessern, und erst dann kannst du ggf. die letzte Optimierung in ASM vornehmen.

Re: Assembler IF->THEN

Verfasst: 04.09.2014 05:40
von Danilo
Martin66119 hat geschrieben:ich habe den Codevorschlag nun mal mit meinen "langsames" Netbook getestet.
[...]
ASM = 1360ms
PB = 1570ms
Bei mir ist die Version von _sivizius auf einem 1 Jahr alten Mac Mini Server (2,6 GHz Intel Core i7) mit Mac OS X
etwa 1/3 schneller als PB (197ms : 303ms).
Auf einem etwa 5 Jahre alten Intel Core2 Quad Q9450 @ 2,66 GHz mit Windows 8.1 ist seine Version
allerdings langsamer als PB (630ms : 540ms), was an der Verwendung des langsamen 'loop' Befehls liegen könnte.

Meine eigene Anpassung von Makkes Code mit ASM-Registern ist auf beiden Computern etwa 3mal so schnell wie PB:
Alter Core2: 169ms : 554ms
Mac Core i7: 90ms : 303ms
Der größte Unterschied ist, dass alle Variablen in ASM-Registern gehalten werden. Kann man natürlich weiter optimieren.

Gegenüber der originalen Version von Makke wurde der Zugriff auf Speichervariablen innerhalb der Schleife weg-optimiert.
Das i = n*2 (MOV eax, n : MOV ebx, 2 : MUL ebx : MOV i, eax) wurde durch ein simples Links-Shiften mit 1 ersetzt.
Der Sprung 'JE l_equal' wurde entfernt, da die erste If-Bedingung einfach durchfallen kann, ohne springen zu müssen.

Code: Alles auswählen

Define.l d, n, m, i, j, t

m = 100000000

t = ElapsedMilliseconds()

EnableASM

    MOV edx, m       ; edx = m
    MOV ecx, 1       ; ecx (n) = 1
  for_loop_1:
    MOV eax, ecx     ; eax (i) = ecx (n)
    SHL eax, 1       ; eax * 2
    
    MOV ebx, edx     ; ebx (j) = edx (m) - kann man verschieben, direkt vor das label 'for_loop_1'
    
    CMP eax, ebx     ; compare eax (i) with ebx (j)
    JL  l_less       ; Jump if Less
    JG  l_greater    ; Jump if Greater
  equal:             ; Equal
    MOV esi, 0       ; esi (d) = 0
    JMP l_finish
  greater:
    MOV esi, 1       ; esi (d) = 1
    JMP l_finish
  less:
    MOV esi, -1      ; esi (d) = -1
  finish:
    INC ecx          ; ecx (n) + 1                   \
    CMP ecx, edx     ; compare ecx (n) with edx (m)   |= Next
    JNA l_for_loop_1 ; Jump if Not Above             /
    
    MOV i, eax       ; i = eax
    MOV j, ebx       ; j = ebx
    MOV n, ecx       ; n = ecx
    MOV d, esi       ; d = esi

DisableASM

MessageRequester("Zeit",Str(ElapsedMilliseconds()-t) + " - i=" + i + " j=" + j + " d=" + d + " n=" + n, #PB_MessageRequester_Ok)

t = ElapsedMilliseconds()

For n = 1 To m
  
  i = n*2
  j = m
  
  If i = j
    d = 0
  ElseIf i > j
    d = 1
  Else
    d = -1
  EndIf
  
Next

MessageRequester("Zeit",Str(ElapsedMilliseconds()-t) + " - i=" + i + " j=" + j + " d=" + d + " n=" + n, #PB_MessageRequester_Ok)