Float als ProcedureReturn Wert

Anfängerfragen zum Programmieren mit PureBasic.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7031
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Float als ProcedureReturn Wert

Beitrag von STARGÅTE »

Ich glaube dieses Ergebnis ist mehr als Deutlich:

Code: Alles auswählen

B.f = 5
C.f = 0.2

time = ElapsedMilliseconds()
For n = 1 To 10000000
 A.f = 1/B/B/B/B 
Next
time1 = ElapsedMilliseconds()-time
time = ElapsedMilliseconds()
For n = 1 To 10000000
 A.f = 1*C*C*C*C 
Next
time2 = ElapsedMilliseconds()-time
MessageRequester("", Str(time1)+" zu "+Str(time2))
---------------------------

---------------------------
2093 zu 157
---------------------------
OK
---------------------------
Multiplikation ist mehr als 10 mal schneller als Division ...
natürlich ohne Debugger
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
freak
PureBasic Team
Beiträge: 766
Registriert: 29.08.2004 00:20
Wohnort: Stuttgart

Re: Float als ProcedureReturn Wert

Beitrag von freak »

Kaeru Gaman hat geschrieben:
es geht um das Multiplizieren, statt Dividieren !
interessant... worauf basierst du diese Aussage?
FMUL Instruktion: 5 Clock Cycles
FDIV Instruktion: 30 Clock Cycles (single precision)

Genaue Daten variieren je nach Prozessortyp.
Quelle: http://www.intel.com/Assets/PDF/manual/248966.pdf
Benutzeravatar
ts-soft
Beiträge: 22292
Registriert: 08.09.2004 00:57
Computerausstattung: Mainboard: MSI 970A-G43
CPU: AMD FX-6300 Six-Core Processor
GraKa: GeForce GTX 750 Ti, 2 GB
Memory: 16 GB DDR3-1600 - Dual Channel
Wohnort: Berlin

Re: Float als ProcedureReturn Wert

Beitrag von ts-soft »

@freak
danke für den Hinweis, hab mir schon gedacht das malnehmen schneller ist.

<offtopic>Ich frag mich sowieso, warum die Rechengenauigkeit eines "ts-soft" höher als die eines modernen PCs ist :mrgreen: </offtopic>
freak
PureBasic Team
Beiträge: 766
Registriert: 29.08.2004 00:20
Wohnort: Stuttgart

Re: Float als ProcedureReturn Wert

Beitrag von freak »

Es sollte übrigens angemerkt werden das FSIN mit 160-200 Cycles hier dann doch überwiegen wird, egal ob mit Multiplikation oder Division. Wenn man also wirklich derart auf Geschwindigkeit aus ist lohnt es sich einen Lookup-Table in Betracht zu ziehen :wink:
Benutzeravatar
Josh
Beiträge: 1028
Registriert: 04.08.2009 17:24

Re: Float als ProcedureReturn Wert

Beitrag von Josh »

STARGÅTE hat geschrieben:---------------------------

---------------------------
2093 zu 157
---------------------------
OK
---------------------------
ich hab hier nur eine alte kiste stehn, komm aber auf ganz andere ergebnisse

2297 zu 1750

also weit weg von dem 10 fachen
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Re: Float als ProcedureReturn Wert

Beitrag von Kaeru Gaman »

freak hat geschrieben:FMUL Instruktion: 5 Clock Cycles
FDIV Instruktion: 30 Clock Cycles (single precision)

Genaue Daten variieren je nach Prozessortyp.
Quelle: http://www.intel.com/Assets/PDF/manual/248966.pdf
klasse, danke! :D
freak hat geschrieben:Es sollte übrigens angemerkt werden das FSIN mit 160-200 Cycles hier dann doch überwiegen wird, egal ob mit Multiplikation oder Division. Wenn man also wirklich derart auf Geschwindigkeit aus ist lohnt es sich einen Lookup-Table in Betracht zu ziehen :wink:
völlig richtig!
http://www.purebasic.fr/german/viewtopic.php?t=5884
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: Float als ProcedureReturn Wert

Beitrag von Helle »

Eine Lookup-Table ist natürlich speed-mäßig unschlagbar, das Problem dürfte aber immer das Verhältnis Genauigkeit (Auflösung ) - Table-Größe sein. Wenn für eine Sinus-Berechnung der Radiant so im Bereich zwischen -Pi/2 und +Pi/2 liegt, kann auch Folgendes mal getestet werden (eine CPU mit SSE-Unterstützung setze ich mal voraus):

Code: Alles auswählen

;- Sinus-Berechnung (Single-Precision) mit SSE im Bereich -90° bis +90°
;- Verwendet wird die Reihenentwicklung Sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! + (x^9)/9!...
;- die Glieder 2-5 können in einem SSE-Register berechnet werden (1 Float-Wert = 32-Bit, 1 SSE-Register = 128-Bit)
;- "Helle" Klaus Helbing, 22.01.2010, PB v4.40 x86, Windows32

Global Radiant.f = 0.987654            ;hier Testwert, je nach erforderlicher Genauigkeit Werte zwischen -Pi/2 und +Pi/2 sinnvoll; testen!
Global SinPB.f
Global SinSSE.f

Procedure SinSSE(Radiant, SinSSE)      ;oder Parameter (auch Anzahl!) anpassen an eigenes Programm, hier eigentlich doppelt gemoppelt 
  !lea eax,[v_Radiant]                 ;Speicher-Adresse von Radiant 
  !movss xmm0,[eax]                    ;1 = x; 1 = unterstes Double-Word des XMM-Registers, 2 = 2.Double-Word usw.
  !shufps xmm0,xmm0,0                  ;1 - 4 = x
  !movaps xmm2,xmm0                    ;x sichern (1.Glied)
  !movaps xmm1,xmm0
  !mulss xmm1,xmm1                     ;1-1-1-2   Exponenten-Belegung des Zielregisters
  !mulps xmm0,xmm0                     ;2-2-2-2
  !mulps xmm0,xmm1                     ;3-3-3-4
  !movlhps xmm0,xmm1                   ;1-2-3-4
  !mulps xmm0,xmm0                     ;2-4-6-8   
  !mulps xmm0,xmm2                     ;3-5-7-9   also oberstes Double-Word = x^3, unterstes = x^9
  !lea edx,[v_RezFak]                  ;Anfangs-Speicher-Adresse der reziproken Fakultäten  
  !movups xmm1,[edx]
  !mulps xmm0,xmm1                     ;Multiplikation der 4 Power-Werte mit den reziproken Fakultäten
  !movaps xmm1,xmm0
  !shufps xmm1,xmm1,11100101b          ;die einzelnen Glieder in unterstes Double-Word des SSE-Registers schieben
  !addss xmm0,xmm1                     ;und addieren (HADDPS in SSE3)
  !shufps xmm1,xmm1,11100110b  
  !addss xmm0,xmm1   
  !shufps xmm1,xmm1,11100111b 
  !addss xmm0,xmm1   
  !addss xmm0,xmm2                     ;zum Schluss 1.Glied addieren (x)
  !lea eax,[v_SinSSE]
  !movss [eax],xmm0                    ;im untersten DWord von XMM0 steht nun das Ergebnis
EndProcedure
;----------------------------------------------------------

;- Structure für SSE --------------------------------------
Structure Sinus 
  RF1.f : RF2.f : RF3.f : RF4.f 
EndStructure

Define.Sinus RezFak
RezFak\RF1  =  2.755731922398589065e-6 ; 1/9!
RezFak\RF2  = -1.984126984126984127e-4 ;-1/7!
RezFak\RF3  =  8.333333333333333333e-3 ; 1/5!
RezFak\RF4  = -1.666666666666666667e-1 ;-1/3!
;----------------------------------------------------------

;- Test SSE
T1= ElapsedMilliseconds() 
For i = 0 To 19999999 
  SinSSE(Radiant, SinSSE)
Next 
T1 = ElapsedMilliseconds() - T1 

;- Test PB
T2= ElapsedMilliseconds() 
For i = 0 To 19999999 
  SinPB = Sin(Radiant) 
Next 
T2 = ElapsedMilliseconds() - T2 

Sin$ = "Test für Radiant = " + StrF(Radiant) + #CRLF$ + #CRLF$
Sin$ + "SSE : " + StrF(SinSSE) + "  in " + Str(T1) + " ms" + #CRLF$ 
Sin$ + "PB   : " + StrF(SinPB) + "  in " + Str(T2) + " ms" + #CRLF$ 
MessageRequester("Sinus Single-Precision", Sin$)
Soll nur eine Anregung sein :mrgreen: !
Schreibt man ein Programm nur mit Single-Precision und vielen Divisionen (nicht jede Division lässt sich leicht durch eine Multiplikation ersetzen) , kann auch Folgendes ausprobiert werden:

Code: Alles auswählen

B.f = 5
C.f = 0.2

;- althergebracht
time = ElapsedMilliseconds()
For n = 1 To 10000000
  A1.f = 1/B/B/B/B
Next
time1 = ElapsedMilliseconds()-time
time = ElapsedMilliseconds()
For n = 1 To 10000000
  A2.f = 1*C*C*C*C
Next
time2 = ElapsedMilliseconds()-time
;MessageRequester("", Str(time1)+" zu "+Str(time2)+#CRLF$+StrF(A1)+" zu "+StrF(A2))

;- und hier der Test mit zwangsweisem Single-Precision-Test 
Global CWO.w                 ;Originales Control-Word
Global CWN.w                 ;Neues Control-Word

Procedure SetSingleFloat()
  !fstcw [v_CWO]             ;Original Control-Word sichern
  !mov ax,[v_CWO]
  !and ax,1111110011111111b  ;die 2 Precisions-Bits auf Null setzen = Single-Float
  !mov [v_CWN],ax
  !fldcw [v_CWN]             ;das neue Control-Word zurückschreiben
EndProcedure

Procedure RestoreCW()
  !fldcw [v_CWO]             ;das originale Control-Word wieder zurückschreiben
EndProcedure

time = ElapsedMilliseconds()
SetSingleFloat()
For n = 1 To 10000000
  A3.f = 1/B/B/B/B
Next
time3 = ElapsedMilliseconds()-time
time = ElapsedMilliseconds()
For n = 1 To 10000000
  A4.f = 1*C*C*C*C
Next
RestoreCW()
time4 = ElapsedMilliseconds()-time 
MessageRequester("", "Althergebracht: "+Str(time1)+" zu "+Str(time2)+"    " + StrF(A1)+" zu "+StrF(A2)+#CRLF$+"Test:                   "+Str(time3)+" zu "+Str(time4)+"    " + StrF(A3)+" zu "+StrF(A4))
Dies orientiert sich an obigem Beispiel und soll auch nur für Testzwecke (nicht nur Divisionen) sein!
Gruß
Helle
DerMeister
Beiträge: 28
Registriert: 30.12.2009 19:20

Re: Float als ProcedureReturn Wert

Beitrag von DerMeister »

Danke für die zahlreichen Antworten.
Aber ich muss sagen, ich hab in der Hilfe nachgeschaut, ich hab nur übersehen dass man an die Procedure den Typ des Rückgabewerts anhängen muss.
Benutzeravatar
hörmi
Beiträge: 106
Registriert: 10.07.2007 15:20
Kontaktdaten:

Re: Float als ProcedureReturn Wert

Beitrag von hörmi »

habs auch mal ausprobiert
766 zu 47 (ohne debugger)
2579 zu 1843 (mit debugger)
schon interessant mal zu sehen wie sich eine fast gleiche rechenoperation auf die performance auswirken kann
BildBild Bild
Benutzeravatar
Thorium
Beiträge: 1722
Registriert: 12.06.2005 11:15
Wohnort: Germany
Kontaktdaten:

Re: Float als ProcedureReturn Wert

Beitrag von Thorium »

Josh hat geschrieben: ich hab hier nur eine alte kiste stehn, komm aber auf ganz andere ergebnisse

2297 zu 1750

also weit weg von dem 10 fachen
Wie du schon geschrieben hast, liegt es an der alten Kiste. Von CPU-Typ zu CPU-Typ variiert wie viele Zyklen bestimmte Instruktionen benötigen.
Zu mir kommen behinderte Delphine um mit mir zu schwimmen.

Wir fordern mehr Aufmerksamkeit für umfallende Reissäcke! Bild
Antworten