Cache-Tabellen für statische Funktionen (Sin - Table, ...)

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Cache-Tabellen für statische Funktionen (Sin - Table, ...)

Beitrag von cxAlex »

Servus.

Ein kleines Code - Schnippsel um automatisch Cache - Tabellen für statische Funktionen anzulegen, also Funktionen die auf denselben Übergabe - Parameter immer denselben Wert zurückgeben. Typisch für Sinus/Cosinus usw. Tabellen um Spiele zu beschleunigen damit die Funktion nicht jedesmal berechnet werden muss, oder auch für eigene Funktionen. Der Code erstellt ein Macro um den Funktionsaufruf der jeweiligen Funktion automatisch so umzubiegen das die Cache - Table benutzt wird, also sehr leicht in bestehenden Source zu integrieren. Des weiteren gibt es Macros um die Umleitung zu aktivieren und deaktivieren.

z.B:

Code: Alles auswählen

cachetable_create(sin, -#PI, #PI, 10000)
Erzeugt automatisch eine Cache-Tabelle für die Sinus - Funktion mit Granularität 10.000 für den Bereich -#PI bis +#PI. Alle Aufrufe der Sin() Funktion benutzen nun automatisch diese Tabelle.

Per cachetable_disable(sin) kann diese Umleitung ausgeschalten werden, es wird wieder die orginale PB - Sin Funktion verwendet. Analog per cachetable_enable(sin) wieder aktiviert.

Der Tabellen - Generator erhöht die Anzahl von Slices automatisch auf eine 2. Potenz um die Bereichsprüfung effizient per Bitoperation durchzuführen. Des weiteren erhöht er die die Anzahl der Slices automatisch um im gewählten Bereich das beste Sigma zu finden (können auch 4,5 2. Potenzen sein oder keine, je nach Funktion/Bereich). cachetable_sigma(function) gibt automatisch das aktuelle sigma für die jeweilige Funktion an. (Die durchschnittliche Abweichung zwischen der echten Funktion und den zwischengespeicherten Werten)

Code: Alles auswählen

; EnableExplicit

CompilerIf #PB_Compiler_Version < 510
  ; Wir benötigen "UndefineMacro"
  CompilerError "Minimum supported Version: PB 5.10"
CompilerEndIf

CompilerIf Not Defined(__macrobuilder, #PB_Constant)
  #__macrobuilder = #True
  Macro _Macro1
    M
  EndMacro
  
  Macro _Macro2
    acro
  EndMacro
  
  Macro _Macro
    : _Macro1#_Macro2
  EndMacro
  
  Macro _EndOfMacro1
    EndM
  EndMacro
  
  Macro _EndOfMacro
    : _EndOfMacro1#_Macro2
  EndMacro
CompilerEndIf

; Logarithmus zur basis 2 (binärlogrithmus)
CompilerIf Not Defined(_1dLN_2, #PB_Constant)
  #_1dLN_2 = 1.4426950408889634074
  Macro ld(x)
    (Log(x)*#_1dLN_2)
  EndMacro
CompilerEndIf

; Schaltet Cache - Table ein
Macro cachetable_enable(func)
  CompilerIf Defined(__cachetable_#func#, #PB_Array)
    _Macro func#(_t)
    __cachetable_#func#(Int(((_t)-__table__start_#func)*__table__part2_#func)&__table_stepsreal_#func)_EndOfMacro
  :CompilerEndIf
EndMacro

; Schaltet Cache - Table ab
Macro cachetable_disable(func)
  CompilerIf Defined(__cachetable_#func#, #PB_Array)
    UndefineMacro func
  CompilerEndIf
EndMacro

Macro cachetable_sigma(func)
  __table_sigma_#func
EndMacro

Macro cachetable_steps(func)
  __table_stepsreal_#func
EndMacro

; Erstell Cache - Table
Macro cachetable_create(func, area_start, area_stop, steps = 4095, type = f, maxbitadd = 3)
  CompilerIf Not Defined(__cachetable_#func#, #PB_Array)
    Define.i __table_bits_#func = Int(Round(ld(steps), #PB_Round_Up))
    Define.i __table_bitsused_#func = __table_bits_#func-1
    Define.type __table_sigmaold_#func
    Define.i __table_lastcalc_#func = #False
    Global.type __table_sigma_#func
    If __table_bitsused_#func>31
      __table_bitsused_#func = 31
    EndIf
    
    Repeat
      __table_sigmaold_#func = __table_sigma_#func
      If __table_lastcalc_#func
        __table_bitsused_#func - 1
      Else
        __table_bitsused_#func + 1
      EndIf
      ; Werte Berechnen...
      Global.i __table_stepsreal_#func = Int(Pow(2, __table_bitsused_#func))-1
      Define.type __table__stop_#func = Abs((area_stop)-(area_start))
      Define.type __table__part_#func = __table__stop_#func/__table_stepsreal_#func
      Global.type __table__part2_#func = __table_stepsreal_#func/__table__stop_#func
      Global.type __table__start_#func = (area_start)
      Define.type __table__cur_#func = (area_start)
      Define.i __table__cnt_#func = 0
      Global Dim __cachetable_#func#.type(0)
      ReDim __cachetable_#func#.type(__table_stepsreal_#func)
    
      ; Cached Werte
      While __table__cnt_#func < __table_stepsreal_#func
        __cachetable_#func#(__table__cnt_#func+1) = func#(__table__cur_#func)
        __table__cur_#func + __table__part_#func
        __table__cnt_#func + 1
      Wend 
      
      ; Sigma Minimieren
      If Not __table_lastcalc_#func
        __table__cnt_#func = 0
        __table_sigma_#func = 0
        While __table__cnt_#func < (__table_stepsreal_#func*2)
          Define.type __table__tval_#func = (__table__cnt_#func/(__table_stepsreal_#func*2))*__table__stop_#func + __table__start_#func
          __table_sigma_#func + (func#(__table__tval_#func) - __cachetable_#func#(Int(((__table__tval_#func)-__table__start_#func)*__table__part2_#func)&__table_stepsreal_#func))
          __table__cnt_#func + 1
        Wend
        __table_sigma_#func = Abs(__table_sigma_#func/(__table_stepsreal_#func*2))
      Else
        Break
      EndIf
       
      ; Neues Sigma schlechter als letztes...
      If (__table_sigmaold_#func < __table_sigma_#func) And (__table_bitsused_#func <> __table_bits_#func)
        __table_lastcalc_#func = #True
        __table_sigma_#func = __table_sigmaold_#func
      ElseIf (__table_bitsused_#func = 32) Or (__table_bitsused_#func = (__table_bits_#func+maxbitadd)) ; Mehr geht nicht!
        Break
      EndIf
    ForEver
    ; cachetable activate
    cachetable_enable(func)
  CompilerEndIf
EndMacro

Macro cachetable_default(steps = 4095)
  cachetable_create(sin, -#PI, #PI, steps, f)
  cachetable_create(cos, -#PI, #PI, steps, f)
  cachetable_create(tan, -#PI, #PI, steps, f)
  cachetable_create(asin, -1, 1, steps, f)
  cachetable_create(acos, -1, 1, steps, f)
  cachetable_create(atan, -1, 1, steps, f)
EndMacro
Zuletzt geändert von cxAlex am 10.03.2013 02:53, insgesamt 4-mal geändert.
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7032
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Cache-Tabellen für statische Funktionen (Sin - Table, ..

Beitrag von STARGÅTE »

cxAlex hat geschrieben:Es muss nun nur noch darauf geachtet werden das der übergebene Parameter innerhalb des vorberechneten Bereichs liegt.
Hm, jo, sodass im Endeffekt, jedesmal Mod(Wert, Bereich) aufgerufen werden müsste, was den Vorteil wieder kaputt macht.

Hier mal meine Geschwindigkeitsmessung:

Code: Alles auswählen

Prototype.i MeasureFunction()
Global QueryPerformanceFrequency.q : QueryPerformanceFrequency_(@QueryPerformanceFrequency)

Procedure.f Measure(Function.MeasureFunction)
	Protected Time1.q, Time2.q
	QueryPerformanceCounter_(@Time1)
	Function()
	QueryPerformanceCounter_(@Time2)
	ProcedureReturn 1000.0*(Time2-Time1)/QueryPerformanceFrequency
EndProcedure


Procedure Offset()
	Protected x.f
	For n = 1 To 100000
		x.f = Random(1000)/10
	Next
EndProcedure

Procedure Messung1()
	Protected x.f
	For n = 1 To 100000
		x.f = Random(1000)/10
		Sin(x)
	Next
EndProcedure

cachetable_create(sin, -#PI, #PI, 10000, f)

Procedure Messung2()
	Protected x.f
	For n = 1 To 100000
		x.f = Random(1000)/10
		Sin(Mod(x, #PI))
	Next
EndProcedure


Define Result1.f = Measure(@Messung1())-Measure(@Offset())
Define Result2.f = Measure(@Messung2())-Measure(@Offset())

Define Message.s = "Sin: "+StrF(Result1,2)+"ms"
Message + #LF$ + "SinTabelle mit Mod: "+StrF(Result2,2)+"ms"

MessageRequester("Vergleich", Message)
---------------------------
Vergleich
---------------------------
Sin: 3.78ms
SinTabelle mit Mod: 6.68ms
---------------------------
OK
---------------------------
Und selbst wenn ich kein Mod benutze, ist der Vorteil nur 50%:
---------------------------
Vergleich
---------------------------
Sin: 4.18ms
SinTabelle ohne Mod: 2.02ms
---------------------------
OK
---------------------------
Vermutlich, weil das dividieren in

Code: Alles auswählen

Int(((_t)-__table__start_#func)/__table__part_#func)
einfach noch zu langsam ist,
wenn man hier Multipliziert:

Code: Alles auswählen

Int(((_t)-__table__start_#func)*__table__part2_#func)
mit

Code: Alles auswählen

Global.type __table__part2_#func = (steps)/__table__stop_#func
wirds zumindest noch mal 25% schneller:
---------------------------
Vergleich
---------------------------
Sin: 4.13ms
neue SinTabelle: 1.43ms
---------------------------
OK
---------------------------
Aber wirklich "umhauen" tut mich der Vorteil nun nicht, oder hab ich n Denkfehler in der Zeitmessung?
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
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Re: Cache-Tabellen für statische Funktionen (Sin - Table, ..

Beitrag von cxAlex »

Das mit der Multiplikation ist gut, hab ich oben eingebaut, danke.

Naja, das mit dem Modulo stimmt, darum ist es ja auch nicht drin. Nur muss das ja nicht sein, es gibt ja durchaus Situationen wo man sich sicher sein kann das der Wert innerhalb der Grenzen ist, bzw. wenn man die Radian() Funktion nutzt erhält man so oder so nen gültigen Bereich.

Und naja, wenn (vereinfacht) die Version mit der Tabelle in deiner Version 1. ms braucht und die ohne 4. ms dann ist die Tabellen - Version 4x schneller oder der braucht "nur" 75% weniger Zeit? Also je nachdem wie oft man die Funktion nun aufruft (und andere, man kann ja für jede Funktion die man benötigt eine Tabelle anlegen) ist das durchaus ein Gewinn würd ich mal sagen ...

Nebenbei irgendwas stimmt nicht mit deiner Zeitmessung, ich erhalte teilweise negative Ergebnisse für die Tabellen - Version ?

Die Offset - Procedure soll doch nur die CPU "aufwärmen", bzw. das CPU - Managemant dazu veranlassen die CPU hochzutakten? Bei Geschwindigkeitsmessungen sollte man die dynamische CPU - Taktung so oder so abstellen und fix aufs Limit setzten, den auch mit diesem Trick verbrät der Takt - Controller sonst etwas Leistung.

Der Code hier verwendet zwar nur das ungenaue ElapsedMilliseconds() da ich mein HighResTimer - Include da jetzt nicht einbauen wollte, aber bei genug Wiederholungen kannst du die 16 - ms Timeslices auch vernachlässigen:

Code: Alles auswählen

Define t, t1, t2, i
#n = 10000000

t = ElapsedMilliseconds()
For i = 1 To #n
  Sin(0)
Next
t1 = ElapsedMilliseconds()-t

cachetable_create(Sin, -#PI, #PI, 10000)

t = ElapsedMilliseconds()
For i = 1 To #n
  Sin(0)
Next
t2 = ElapsedMilliseconds()-t

MessageRequester("", "Sin: " + t1 + Chr(13) + "Cached Sin: " + t2)
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8812
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Cache-Tabellen für statische Funktionen (Sin - Table, ..

Beitrag von NicTheQuick »

Wenn man nur Zweierpotenzen als Granularität zulässt, könnte man das Mod einsparen und mit schnellen Bit-Operationen arbeiten.

Code: Alles auswählen

EnableExplicit

Define.i granBits = 12
Define.d low = -#PI, high = #PI


Define.d diff = high - low
Define.i maxIndex = (1 << granBits) - 1
Define.d tmpMult1 = diff / (maxIndex + 1)
Define.d tmpMult2 = (maxIndex + 1) / diff

Dim table.d(maxIndex)

Define i.i = 0
While i <= maxIndex
	table(i) = Cos(low + i * tmpMult1)
	i + 1
Wend

Macro FastCos(x)
	(table(Int((x - low) * tmpMult2) & maxIndex))
EndMacro

;Zugriff

Debug "Grenzen"
Debug Cos(#PI) - FastCos(#PI)
Debug Cos(-#PI) - FastCos(-#PI)
Debug "Zufallswerte"

#n = 1000

Define j.i, z.d, sigma.d
For j = 1 To #n
	z = Random(-1000000, 1000000) / 1000000
	sigma + Cos(z) - FastCos(z)
Next

Debug sigma / #n
Leider ist die Abweichung sogar bei 24 Bit nicht besonders toll. Oder ich hab auch irgendwas falsch gemacht.

Achja, die Geschwindigkeitstest überlasse ich euch.
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Re: Cache-Tabellen für statische Funktionen (Sin - Table, ..

Beitrag von cxAlex »

Ich hab den Code oben aktualisiert, der Es gibt nun einen optionalen Parameter der den Bereichscheck aktiviert. Wenn man das Ergebnis des Int() % Steps nimmt erhält man dasselbe Ergebnis als wenn man bereits die Floating - Point Werte abschneidet, aber bedeutend schneller da einfacher zu berechnen. Mit aktivierten Bereichscheck ist die Tabelle bei mir immer noch 25% schneller als das native Modulo.

@NicTheQuick, seh ich mir mal an, vielleicht kann ich das einbauen.
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7032
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Cache-Tabellen für statische Funktionen (Sin - Table, ..

Beitrag von STARGÅTE »

cxAlex hat geschrieben:Nebenbei irgendwas stimmt nicht mit deiner Zeitmessung, ich erhalte teilweise negative Ergebnisse für die Tabellen - Version ?

Die Offset - Procedure soll doch nur die CPU "aufwärmen", bzw. das CPU - Managemant dazu veranlassen die CPU hochzutakten?
Nein, die Offset-Prozedur macht das, das in der eigentlichen Messung wieder raus soll, nämlich das Berechnen der Zufallszahlen und die Schleife, sodass am Ende wirklich nur die Sin()-Zeit berechet wird.
Wenn du negative Werte bekommen hast, waren die Prozeduren nicht mehr identisch.

Deine Zeitmessung ist im den Sinne "Nutzlos", weil du nur Sin(0) testest, und Sin(0) ist ja sehr viel schneller als Sin(3).
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
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Re: Cache-Tabellen für statische Funktionen (Sin - Table, ..

Beitrag von cxAlex »

Stimmt, deine Zeitmessung sollte in der Hinsicht besser sein.

Aber die beiden Schleifen (Offet und die Testschleifen) werden nacheinander abgearbeitet. Was wenn wärend der Offset - Schleife die CPU stark beansprucht wird, irgend eine Aktualisierung z.B. und wärend der eigentlichen Testschleife nicht? Dann kann es vorkommen das z.B. die Testschleife 5 ms benötigt und die Offset - Schleife 20, und wir haben ein negatives Ergebnis? So kann man das nicht messen ...

So, ich hab den Code oben akutalisiert. Er verwendet nun automatisch die nächstgrößere 2. Potenz um wie von NicTheQuick gezeigt die Bereichsprüfung per Bit - And vorzunehmen, ich kann fast keinen Geschwindigkeitsverlust feststellen. Des weiteren versucht der Code nun auch das Sigma zu minimieren, also schlägt er (wenn nötig noch ein paar Potenzen oben drauf. Irgendwann wird die Ungenauigkeit durch's teilen größer als die größere Step - Anzahl auszugleichen vermag, also 2^19 Steps werden ungenauer als 2^18 (Sinus und Kosinus sind anscheinend ideal bei 18)) d.H. beim Erstellen muss die Tabelle mindestens 2. mal berechnet werden. Aber das wird so und so nur 1. mal beim Programmstart erledigt und spielt bei der Laufzeit keine Rolle.

Gruß, Alex

//Edit:

Auch sollten beide Proceduren Messung1() und Messung2() DIESELBEN Zufallszahlen benutzen, sonst verfälscht man das Ergebnis!
So sollte es klappen, die Random() - Generation ist ausgelagert und alle haben dieselben Zahlen:

Code: Alles auswählen

#n = 10000000

Global Dim RandomNumbers.d(#n)

Procedure GenerateRandom()
   Protected n
   For n = 1 To #n
      RandomNumbers(n) = Random(1000)/10
   Next
EndProcedure

Procedure Messung1()
  Protected n
   For n = 1 To #n
      Sin(RandomNumbers(n))
   Next
EndProcedure

cachetable_create(sin, -#PI, #PI, 10000, f)

Procedure Messung2()
  Protected n
   For n = 1 To #n
      Sin(RandomNumbers(n))
   Next
EndProcedure


GenerateRandom()
Define Result1.f = Measure(@Messung1())
Define Result2.f = Measure(@Messung2())

Define Message.s = "Sin: "+StrF(Result1,2)+"ms"
Message + #LF$ + "SinTabelle mit Mod: "+StrF(Result2,2)+"ms"
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Re: Cache-Tabellen für statische Funktionen (Sin - Table, ..

Beitrag von cxAlex »

Kleines Update, die Sigma - Berechnung beachtet nun bei allen Funktionen den gültigen Wertebereich.

Zur Sinnhaftigkeit, ich hab einfach mal diesen Code hier von Stargate (http://purebasic.fr/german/viewtopic.ph ... 30#p308764) verwendet. Damit's die CPU/GPU auslastet die Konstanten am Start wie folgt gesetz:

Code: Alles auswählen

#ImageSpaceAngle = 1 ; Größe eines Bilders in Grad
#StepSpaceAngle  = 1 ; Abstand der Bilder in Grad (Teiler von 140)
#Radius = 18 ; Kugelradius
Und ein kleiner Fps - Anzeiger in der Event - Schleife, ganz rudimentär:

Code: Alles auswählen

Define fps = 0
Define t = ElapsedMilliseconds()
Repeat
  fps + 1
  If ElapsedMilliseconds()-t >= 1000
    SetWindowTitle(#Window, "fps: " + Str(fps))
    fps = 0
    t = ElapsedMilliseconds()
  EndIf
   Repeat

; ...
Bringt meinen Rechner ganz schön ins Schwitzen, mehr als 17,18 fps kommen nicht zustande.
Einfach am Anfang des Codes folgende einfügen:

Code: Alles auswählen

XIncludeFile "cachetable.pbi"

cachetable_default()
Bringt 24,25 fps. Also doch 35-45% Zuwachs.
Erkauft wird das Ganze durch 2 MByte mehr benötigtem Arbeitsspeicher, ~9MByte zu ~11MByte

Gruß, Alex
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7032
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Cache-Tabellen für statische Funktionen (Sin - Table, ..

Beitrag von STARGÅTE »

:allright:
Oke, ich bin überzeugt, in "dieser" Situation ist es wirklich ein sichtbarer Vorteil:
Ich habe hier 37 zu 45 Frames.

Allerdings möchte ich noch auf eine kleine Gefahr bei Tan() hinweisen: Tan() hat im gegensatz zu Sin/Cos Definitionslücken, was bedeutet, das die Tabelle extrem ungenau wird im Bereich um PI/2.

Code: Alles auswählen

Debug Tan(Radian(87))
cachetable_default()
Debug Tan(Radian(87))
19.081136687728161
18.48980712890625
Hinzukommt bei ATan(), dass der Bereich [-1,1] bei weitem nicht ausreicht (wäre ja nur -45° bis 45°).
Dort, solltest du (im default) den Bereich auf ca. [-200,200] ausweiten, damit man einen Winkel von rund 89.7 rund 90° bekommen kann.
Problem wäre dann aber, dass auch mehr steps nötig sind, damit man um ATan(0.0) auch die kleinen Winkel gut aufgelöst zurück bekommt.

Erinnert mich irgendwie an die UNI, wo man in der Signalverarbeitung eine "günstige" Abtastrate versucht zu finden^^
Aber ich denke du kannst locker noch höher gehen, mit den steps, das gibt ja der RAM heutzutage her.
Nur die Vorberechnung dauert dann doch schon etwas länger..
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
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8812
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Re: Cache-Tabellen für statische Funktionen (Sin - Table, ..

Beitrag von NicTheQuick »

Für ATan und Tan helfen Cachetabellen in der Tat nicht mehr viel, da z.B. ATan auch einen Definitionsbereich von -Unendlich bis Unendlich hat. Da hilft dann eher eine Taylorannäherung:

Code: Alles auswählen

#PI_2 = #PI * 0.5

Procedure myAtan(x.f)
	If (x > 1.0)
		ProcedureReturn #PI_2 - (x / (x * x + 0.28))
	ElseIf (x < -1.0)
		ProcedureReturn -#PI_2 - (x / (x * x + 0.28))
	EndIf
	ProcedureReturn x / (1 + 0.28 * x * x)
EndProcedure

#n = 100000000

Dim randomNumbers.f(#n)
Define i.i, time1.i, time2.i, result.f, diff.d

For i = 1 To #n
	randomNumbers(i) = Random(-1000000, 1000000) / 10000
Next

time1 = ElapsedMilliseconds()
For i = 1 To #n
	result = myAtan(randomNumbers(i))
Next
time1 = ElapsedMilliseconds() - time1

time2 = ElapsedMilliseconds()
For i = 1 To #n
	result = ATan(randomNumbers(i))
Next
time2 = ElapsedMilliseconds() - time2

For i = 1 To #n
	diff + ATan(randomNumbers(i)) - myAtan(randomNumbers(i))
Next
diff / #n

MessageRequester("Zeiten", "myAtan: " + Str(time1) + " ms" + #CRLF$ + "ATan: " + Str(time2) + " ms" + #CRLF$ + "Abweichung: " + StrD(diff))
Auf meinem Rechner bekomme ich da dann folgende Ausgabe:
Zeiten hat geschrieben:myAtan: 3523 ms
ATan: 6109 ms
Abweichung: 0.0000358643
Möchte man eine höhere Genauigkeit, muss man natürlich das Taylorpolynom einen Grad höher machen. Leider überwiegt schonn dann der Geschwindigkeitsvorteil nicht mehr wirklich. Deswegen geht heute ja auch alles eher in die Richtung SIMD und Parallel Processing, d.h. per SSE2, SSE3, SSSE4 oder wie die auch alle heißen, werden gleich mehrere Fließkommazahlen auf einmal verarbeitet. Das lohnt sich bei SIMD aber auch nur dann, wenn für jede die selbe Operation ausgeführt werden soll.
Aber ich bin schon wieder ganz wo anders. ;)
Antworten