Beiträge von alpines
-
-
Ich sollte noch dazu sagen, dass ich ursprünglich x0.7 der Höhe genommen hatte (weil ich noch ID3-Tags anzeigen wollte) und das Testen ohne x0.7 gemacht habe.
Wenn wir das noch in Betracht ziehen sind wir bei knapp 1000 Zyklen pro Sekunde!
Falls hier jemand das gerne mal ausprobieren möchte (AutoIt-Berechnung mit ASM-Code zu substituieren) soll er das auf jeden Fall machen.
Wenn man ständig auf seinen x86-Befehlssatzspicker schaut, ist das gar nicht mal so schwierig. Zumindest habe ich kaum mehr Fehler gehabt als normal mit AutoIt zu scripten.
Und mit ein paar Bytes Code kann man seinem Script einen unfassbaren Boost verpassen!
-
Das wären dann ca. 300-400 Zyklen
300-400 Zyklen? Nix da! Ich kriege 800 Zyklen pro Sekunde jetzt raus!!
ASSEMBLER ROCKT!!!Der ASM-Code berechnet die Amplituden und die Koordinaten der Frequenzen und fügt diese mit GdipAddPathLine2 hinzu.
In der AutoIt-Hauptschleife wird nur noch der Gdi+Buffer gecleared, der fftBuffer gelesen, der ASM-Code gecalled und anschließend der Pfad und der Double-Buffer gezeichnet.
Kein unnötiges Hin und Her mit DllStructs sondern alles zeitkritische in ASM!
Das tolle obendrein ist ja, dass ich 100 Zwischenfrequenzen (_SubdivideFrequencies) statt 10 nehmen kann
und die Performance bricht bei weitem nicht so heftig zusammen wie vorher.
Man kann sicherlich noch bisschen was rauskitzeln aber das ist schon mal ne Wucht! Kein Vergleich zu reinem AutoIt!
-
Beim Aufrufen einer jedwegen Funktion steht die Rücksprungadresse immer auf [esp+0]
Ich dachte es mir fast schon! Danke für die Bestätigung!

Wenn ich nach unten hin schiebe werden diese dann auch anschließend überschrieben, kein Wunder, dass die Rücksprungsadresse dann nicht mehr stimmt.
Ich werde mich mal jetzt dranmachen die Berechnung der Punkte und das Hinzufügen mittels _GDIPlus_PathAddLine2 in ASM zu packen und berichte dann wieder.
-
Ich hab mir mal aus der GDIPLus.au3 angeguckt in welchem Format das ganze sein muss und habe gesehen, dass dort einfach nur ein DllStruct mit floats erstellt wird.
Das wollte ich jetzt auch machen und habe das ganze mehr oder weniger übersetzt und versuche es in ASM zu callen.Den Pointer zur Funktion, das Handle des Pfads, den Pointer zur Struct und die Anzahl der Punkte übergebe ich aber es crasht leider:
Die Funktion wird auf jeden Fall ausgeführt, da der Stack aufgeräumt wird aber das Skript crasht anschließend.
EAX wird auch auf 0 gesetzt. Das ist der GdiStatus 0 = OK.
Wenn ich das ganze so wie du erstmal in die Register schiebe und dann neu auf den Stack pushe dann klappt das ganze (2. Script) aber wenn ich alles schon vorher richtig auf dem Stack platziere und dann nur den Call ausführe klappt das komischerweise nicht. Wieso?
AutoIt
Alles anzeigen#include <WinAPI.au3> #include <Array.au3> #include <GDIPlus.au3> #include <assembleit2_64.au3> #cs _addToPath use32 start: add esp, 4 pop eax _ASMDBG_() call eax _ASMDBG_() sub esp, 4 ret #ce _GDIPlus_Startup() $hKERNEL32 = DllOpen("kernel32.dll") ;~ $hGDIPLUS = dllopen("gdiplus.dll") $handle= _WinAPI_GetModuleHandle("gdiplus.dll") ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $handle = ' & $handle & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console $pointer= DllCall($hKERNEL32, "ptr", "GetProcAddress", "ptr", $handle, "str", "GdipAddPathLine2") ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $pointer[0] = ' & $pointer[0] & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console $hGUI = GUICreate("") GUISetState() $tPoints = DllStructCreate("float [4]") DllStructSetData($tPoints, 1, 10.0, 1) DllStructSetData($tPoints, 1, 10.0, 2) DllStructSetData($tPoints, 1, 150.0, 3) DllStructSetData($tPoints, 1, 150.0, 4) $hGraphics = _GDIPlus_GraphicsCreateFromHWND($hGUI) $hPath = _GDIPlus_PathCreate() ;~ _GDIPlus_PathAddLine($hPath, 10, 10, 150, 150) $ret = _AssembleIt2("int", "_addToPath", "ptr", $pointer[0], "ptr", $hPath, "ptr", DllStructGetPtr($tPoints), "int", 2) ConsoleWrite($ret & @CRLF) _GDIPlus_GraphicsDrawPath($hGraphics, $hPath) While GUIGetMsg() <> -3 WEndAutoIt
Alles anzeigen#include <WinAPI.au3> #include <Array.au3> #include <GDIPlus.au3> #include <assembleit2_64.au3> #cs _addToPath use32 start: mov eax, [esp+8] mov ebx, [esp+12] mov ecx, [esp+16] mov esi, [esp+4] _ASMDBG_() push ecx push ebx push eax _ASMDBG_() call esi _ASMDBG_() ret #ce _GDIPlus_Startup() $hKERNEL32 = DllOpen("kernel32.dll") ;~ $hGDIPLUS = dllopen("gdiplus.dll") $handle= _WinAPI_GetModuleHandle("gdiplus.dll") ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $handle = ' & $handle & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console $pointer= DllCall($hKERNEL32, "ptr", "GetProcAddress", "ptr", $handle, "str", "GdipAddPathLine2") ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $pointer[0] = ' & $pointer[0] & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console $hGUI = GUICreate("") GUISetState() $tPoints = DllStructCreate("float [4]") DllStructSetData($tPoints, 1, 10.0, 1) DllStructSetData($tPoints, 1, 10.0, 2) DllStructSetData($tPoints, 1, 150.0, 3) DllStructSetData($tPoints, 1, 150.0, 4) $hGraphics = _GDIPlus_GraphicsCreateFromHWND($hGUI) $hPath = _GDIPlus_PathCreate() ;~ _GDIPlus_PathAddLine($hPath, 10, 10, 150, 150) ;~ _GDIPlus_PathAddLine($hPath, 150, 150, 150, 150) $ret = _AssembleIt2("uint", "_addToPath", "ptr", $pointer[0], "hwnd", $hPath, "struct*", DllStructGetPtr($tPoints), "uint", 2) ;~ ConsoleWrite($ret & @CRLF) _GDIPlus_GraphicsDrawPath($hGraphics, $hPath) While GUIGetMsg() <> -3 WEnd -
Ich saß gestern den gesamten Abend dran endlich meine Berechnung in ASM zu machen statt purem AutoIt und es hat sich rausgestellt,
dass meine Implementation gerade mal einen Vorteil von x1.28 bringt.
Natürlich bin ich kein ASM-Profi und der Code ist sicherlich noch weiter optimierbar, also tobt euch mal bitte aus und hilft mir, den Code zu optimieren!

Ich habe keine Funktion zum Berechnen der dritten Wurzel gefunden, also habe ich mir eine Approximation geschrieben.
Idee der Approximation
Approximation von einem Dezimalbruch N
durch Addition von 1/(2^x), x in Natürlichen Zahlen
Im x86-Befehlssatz habe ich zum Potenzieren keine wirklichen Funktionen gefunden bis auf F2XM1.
F2XM1 berechnet (2^x)-1 und da ich x^(1/3) rechnen musste bietet sich da die Umrechnungsformel x^y = 2^(y * log2(x)) an,
allerdings darf beim F2XM1 Befehl der Exponent x nur zwischen -1.0 und +1.0 liegen.
Damit konnte ich leider nicht den gesamten Wertebereich von 0.0 bis 1.0 hoch 1/3 abdecken -> 2^(1/3 * log2(0.0 bis 1.0)) da bei einem x von 0.125 (wird zu -1) und 1.0 (wird zu 0) abdecken.
Das reichte mir leider immer noch nicht, da ich gerne eine Abdeckung von 0.0 (wird zu -unendlich) bis 1.0 wollte.
Dann kam mir die Idee, einfach den Exponenten mittels der Wurzelfunktion zu approximieren.
Es gibt im x86-Befehlssatz die Funktion FSQRT und diese berechnet die Quadratwurzel von ST(0) (oberster Wert auf dem FPU-Stack).
Die Potenzgesetze erlauben es einem den Exponent zu addieren, wenn man Potenzen gleicher Basen multiplziert.
Beispiel: 2^(1/2) * 2^(1/4) = 2^(1/2 + 1/4) = 2^(3/4)
Jetzt kann man sich immer näher an den gewünschten Wert mittels Quadratwurzeln rantasten:
Beispiel: 1/6 = 0.166666666
Approximationsanfang = 0
1/2^0 = 1/1 = 1 1 + Approximation <= 1/6 ? Nein!
1/2^1 = 1/2 = 0.5 0.5 + Approximation <= 1/6 ? Nein!
1/2^2 = 1/4 = 0.25 0.25 + Approximation <= 1/6 ? Nein!
1/2^3 = 1/8 = 0.125 0.125 + Approximation <= 1/6 ? Ja! 0.125 + 0 = 0.0125 => Neue Approximation = 0.125
1/2^4 = 1/16 = 0.0625 0.0625 + Approximation <= 1/6 ? Nein!
1/2^5 = 1/32 = 0.03125 0.03125 + Approximation <= 1/6 ? Ja! 0.03125 + 0.125 = 0.15625 => Neue Approximation = 0.15625
1/2^6 = 1/64 = 0.015625 0.015625 + Approximation <= 1/6 ? Nein!
1/2^7 = 1/128 = 0.0078125 0.0078125 + Approximation <= 1/6 ? Ja! 0.15625 + 0.0078125 = 0.1640625 => Neue Approximation = 0.1640625
Wenn wir nun 0.1640625 als Approximation nutzen wollen müssen wir lediglich folgende Rechnung durchführen:
x^(1/8) * x^(1/32) * x^(1/128)
Als ASM-Code ungefähr so zu implementieren:
x -> ST0 | ST0 = x
SQRT(ST0) -> ST0 | ST0 = x^(1/2)
SQRT(ST0) -> ST0 | ST0 = (x^(1/2))^(1/2) = x^((1/2) * (1/2)) = x^(1/4)
SQRT(ST0) -> ST0 | ST0 = x^(1/8)
Wir haben nun einen Wert den wir für die Approximation brauchen also kopieren wir ST0 nach ST1
FST(ST1) | ST0 = ST0, ST1 = ST0
SQRT(ST0) -> ST0 | ST0 = x^(1/16)
SQRT(ST0) -> ST0 | ST0 = x^(1/32)
Neuer benötigter Wert!
FST(ST2) | ST0 = x^(1/32), ST1 = x^(1/8)
SQRT(ST0) -> ST0 | ST0 = x^(1/64)
SQRT(ST0) -> ST0 | ST0 = x^(1/128)
Neuer benötigter Wert steht in ST0.
Wir haben die Approximatino nun ausgerechnet und müssen nur noch die Ergebnisse multiplizieren:
FMULP | ST0 = x^(1/128) * x^(1/32), ST1 = x^(1/8) FMULP popt nämlich das Register nach der Multiplikation
FMULP | ST0 = x^(1/128) * x^(1/32) * x^(1/8)
Fertig gerechnet, das Ergebnis steht in ST0!
Man kann das ganze natürlich in ASM so programmieren, dann man beliebig nah an die Approximation kommt (wie das AutoIt-Äquivalent unten)
aber in meinem Code haben vier Faktoren als Genauigkeit ausgereicht also habe ich es hardgecodet und nicht dymanisch.
Momentan übergebe ich fast alle Daten mittels DllStructs an den ASM-Code und lese diese auch so wieder aus (DllStructGetData).
Gibt es da eine schnellere Möglichkeit als jeden Index einzeln aus dem Struct zu lesen?
Die "Zyklen pro Sekunde"-Messung schwankt sehr stark je nach Bildschirmauflösung und Visualizergröße.
Das Script liest aus dem fftBuffer aus der aktuellen .mp3-Datei und berechnet einen Visualizer welcher mittels GDI+ angezeigt wird.
Das Script zieht bei mir rund 15-20% CPU-Last (Intel Core i7 4790) und berechnet 260 Zyklen die Sekunde.
Am Ende soll ein Sleep in die Schleife eingebaut werden, damit nur noch 40-50 Zyklen pro Sekunde berechnet werden (sieht immer noch flüssig aus) und die CPU-Last dann auf <1% sinkt.
Das klappt auch bei mir schon fast mit einem Sleep(15) (42-44 Zyklen): CPU-Last: ~2% (mit anderen Prozessen im Hintergrund die nichts berechnen).
Dann wäre noch die Frage:
Ist es möglich die _GDIPlus_PathAddLine2 aus dem ASM-Code aufzurufen? Dann könnte ich nämlich die gesamte Berechnung der Punkte auf den ASM-Code auslagern und hätte dann nochmal einen Geschwindigkeitsboost! Wäre nett, wenn mir jemand zeigen könnte (wenn es möglich ist) wie es geht, die Implementierung würde ich gerne selber ausprobieren!!
Angehangen ist AssembleIt2_64, Approximation.au3, BASS.au3, BASSconstants.au3, bass.dll, visualizer.au3
Tobt euch aus!
-
Damals hatte man noch irre Angst vor Headcrashes der Festplatte, ich hab also den Rechner gekauft und einen Kumpel gebeten mein Auto (Käfer BJ1965) zu fahren von Wiesbaden nach Mainz, ich auf dem Rücksitz und den Rechner freischwebend in den Händen gehalten um ja die kleinste Erschütterung abzufangen.

-
Mit dem Release der 3.3.14.3 wurde auch endlich eine Tabelle in die Hilfe mitaufgenommen. Siehe hier.
-
Zur GOTO Diskussion fällt mir noch ein - gab es nicht sogar programierbare Taschenrechner mit GOTO Befelssatz?
Diese haben meistens eine Implementation eines Akzentes von BASIC. Mein alter Schultaschenrechner (TI-84+) unterstützt Goto und Labels.
-
Wenn du den Pfad anpasst wird das aber nicht mehr funktionieren.
-
Und falls man doch das Bedürfnis verspürt es mit Stringoperationen selbst zu machen, geht z.B. auch
Das wäre aber nun wirklich mal eine einfache Aufgabe für Dich

-
Alles anzeigen
Func drawRedRect($hGfx)
Local $hBrush
$hBrush = _GDIPlus_BrushCreate(0xffff0000)
If $hBrush = Null Then Goto CleanUp
_GDIPlus_GraphicsFillRect($hGfx, 0, 0, 100, 100, $hBrush)
CleanUp:
_GDIPlus_BrushDispose($hBrush)Return
EndFuncStehst du heute wieder auf dem Schlauch oder übersehe ich da etwas? Wenn $hBrush = Null ist, dann existiert das Objekt doch offenbar nicht und muss nicht extra disposed werden.
-
Alle Globalen-Variablendefinitionen sollten auf jeden Fall aus den Funktionen raus, das ist sehr -- wirklich -- sehr schlechter Stil und führt nur zu Unmengen an Warnungen.
Die GUIs solltest du ebenfalls überarbeiten, denn du rufst sie rekursiv auf. Das bedeutet, dass du von GUI1 zu GUI2 springst und dann von GUI2 GUI1 neu erstellst.
Den Befehl Call solltest du komplett aus deinem Code streichen, der ist unnötig. Rufe deine Funktionen einfach direkt auf wie du MsgBoxen z.B. aufrufst.
-
Poste ihn doch auf pastebin.com und verlink ihn dann hier.
-
BTW, weiß noch jemand wie die Commodore-Computer vor dem VC20 hießen?
Meinst du die Commodore PET Serie?
-
Wollte ihn gerade hier posten aber irgendwie will das nicht so recht.
Du hast es doch bisjetzt auch hinbekommen, woran scheiterts?
-
Du kannst den Dateinamen mit Stringfunktionen zurechtschneiden.
StringTrimLeft schneidet alles links ab der angegebenen Position zurück.
StringInStr sucht in einem String ein Zeichen (du kannst auch angeben ab welchem x.ten Auftreten des Zeichens er die Position zurückgeben soll, auch von hinten)
Die beiden Befehle brauchst du zum löschen des Teils davor. Jetzt liegt es an dir sie vernünftig mit Hilfe der Dokumentation zusammenzusetzen.
-
Falls Du SciTE als Editor benutzt, rufe einfach Tidy (STRG & T) auf. Das formatiert den Code.
Psscht! Das soll beim Coden automatisch von der Hand gemacht werden!
-
Schön, dass es funktioniert. Ich würde dir aber sehr ans Herz legen, dass du deinen Code von der Struktur/Formatierung nochmal überarbeitest.
Die Klammern bei der $sArtikeltext-Definition sind nicht nötig und du verwendest keine Tabs zum Einrücken von Code.
Funktional hat das alles keinen Wert aber erhöht die Leserlichkeit des Codes und du blickst auch nach ein paar Jahren schnell wieder durch.
-
Kann höchstens daran liegen, dass dein Script schon bei dem 1. If sich beendet.
Du findest im Editor den "</>"-Button für Code, wähle als Sprache AutoIt aus und paste ihn dann rein.