1. Dashboard
  2. Mitglieder
    1. Letzte Aktivitäten
    2. Benutzer online
    3. Team
    4. Mitgliedersuche
  3. Forenregeln
  4. Forum
    1. Unerledigte Themen
  • Anmelden
  • Registrieren
  • Suche
Alles
  • Alles
  • Artikel
  • Seiten
  • Forum
  • Erweiterte Suche
  1. AutoIt.de - Das deutschsprachige Forum.
  2. Mitglieder
  3. Andy

Beiträge von Andy

  • GetUniqueColors

    • Andy
    • 24. Dezember 2017 um 12:04

    AspirinJunkie

    Ich bekomme den "simplen" Radix-Sort nicht zum laufen. Zwar habe ich unter AutoIt den Kernel in einigen Minuten zum Laufen gebracht, aber die Sortierergebnisse sind falsch....

    Leider kann ich mangels passendem C++-Compiler nicht verifizieren, ob das C++-Programm überhaupt funktioniert.

    Könnte das mal jemand prüfen?

  • Quicksort mit 32-Bit Integerwerten

    • Andy
    • 24. Dezember 2017 um 11:44
    Zitat von AspirinJunkie

    Also z. B. wenn es ein Min/Max bei Assembler gibt: max(min(a,b), min(max(a,b),c)) oder sowas.
    Wie du es jetzt machst weiß ich nicht - denn ich hab keine Ahnung von Assembler

    min() und max() gibt es als Befehl in der SSE, imho aber für 32Bit-Zahlen nur für Float. Ggf. muss man die integer zuerst in float umwandeln und nach der Berechnung wieder zurück. https://c9x.me/x86/

    Und hier die (alten, aber um Klassen besseren) AMD-Docs zu den SSE-Befehlen http://www.share-online.biz/dl/QPV4OG1P30 mit sofort nachvollziehbaren Bildern wie die Register miteinander interagieren.

    Ansonsten MIN() als C-Funktion HIER eingeben und schauen, was die diversen Compiler daraus machen :o)

  • Random in ASM

    • Andy
    • 22. Dezember 2017 um 20:00

    Oh weh, armer Oscar,

    gleich schlagen hier die Mathematiker auf und zerreissen dich in der Luft.

    Ist mir aber egal, ob deine Zufallszahlen den herkömmlichen Prüfverfahren standhalten. ICH finds geil!:rock:

    //EDIT

    Gleich mal 6 Zufallszahlen rausfeuern und damit morgen die 27 Lotto-Millionen einsacken. DANN will ich das Gequieke hören...und Du scheffelst auch Geld, weil dann jeder deinen "Zufallsgenerator" haben will:rofl:

  • Quicksort mit 32-Bit Integerwerten

    • Andy
    • 22. Dezember 2017 um 19:22
    Zitat von Oscar

    Andy: Das erfordert dann aber einen zusätzlichen Speicher (bei 1Mio. DWORDs = 4 Mio. Bytes wären das zusätzliche 8 Mio. Bytes für die Chars). Aber das bestätigt eigentlich meine Vermutung, dass die vielen DLLStructGetData die eigentliche Bremse sind. Es fehlt in AutoIt, die Möglichkeit die Structur wieder schnell in ein Array umzuwandeln.

    Jetzt bin ich mal unverschämt und frage, wer bei der Verwendung von 1 Mio DWORDS als Integerwerte überhaupt mit AutoIt-"Arrays" arbeitet?!

    Eine wie auch immer Struct ist bereits ein Array. In einer der früheren AutoIt-Versionen hatte ich mal einen Vergleich erstellt, der Struct-Funktionen und Arrayfunktionen verglichen hat, wobei beide Verfahren etwa gleich schnell waren.

    Daraufhin hatte ich ein Script geschrieben, welches meine bisherigen "Array"-Funktionen-Scripte in "DllStruct..."-Scripte umschrieb. So war es auch kein Thema, mit schnellen (bei mir ASM-) Funktionen diese Daten zu bearbeiten. Das hat so lange zu meiner Zufriedenheit funktioniert, bis ich festgestellt hatte, dass "irgendjemand" aus der AutoIt-Entwicklercrew die "Struct"-Funktionen massiv in der Geschwindigkeit beschnitten hatte....Soviel zum Update-Wahn und "alles wird besser...". Sch*** drauf!

    Ich hatte mich übrigens mit LarsJ´s Scripten beschäftigt. Sehr intensiv. Um dann festzustellen, dass GENAU das, was eigentlich der riesengroße Vorteil von AutoIt-Arrays ist, nämlich beliebige Datentypen in einem Array zusammenzuwürfeln, nur über massive Umwege wieder in SaveArrays umzuwandeln ist!

    Nur, und da liegt der Hase im Pfeffer, wozu braucht man überhaupt Array´s?!

    Damit irgendwelche Programmieranfänger mit Antwortposts zu _FileReadToArray-Anfragen zugeschmissen werden und davon ausgehen, das sei der ultimative Heilsbringer. Und sich dann wundern, warum 800MB große CSV-Files eine Viertelstunde brauchen um eingelesen zu werden...:Face:

    Arrays machen da Sinn, wo man relativ problemlos und schnell "kleine" Probleme lösen will/muss. Für alles andere MUSS es auch andere Lösungsansätze geben. Und ein "Array" aus 1 Mio 32-Bit-DWORDS ist imho nichts, was man mit AutoIt-Arrays angehen sollte:theke:. Jedenfalls wenn man Wert auf Geschwindigkeit legt.:P

    Übrigens umgeht man mit der Verwendung der SaveArrays nur die AutoIt-interne umkopiererei. Wenn man bspw. an eine Dll-Funktion den Pointer auf einen Datentyp (bspw. String) übergeben muss und dann denkt, "einfach" nur mit "ptr",$string* sei das Problem gelöst, dann fängt der Ärger schon an. AutoIt alloziert zunächst einen Speicherbereich, kopiert dann den String in diese extra dafür(!) erstellte Struct im Speicher und liefert dann den Pointer zurück:Face:.Bei kurzen Strings kein Thema, bei einem MB großen String sind 2 Sekunden weg nur für die Kopieraktion. Bei 100MB großen Strings fängt es dann an, SEHR zäh zu werden...und Strings sind nur ein Beispiel, man kann Pointer auf beliebige Datentypen verwenden...X/

  • GetUniqueColors

    • Andy
    • 20. Dezember 2017 um 13:17
    Zitat von Oscar

    Also das Ergebnis der GPU ist ja schon nicht schlecht, aber das Ergebnis von der CPU ist dann ja doch schlechter als mein ASM-Code zum sortieren.

    Wie bereits gesagt, dein ASM-Code ist handoptimiert :thumbup:und läuft "in einem Rutsch" durch.

    Algorithmusbedingt wird aus AutoIt 300x der OpenCL-Kernel aufgerufen incl. allen Speichergedöns. Selbst in der SIMD-Variante ist da nicht mehr viel zu holen....

    Das schöne ist, man kann sich den vom Compiler erstellten (ASM-) Code ausgeben lassen. Erfahrungsgemäß ist der vom Compiler erstellte Code für die CPU sooo schlecht aber nicht. Da noch die beiden Schleifen drumrum und man hat noch was zum rumspielen:saint:

    Ggf. findet sich ja ein Sortier-Kernel, welcher nicht mehrfach aufgerufen werden muss, dann verbessert sich das Ergebnis sicher massiv, selbst wenn der Algorithmus als "langsamer" eingeordnet wird!

    Oder einer der hier im Forum reichlich vorhandenen C/C++-Programmierer hat ein erbarmen und schreibt uns fix nen Kernel....schaumamal...:theke:

    Wobei dann lediglich die Frage ist, inwieweit die Sortierfunktion von der GPU profitiert. auf der CPU ist ja mit MT und SIMD sowieso alles erreichbare ausgenutzt.

  • GetUniqueColors

    • Andy
    • 20. Dezember 2017 um 07:41

    Noch etwas zum Thema OpenCL-Code GPU vs CPU:

    Der in den Treibern integrierte OpenCL-Compiler erstellt aus dem CL-Kernel Code spezifisch für das verwendete Device. Es wird also für eine GPU der entsprechende Maschinencode generiert, der natürlich "anders" aussieht wie der Code für eine CPU.

    Dabei hat der Compiler das grundlegende Problem, aus dem Quellcode für die jeweilige CPU das schnellstmögliche Ergebnis rauszuholen. Was dazu führt, dass je nach Quellcode auf der CPU(!!!) ein mit einer anderen Programmiersprache bzw. Compiler erstellter Code etwas schneller sein kann. OpenCL hat den großen Vorteil, dass man sich nicht um Multithreading kümmern muss, der Compiler nutzt idR. alle verfügbaren Cores! Dann noch SIMD, und das obere Ende der Fahnenstange ist erreicht!

    Die Verwendung von lokalem Speicher (Cache) und anderen Beschleunigungsmechanismen sollte man allerdings immer im Auge behalten.

    Auch auf der GPU ist bspw. durch die Verwendung von lokalem Speicher und anderen Tricks noch reichlich Optimierungspotenzial vorhanden.

    Kein Wunder, dass CUDA im rechenintensiven Bereich so stark verbreitet ist....

  • GetUniqueColors

    • Andy
    • 19. Dezember 2017 um 22:19
    Zitat von Mars

    Man sieht: Die Intel HD620 ist ähnlich schnell wie eine Nvidia 630GT^^

    :D...wart mal auf die integrierte Grafik bei den Ryzens, die kommen sicherlich schon zur nächsten Cebit...da wird der Markt der Grakas bis 100 Euro komplett zusammenbrechen!

    Übrigens sieht man aber auch, was die GTX1060 trotz des Speichergeschiebes und des "langsamen" AutoIt reißt! Und diese Graka ist definitiv nicht dafür bekannt, bei OpenCL die Wurst vom Brot zu ziehen :o)

    https://browser.geekbench.com/opencl-benchmarks

  • Quicksort mit 32-Bit Integerwerten

    • Andy
    • 19. Dezember 2017 um 22:03

    Hallo Oscar,

    in diesem Post GetUniqueColors habe ich eine Version verwendet, welche aus den Speicherinhalten (UINT/DWORDS) "lesbare" Zahlen (bestehend aus Ziffern^^) macht.

    Das geht relativ schnell, dann hast du einen Text, welchen du per Stringsplit in ein AutoIt-Array umwandeln kannst.

    Ich habe eben mal getestet, bei 1Mio Elementen (in diesem Fall Pixelfarben) dauert auf meinem Rechner der Stringsplit() ca. 500 ms! Die Handvoll Takte zum Erstellen des Textes aus den DWORDS fallen dabei nicht auf....

  • GetUniqueColors

    • Andy
    • 19. Dezember 2017 um 21:28

    Hi zusammen,

    anbei Beispiel(e) für OpenCL unter AutoIt (funktionieren unter 32 und 64Bit-Systemen!) , u.a. auch ein BitonicSort, mit dem ich Oscars Beispieldateigröße (7680 * 4320Pixel) nach Farben sortiere.

    Die Beispiele laufen bei mir auf diversen Platformen, einmal Laptop AMD A6-3400M APU mit 2 Grafik"karten" und auf einem Intel i7-7500U CPU mit HD620 und zusätzlicher GeForce GTX950M.

    Leider habe ich zzt keinen Zugriff auf einen "richtigen" Rechner, aber dafür sind ja hier Leute mit entsprechender Hardware^^

    IdR. werden die OpenCL-Treiber mit den Grafikkartentreibern mitgeliefert, ansonsten findet jeder auf Anhieb bei Tante Google aktuelle Treiber für sein System. Bitte bei 32Bit-Systemen nicht vergessen in den AutoIt-Scripten #AutoIt3Wrapper_UseX64=n zu setzen.

    Wer mehrere Platforms/Devices hat, bekommt ein Auswahlmenü präsentiert, bei dem man das Device auswählen kann. Fenster schließen und es geht los....

    Natürlich kann man auch explizit ein Device (GPU oder CPU) auswählen, sogar mehrere Devices kann man gleichzeitig(!) rechnen lassen, allerdings ist der Aufwand meist die Mühe nicht wert...

    Bei ggf. auftretenden Bugs/Fehlern bitte im Script den Schalter $CL_DEBUGFLAG = 1 setzen und dann die Consolenausgabe posten. Einige OpenCL-Treiber haben feine Fehlerlogs, die ich auswerte/anzeige.

    Günstigenfalls startet man den ersten Versuch mit der Datei Devices64.au3


    Zum BitonicSort unter OpenCL...

    Naja, ich hatte absolut keine Lust, das Rad neu zu erfinden, das wirklich Gute an den OpenCL-Kerneln ist, sie laufen idR. ohne Änderung auf JEDEM System (auch Linux). Also hab ich mich bei den INTEL-Beispielen bedient und den Kernel (die Datei BitonicSort.CL) einfach verwendet....

    Die Verwendung der Parameter bzw. das Ansprechen des Kernels entnimmt man günstigenfalls aus dem Code, mit der das ursprüngliche Programm (meist C/C++) erstellt wurde.

    Übrigens macht es absolut keinen Unterschied in der Geschwindigkeit, ob OpenCL nun über C/C++ oder AutoIt angesprochen wird. Der OpenCL-Treiber compiliert den Kernel für das ausgewählte Device und übernimmt alle Ein/Ausgaben.

    Beim vorliegenden BitonicSort-Algorithmus gibt es allerdings einen Pferdefuß. Der Algorithmus besteht aus nacheinander auzurufenden Stufen (stages), welche jede einzeln sehr gut zu parallelisieren sind.

    Leider muss dazu der Kernel jedes mal aufgerufen werden, bei Oscars Beispieldateigröße (7680 * 4320Pixel) sind das immerhin 300 Aufrufe!

    Dazu kommt, dass der Datentransfer zu den Grafikkarten nur über den PCI-Bus erfolgt. Kostet alles wertvolle Mikro/Millisekunden die sich in der Summe doch bemerkbar machen.

    Im Vergleich dazu die anderen Beispiele, die ich alle selbst geschrieben habe (auch die Kernel). Da hat eine GPU absolut kein Problem, ein Apfelmännchen in fast beliebiger Rechentiefe in Millisekunden darzustellen! Selbst eine aktuelle CPU ist mittels SIMD schon SEHR performant!

    AspirinJunkie, schau dir mal die beiden Kernel im "Tunnel"-Beispiel an, einmal SIMD, einmal "einfach". Einfach den nicht benötigten auskommentieren. Auf der GPU haben beide die gleiche Performance, auf der CPU ist SIMD ca. 3-4x schneller.....

    Hier die Beispiele und Wrapper: OpenCL incl. Beispiele.zip

    Und meine (für mich enttäuschenden) Ergebnisse für den BitonicSort:

    Spoiler anzeigen
    Code
    AMD-System:
    Anzahl zu sortierender 32-Bit-Farben = 33554432   = 0x0000000002000000
    >Number Platforms = 1
    >Device verfügbar= 1;1;4;GPU;BeaverCreek;48731504;8791494502920
    >Device verfügbar= 1;2;4;GPU;Caicos;65627776;8791494502920
    >Device verfügbar= 1;3;2;CPU;AMD A6-3400M APU with Radeon(tm) HD Graphics;66179856;8791494502920
    
    verwendetes Device  1;1;4;GPU;BeaverCreek;48731504;8791494502920
    @@ Debug(537) : $MAX_WORK_GROUP_SIZE = 1024
    @@ Debug(545) : $LOCAL_WORKGROUP_SIZE = 1024
    Puffer füllen...Bitte warten...
    VOR  SORT:   0x0000000000000001  0x0000000000000002  0x0000000000000003  0x0000000000000004  0x0000000001FFFFFD  0x0000000001FFFFFE  0x0000000001FFFFFF  0x0000000002000000
    @@ Debug(97) : Kernelruntime = 8724.23621822232
    @@ Debug(101) : Speicherkopierzeit = 35.1235203388154
    @@ Debug(102) : Anzahl Kernelaufrufe = 300
    NACH SORT:   0x0000000002000000  0x0000000001FFFFFF  0x0000000001FFFFFE  0x0000000001FFFFFD  0x0000000000000004  0x0000000000000003  0x0000000000000002  0x0000000000000001
    
    verwendetes Device  1;2;4;GPU;Caicos;66807424;8791500794376
    @@ Debug(537) : $MAX_WORK_GROUP_SIZE = 256
    @@ Debug(545) : $LOCAL_WORKGROUP_SIZE = 256
    @@ Debug(97) : Kernelruntime = 7678.76578580038
    @@ Debug(101) : Speicherkopierzeit = 138.358899582709
    @@ Debug(102) : Anzahl Kernelaufrufe = 300
    
    verwendetes Device  1;3;2;CPU;AMD A6-3400M APU with Radeon(tm) HD Graphics;68318544;8791498697224
    @@ Debug(537) : $MAX_WORK_GROUP_SIZE = 1024
    @@ Debug(545) : $LOCAL_WORKGROUP_SIZE = 1024
    @@ Debug(97) : Kernelruntime = 8867.72863993904
    @@ Debug(101) : Speicherkopierzeit = 37.2118072474547
    @@ Debug(102) : Anzahl Kernelaufrufe = 300
    
    
    
    INTEL-System:
    Anzahl zu sortierender 32-Bit-Farben = 33554432   = 0x0000000002000000
    >Number Platforms = 2
    >Device verfügbar= 1;1;4;GPU;GeForce GTX 950M;2659965740256;2659965740336
    >Device verfügbar= 2;1;2;CPU;Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz;2659959786096;2659965605984
    >Device verfügbar= 2;2;4;GPU;Intel(R) HD Graphics 620;2659966195648;2659965605984
    
    verwendetes Device  1;1;4;GPU;GeForce GTX 950M;2218229622960;2218229620880
    @@ Debug(540) : $deviceid = 2218229622960
    >Error code: 0
    @@ Debug(542) : $MAX_WORK_GROUP_SIZE = 1024
    @@ Debug(550) : $local_workgroup_size = 1024
    VOR  SORT:   0x0000000000000001  0x0000000000000002  0x0000000000000003  0x0000000000000004  0x0000000001FFFFFD  0x0000000001FFFFFE  0x0000000001FFFFFF  0x0000000002000000
    @@ Debug(98) : Kernelruntime = 3046.04827258151
    @@ Debug(102) : Speicherkopierzeit = 41.1271477469351
    @@ Debug(103) : Anzahl Kernelaufrufe = 300
    NACH SORT:   0x0000000002000000  0x0000000001FFFFFF  0x0000000001FFFFFE  0x0000000001FFFFFD  0x0000000000000004  0x0000000000000003  0x0000000000000002  0x0000000000000001
    
    
    verwendetes Device  2;1;2;CPU;Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz;2000432780336;2000465201648
    @@ Debug(542) : $MAX_WORK_GROUP_SIZE = 8192
    @@ Debug(550) : $local_workgroup_size = 8192
    @@ Debug(98) : Kernelruntime = 3970.81246868319
    @@ Debug(102) : Speicherkopierzeit = 13.5320354436647
    @@ Debug(103) : Anzahl Kernelaufrufe = 300
    
    
    verwendetes Device  2;2;4;GPU;Intel(R) HD Graphics 620;2570054211120;2570053626544
    LOG:
    fcl build 1 succeeded.
    bcl build succeeded.
    @@ Debug(539) : $MAX_WORK_GROUP_SIZE = 256
    @@ Debug(547) : $LOCAL_WORKGROUP_SIZE = 256
    @@ Debug(107) : Kernelruntime = 4226ms
    @@ Debug(111) : Speicherkopierzeit = 18ms
    @@ Debug(112) : Anzahl Kernelaufrufe = 300
    Alles anzeigen


    Wie schnell sind eure Desktop-Grafikkarten bzw. CPU´s?

    Wer hat ggf einen "einfachen" OpenCL-Kernel zum Sortieren gefunden? Bei dem von mir gefundenen Radix-Sort verstehe ich den C/C++-Code nicht, der den Kernel anspricht.....

  • GetUniqueColors

    • Andy
    • 17. Dezember 2017 um 16:20
    Zitat von AspirinJunkie

    Mir auch. Gerade wenn ich Bilddateien höre.
    Hätte ich auch schon längst was gebastelt - mir ist nur halt die Erstkonfiguration zu aufwendig.
    Wenn ich aber dennoch mal unverhofft Zeit und langeweile hab bastel ich auch damit mal was - versprochen.

    Ist ne Grafikkarte nicht sogar eher ein Massive-SIMD-Device?

    Auf einer Grafikkarte sind viele sog. Compute-Units CU verbaut,jedes hat einen eigenen Speicherbereich.

    Der OpenCL-Kernel, der übrigens Sprachtechnisch auf simples C (ohne ++ -gedöns) gründet, wird dabei auf ALLE die Compute-Units verteilt und dort SIMULTAN abgearbeitet.

    Dabei uss man sich idR nicht um alles das kümmern, was einem als Multithread/task-Programmierer das Leben schwer macht. Viele Fehler/Schwierigkeiten entstehen dadurch, dass die Programmierer sich viel zu viele Gedanken machen.

    OpenCL nimmt einem die Komplexität meistens völlig ab, daher bekommen das sogar Leute wie ich es auf die Reihe, extrem schnellen Code zu produzieren.

    Bzgl. Massive-SIMD-Device....wenn du es so nennen willst, dann ja :o)

    Du musst dir aber im Gegenteil zur "reinen" CPU-Programmierung keinen Kopf machen, ob du nun SSE- oder AVX- Befehle einsetzt. Auf der Grafikkarte hast du reichlich Workunits, auf der CPU MUSST du wenn du SIMD nutzen willst, dieses auch explizit im Kernel so programmieren.

    Der OpenCL-Compiler macht aus dem Code für die GPU ein "Massive Multithreading" genau wie für die CPU.

    Da auf der CPU allerdings nur 4-6 Cores (wegen mir je auch noch mit bis zu 2 Threads) statt hunderte bei der GPU zur Verfügung stehen, macht es bei der CPU Sinn, die SSE-Befehle zu nutzen, welche bspw. 4 UINT oder FLOAT gleichzeitig(!) berechnen können.

    OpenCL verteilt den (identischen) Code also sowohl auf GPU als auch auf die CPU, allerdings profitiert die CPU von explizitem SSE/SIMD-Code stärker!

    In den OpenCL-Threads hier im Forum habe ich sowohl/als auch-*.CL Kernel vorgestellt.

    Btw. sollte ich dort auch mal meine neuesten Versionen updaten da hat sich einiges getan......

  • GetUniqueColors

    • Andy
    • 17. Dezember 2017 um 09:54

    Hi zusammen,

    wenn ich Multithreading/tasking höre, schießt mir sofort OpenCL in den Kopf.

    Nach bissl googeln habe ich einige auf jedem System und Programmiersprache verwendbare *.cl-Dateien gefunden, die ich gerne in AutoIt implementiert hätte. Leider sind diese Umsetzungen, bspw. Radix-Sort (für mich) viel zu kompliziert.

    Bei den Intel-Beispielen habe ich einen sehr simplen Bitonic-Sort gefunden, der den SIMD-Ansatz nutzt, also 4x32Bit-UINT´s "auf einen Rutsch" umsortiert. Auf einer Grafikkarte ist kein Geschwindigkeitsgewinn durch SIMD zu erreichen, auf der CPU hilft SIMD massiv durch Einsatz der SSE-Register.

    Ich bin mir nicht ganz sicher wieso das Script auf der Intel-CPU (i7-7500U Kaby-Lake im Laptop) abstürzt, auf dem gleichen Laptop aber die im Prozessor integrierte(n) GPU(s) ( Gforce GTX 950M und Intel HD Graphics 620) einwandfreie und auch sehr schnelle Ergebnisse bringen.

    Ich werde dem auf anderen Rechnern mal nachgehen und dann Ergebnisse posten.

    Zur Not übertrage ich die Sortierroutine nach ASM (exzessiver Gebrauch von SSE :D) , was auf der CPU mindestens Faktor 3 (im Vergleich zur 32-Bit "normal"-Programmierung) in der Geschwindigkeit bringen sollte.

    Aber da meiner Erfahrung nach auch die schnellste CPU nicht mal ansatzweise gegen eine (billigst-) GPU anstinken kann, werde ich ich das als letzte Möglichkeit in Betracht ziehen.

    Zitat von Mars

    Das hier ist der beste Thread seit langem. So viel Elan für etwas das bereits auf 5 Arten erfolgreich gelöst wurde

    ...to be continued...:Face:

  • GetUniqueColors

    • Andy
    • 14. Dezember 2017 um 22:25

    Hallo Oscar,

    wenn du Registerpressure hast (und welcher ASM-Programmierer hat das nicht^^) dann kannst du sehr einfach auch zum zwischenspeichern von Werten/Registerinhalten auf die XMM-Register zugreifen

    Code
    ;~                             push edi                 ; Pixel[loopleft] sichern
                                movd xmm0,dword[esi+ebx*4] ; Pixel[loopright] holen
                                movd dword[esi+eax*4],xmm0 ; nach Pixel[loopleft] schreiben
    ;~                             pop edi                  ; Pixel[loopleft] wiederherstellen

    Bringt im vorliegenden Fall nicht viel, aber einmal PUSH/POP ist eingespart. Im Debugger werden diese 128-Bit Register auch angezeigt incl. aller ihrer sowohl als auch integer oder float (4-Byte) Teile.

    Wenn du weißt, was du tust, kannst du auch noch das EBP-Register, also den Basepointer in deinem Code verwenden. Am Anfang des Programms pushen und am Ende wieder restaurieren, dort steht nach einem Call die Rücksprungadresse fürs "Return" :o).

    In den allermeisten Programmen ändert sich während der Laufzeit EBP nicht....wie gesagt, man muss wissen, was man da tut....

    Du kannst deinen ASM-code übrigens sehr einfach auch in AutoIt multithreadingfähig machen. Für deinen Algorithmus ist das ziemlich ungünstig, aber das am Anfang genannte Verfahren, also Farben an ihrer entsprechenden Adresse speichern und dann die Anzahl der "gesetzten" Adressen zur Summe der Farben zählen schreit geradezu nach Multithreading.

    Hier ein Beispiel <-das ist ein Link

    Mich interessiert die Variante, die Pixelfarbe als Adresse in einem Bit statt in einem Byte (wie von Mars gezeigt) abzuspeichern und dann die Bits als Summe der Anzahl der Farben zu zählen. Selbst für den vollen 32-Bit Farbraum, also incl. Alphakanal wären das gerade mal 500MB Speicher ( 255^4 Farben / 8 Bit pro byte). Vielleicht habe ich dafür ja bissl Zeit, dann sollte aber auch die Multithreadingvariante kommen :o)

  • GetUniqueColors

    • Andy
    • 13. Dezember 2017 um 20:15

    Hier mal die Version mit Zählen der Takte pro Pixel, vielleicht hilft dir das ja weiter.

    C
    #AutoIt3Wrapper_UseX64=n                          ; 32Bit-Modus
    #include <GDIPlus.au3>
    #include <Memory.au3>
    #include "assembleit2_64.au3"
    
    #Region ASM-Code
    #cs _CountUniqueColors                            ;
        Use32                                         ; 32Bit Modus!
    
    
        rdtsc            ;schreibt counter in edx:eax
        movd xmm0,eax   ;sichern
    
    
        mov esi,dword[esp+4]                          ; esi = Pixelstruct-Pointer
        mov edx,dword[esp+8]                          ; edx = right & Pixelcounter
    
    
        dec edx                                       ; um eins verringern, weil die Pixelstruct bei 0 beginnt
        push edx                                      ; edx fuer die Zaehlschleife sichern
        xor ecx,ecx                                   ; ecx = left (auf 0 setzen)
        push edx                                      ; right auf den Stack (fuer Quicksort)
        push ecx                                      ; left auf den Stack (fuer Quicksort)
        call quicksort                                ; Quicksort aufrufen (die Pixelstruct sortieren)
        pop edx                                       ; edx wiederherstellen
        mov eax,1                                     ; eax auf 1 setzen (Farbzaehler)
        mov ebx,dword[esi+edx*4]                      ; ebx = Farbwert des letzten Pixels (zum Farbvergleich)
        @count:                                       ; Zaehlschleife fuer die Farben
            dec edx                                   ; Pixelcounter runterzaehlen
            mov ecx,dword[esi+edx*4]                  ; ecx = Farbwert des vorletzten Pixels (zum Farbvergleich)
            cmp ecx,ebx                               ; ecx mit ebx vergleichen
            jae @next                                 ; wenn groesser/gleich, dann mit naechsten Pixel weitermachen
                inc eax                               ; wenn kleiner, dann Farbzaehler um eins erhoehen
                mov ebx,ecx                           ; und die dazugehoerige Farbe merken
            @next:
            cmp edx,0
            jnz @count                                ; wenn noch nicht 0, dann Schleife @count
    
    
        rdtsc
    movd ebx,xmm0
    sub eax,ebx
    
    
    
        ret                                           ; Pixelcounter an AutoIt zurueckgeben (eax)
    
        quicksort:                                    ; die Quicksort-Funktion
            mov ecx,dword[esp+4]                      ; ecx = left
            mov edx,dword[esp+8]                      ; edx = right
            cmp ecx,edx                               ; left und right vergleichen
            jae @end                                  ; wenn groesser/gleich, dann Funktion beenden
    ;~             _ASMDBG_()                            ; debug-gui anzeigen
                push edx                              ; edx sichern
                push ecx                              ; ecx sichern
                push edx                              ; edx auf den Stack (fuer Partition)
                push ecx                              ; ecx auf den Stack (fuer Partition)
                call partition                        ; Partition aufrufen
                pop ecx                               ; ecx wiederherstellen
                pop edx                               ; edx wiederherstellen
                push ebx                              ; ebx sichern
                push edx                              ; edx sichern
                push ecx                              ; ecx sichern
                push ebx                              ; eax auf den Stack (right fuer Quicksort)
                push ecx                              ; ecx auf den Stack (left fuer Quicksort)
                call quicksort                        ; Quicksort aufrufen (rekursiv)
                pop ecx                               ; ecx wiederherstellen
                pop edx                               ; edx wiederherstellen
                pop ebx                               ; ebx wiederherstellen
                inc ebx                               ; ebx++
                push edx                              ; edx auf den Stack (right fuer Quicksort)
                push ebx                              ; ebx auf den Stack (left fuer Quicksort)
                call quicksort                        ; Quicksort aufrufen (rekursiv)
            @end:
            ret 8
    
        partition:
            mov ecx,dword[esp+4]                      ; ecx = left
            mov edx,dword[esp+8]                      ; edx = right
            mov edi,dword[esi+ecx*4]                  ; edi = Pivotwert = Pixel[left]
            mov eax,ecx
            dec eax                                   ; eax = left - 1
            mov ebx,edx
            inc ebx                                   ; ebx = right + 1
    ;~         _ASMDBG_()                                ; debug-gui anzeigen
            @loop:                                    ; Hauptschleife
                @left:                                ; Schleife fuer die linke Seite
                    inc eax                           ; left++
                    cmp dword[esi+eax*4],edi          ; Vergleich mit Pivotwert
                    jb @left                          ; wenn kleiner, dann Schleife @left
                @right:                               ; Schleife fuer die rechte Seite
                    dec ebx                           ; right--
                    cmp dword[esi+ebx*4],edi          ; Vergleich mit Pivotwert
                    ja @right                         ; wenn groesser, dann Schleife @right
                cmp eax,ebx                           ; Vergleich left und right
                jae @return                           ; wenn groesser/gleich, dann @return
                mov ecx,dword[esi+eax*4]              ; Pixel[left] gegen Pixel[right] austauschen
                mov edx,dword[esi+ebx*4]
                mov dword[esi+eax*4],edx
                mov dword[esi+ebx*4],ecx
                jmp @loop                             ; und mit @loop fortfahren
            @return:
            ret 8                                     ; right zurueckgeben (ebx)
    #ce
    #EndRegion ASM-Code
    
    ;~ $binarycode = _AssembleIt2("retbinary", "_CountUniqueColors") ;gibt nur den assemblierten code zurück
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & ($binarycode) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    ;~ Exit
    Global $g_pMem, $g_iMemSize, $bCode, $tCodeBuffer, $hImage, $iW, $iH, $iTimer, $tBitmapData, $pScan0, $tPixel, $pPixel, $tColors, $pColors, $ret
    ;~ $bCode = "0x8B7424048B5424084A5231C95251E81D0000005A31C08B1C964A8B0C9639D97201408B0C9639D973034089CB4A75F3C38B4C24048B54240839D1732052515251E81A000000595A5352515351E8DFFFFFFF595A5B435253E8D4FFFFFFC208008B4C24048B5424088B3C8E89C84889D34340393C8672FA4B393C9E77FA39D8730E8B0C868B149E891486890C9EEBE2C20800"
    ;~ $tCodeBuffer = _dllstructcreate64_("byte[" & StringLen($bCode) / 2 - 1 & "]") ;reserve Memory for opcodes
    ;~ DllStructSetData($tCodeBuffer, 1, $bCode)
    
    _GDIPlus_Startup()
    
    $file = FileOpenDialog("Select 32 Bpp image!", @ScriptDir, "Image (*.jpg;*.bmp;*.png)", 1 + 2)
    ;~ $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\test_g.jpg')
    $hImage = _GDIPlus_BitmapCreateFromFile($file)
    ;~ $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\test.png')
    
    $aDim =  _GDIPlus_ImageGetDimension($hImage)
    ConsoleWrite(StringFormat('_GetUniqueColors (ASM)\n%d x %d = %s px\n', $aDim[0], $aDim[1], $aDim[0] * $aDim[1]))
    $iTimer = TimerInit()
    
    $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $aDim[0], $aDim[1], BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32ARGB)
    ConsoleWrite('Zeit (BitmapLockBits): ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    $tPixel = DllStructCreate('dword[' & $tBitmapData.Width * $tBitmapData.Height & '];', $tBitmapData.Scan0) ; erstelle Pixelstruct (dword = 32 Bit pro Pixel)
    $pPixel = DllStructGetPtr($tPixel)
    ConsoleWrite('Zeit (Pixelstruct): ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    
    
    
    
    $binarycode = _AssembleIt2("retbinary", "_CountUniqueColors") ;assembles the code into binary
    
    ;$binarycode="0x8B7424048B5424084A5231C95251E81A0000005AB8010000008B1C964A8B0C9639D973034089CB83FA0075F0C38B4C24048B54240839D1732052515251E81A000000595A5352515351E8DFFFFFFF595A5B435253E8D4FFFFFFC208008B4C24048B5424088B3C8E89C84889D34340393C8672FA4B393C9E77FA39D8730E8B0C868B149E891486890C9EEBE2C20800"
    
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & $binarycode & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    Global $tCodeBuffer = DllStructCreate("byte[" & StringLen($binarycode) / 2 - 1 & "]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1, $binarycode)
    
    $ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $pPixel, "dword", $tBitmapData.Width * $tBitmapData.Height)
    $ret = $ret[0] / ($aDim[0] * $aDim[1])
    ;$ret = _AssembleIt2("dword", "_CountUniqueColors", "ptr", $pPixel, "dword", $tBitmapData.Width * $tBitmapData.Height)
    ConsoleWrite('Anzahl der Farben / Takte pro Pixel = ' & $ret & @CR)
    ConsoleWrite('Zeit: ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)
    $tPixel = 0
    $tColors = 0
    $tBitmapData = 0
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()
    ;~ _MemVirtualFree($g_pMem, $g_iMemSize, $MEM_DECOMMIT)
    Exit
    Alles anzeigen

    Und noch die letzte AssembleIt()-Version, bissl finetuning unter der Oberfläche.

    Bspw. benötigt man für den Debugger die ORG-Direktive nicht mehr im Code

    Assembleit2_64.zip

  • GetUniqueColors

    • Andy
    • 13. Dezember 2017 um 19:28

    Hallo Oscar,

    zunächst: [Blockierte Grafik: http://smilie-land.de/t/t-v/verneigen/verneigen0010.gif] TOP(!!) Leistung.

    Ggf solltest du in deinem Post die Version posten, welche kein AssembleIt() benötigt, für diejenigen die "nur" deinen Code ausprobieren wollen benötigt man ausschliesslich AutoIt!

    Das _DllStructCreate64() habe ich auch rausgenommen, ist nur für die 64-Bit-Kompatibilität (also 64-Bit-ASM-Code) nötig.

    C
    #AutoIt3Wrapper_UseX64=n                          ; 32Bit-Modus
    #include <GDIPlus.au3>
    #include <Memory.au3>
    ;#include "assembleit2_64.au3"
    
    #Region ASM-Code
    #cs _CountUniqueColors                            ;
        Use32                                         ; 32Bit Modus!
    ;~     org $PTR_SOURCE_ASMCODE                       ; only needed for assembleit debugger
        mov esi,dword[esp+4]                          ; esi = Pixelstruct-Pointer
        mov edx,dword[esp+8]                          ; edx = right & Pixelcounter
        dec edx                                       ; um eins verringern, weil die Pixelstruct bei 0 beginnt
        push edx                                      ; edx fuer die Zaehlschleife sichern
        xor ecx,ecx                                   ; ecx = left (auf 0 setzen)
        push edx                                      ; right auf den Stack (fuer Quicksort)
        push ecx                                      ; left auf den Stack (fuer Quicksort)
        call quicksort                                ; Quicksort aufrufen (die Pixelstruct sortieren)
        pop edx                                       ; edx wiederherstellen
        mov eax,1                                     ; eax auf 1 setzen (Farbzaehler)
        mov ebx,dword[esi+edx*4]                      ; ebx = Farbwert des letzten Pixels (zum Farbvergleich)
        @count:                                       ; Zaehlschleife fuer die Farben
            dec edx                                   ; Pixelcounter runterzaehlen
            mov ecx,dword[esi+edx*4]                  ; ecx = Farbwert des vorletzten Pixels (zum Farbvergleich)
            cmp ecx,ebx                               ; ecx mit ebx vergleichen
            jae @next                                 ; wenn groesser/gleich, dann mit naechsten Pixel weitermachen
                inc eax                               ; wenn kleiner, dann Farbzaehler um eins erhoehen
                mov ebx,ecx                           ; und die dazugehoerige Farbe merken
            @next:
            cmp edx,0
            jnz @count                                ; wenn noch nicht 0, dann Schleife @count
        ret                                           ; Pixelcounter an AutoIt zurueckgeben (eax)
    
        quicksort:                                    ; die Quicksort-Funktion
            mov ecx,dword[esp+4]                      ; ecx = left
            mov edx,dword[esp+8]                      ; edx = right
            cmp ecx,edx                               ; left und right vergleichen
            jae @end                                  ; wenn groesser/gleich, dann Funktion beenden
    ;~             _ASMDBG_()                            ; debug-gui anzeigen
                push edx                              ; edx sichern
                push ecx                              ; ecx sichern
                push edx                              ; edx auf den Stack (fuer Partition)
                push ecx                              ; ecx auf den Stack (fuer Partition)
                call partition                        ; Partition aufrufen
                pop ecx                               ; ecx wiederherstellen
                pop edx                               ; edx wiederherstellen
                push ebx                              ; ebx sichern
                push edx                              ; edx sichern
                push ecx                              ; ecx sichern
                push ebx                              ; eax auf den Stack (right fuer Quicksort)
                push ecx                              ; ecx auf den Stack (left fuer Quicksort)
                call quicksort                        ; Quicksort aufrufen (rekursiv)
                pop ecx                               ; ecx wiederherstellen
                pop edx                               ; edx wiederherstellen
                pop ebx                               ; ebx wiederherstellen
                inc ebx                               ; ebx++
                push edx                              ; edx auf den Stack (right fuer Quicksort)
                push ebx                              ; ebx auf den Stack (left fuer Quicksort)
                call quicksort                        ; Quicksort aufrufen (rekursiv)
            @end:
            ret 8
    
        partition:
            mov ecx,dword[esp+4]                      ; ecx = left
            mov edx,dword[esp+8]                      ; edx = right
            mov edi,dword[esi+ecx*4]                  ; edi = Pivotwert = Pixel[left]
            mov eax,ecx
            dec eax                                   ; eax = left - 1
            mov ebx,edx
            inc ebx                                   ; ebx = right + 1
    ;~         _ASMDBG_()                                ; debug-gui anzeigen
            @loop:                                    ; Hauptschleife
                @left:                                ; Schleife fuer die linke Seite
                    inc eax                           ; left++
                    cmp dword[esi+eax*4],edi          ; Vergleich mit Pivotwert
                    jb @left                          ; wenn kleiner, dann Schleife @left
                @right:                               ; Schleife fuer die rechte Seite
                    dec ebx                           ; right--
                    cmp dword[esi+ebx*4],edi          ; Vergleich mit Pivotwert
                    ja @right                         ; wenn groesser, dann Schleife @right
                cmp eax,ebx                           ; Vergleich left und right
                jae @return                           ; wenn groesser/gleich, dann @return
                mov ecx,dword[esi+eax*4]              ; Pixel[left] gegen Pixel[right] austauschen
                mov edx,dword[esi+ebx*4]
                mov dword[esi+eax*4],edx
                mov dword[esi+ebx*4],ecx
                jmp @loop                             ; und mit @loop fortfahren
            @return:
            ret 8                                     ; right zurueckgeben (ebx)
    #ce
    #EndRegion ASM-Code
    
    ;~ $binarycode = _AssembleIt2("retbinary", "_CountUniqueColors") ;gibt nur den assemblierten code zurück
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & ($binarycode) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    ;~ Exit
    Global $g_pMem, $g_iMemSize, $bCode, $tCodeBuffer, $hImage, $iW, $iH, $iTimer, $tBitmapData, $pScan0, $tPixel, $pPixel, $tColors, $pColors, $ret
    ;~ $bCode = "0x8B7424048B5424084A5231C95251E81D0000005A31C08B1C964A8B0C9639D97201408B0C9639D973034089CB4A75F3C38B4C24048B54240839D1732052515251E81A000000595A5352515351E8DFFFFFFF595A5B435253E8D4FFFFFFC208008B4C24048B5424088B3C8E89C84889D34340393C8672FA4B393C9E77FA39D8730E8B0C868B149E891486890C9EEBE2C20800"
    ;~ $tCodeBuffer = _dllstructcreate64_("byte[" & StringLen($bCode) / 2 - 1 & "]") ;reserve Memory for opcodes
    ;~ DllStructSetData($tCodeBuffer, 1, $bCode)
    
    _GDIPlus_Startup()
    
    $file = FileOpenDialog("Select 32 Bpp image!", @ScriptDir, "Image (*.jpg;*.bmp;*.png)", 1 + 2)
    ;~ $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\test_g.jpg')
    $hImage = _GDIPlus_BitmapCreateFromFile($file)
    ;~ $hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\test.png')
    
    $aDim =  _GDIPlus_ImageGetDimension($hImage)
    ConsoleWrite(StringFormat('_GetUniqueColors (ASM)\n%d x %d = %s px\n', $aDim[0], $aDim[1], $aDim[0] * $aDim[1]))
    $iTimer = TimerInit()
    
    $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $aDim[0], $aDim[1], BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32ARGB)
    ConsoleWrite('Zeit (BitmapLockBits): ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    $tPixel = DllStructCreate('dword[' & $tBitmapData.Width * $tBitmapData.Height & '];', $tBitmapData.Scan0) ; erstelle Pixelstruct (dword = 32 Bit pro Pixel)
    $pPixel = DllStructGetPtr($tPixel)
    ConsoleWrite('Zeit (Pixelstruct): ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    
    
    
    
    ;$binarycode = _AssembleIt2("retbinary", "_CountUniqueColors") ;assembles the code into binary
    
    $binarycode="0x8B7424048B5424084A5231C95251E81A0000005AB8010000008B1C964A8B0C9639D973034089CB83FA0075F0C38B4C24048B54240839D1732052515251E81A000000595A5352515351E8DFFFFFFF595A5B435253E8D4FFFFFFC208008B4C24048B5424088B3C8E89C84889D34340393C8672FA4B393C9E77FA39D8730E8B0C868B149E891486890C9EEBE2C20800"
    
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & $binarycode & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    Global $tCodeBuffer = DllStructCreate("byte[" & StringLen($binarycode) / 2 - 1 & "]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1, $binarycode)
    
    $ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $pPixel, "dword", $tBitmapData.Width * $tBitmapData.Height)
    $ret = $ret[0]
    ;$ret = _AssembleIt2("dword", "_CountUniqueColors", "ptr", $pPixel, "dword", $tBitmapData.Width * $tBitmapData.Height)
    ConsoleWrite('Anzahl der Farben = ' & $ret & @CR)
    ConsoleWrite('Zeit: ' & Round(TimerDiff($iTimer), 3) & ' ms' & @CR)
    
    _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)
    $tPixel = 0
    $tColors = 0
    $tBitmapData = 0
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_Shutdown()
    ;~ _MemVirtualFree($g_pMem, $g_iMemSize, $MEM_DECOMMIT)
    Exit
    Alles anzeigen

    Jetzt noch ein ganz privates Statement meinerseits....DANKE! Ich war all die Jahre zu faul, einen Quicksort in ASM zu erstellen, ich werde mich in Zukunft an deinem Code bedienen.:P


    Zitat von Oscar

    Mein Quicksort ist eine rekursive Variante, die den Stack ausgiebig nutzt, aber dafür ansonsten nur den Speicher der Pixelstruct (für das Bild) benutzt.

    Das ist auch der Hauptgrund für die vergleichbare "Langsamkeit". Stack heißt Speicherzugriffe, und die kosten Zeit. Schlimmstenfalls liegen die rechten/linken Pixeladressen nicht im Cache und dann tritt der Supergau auf: Cachemiss. Das kostet je nach Prozessor massig Takte!

    Daran ist aber nicht deine Umsetzung schuld, sondern der für die landläufige PC-Architektur "langsame" (sprich Speicherzugriffstechnisch aufwendige) Quicksort-Algorithmus.

    Mir würde jetzt auf Anhieb nicht einfallen, wie man vorliegenden Code stark beschleunigen könnte.

    :klatschen:

    //EDIT

    Habe mal per RDTSC die Prozessor-Takte pro Pixel ausgeben lassen, bei kleinen Bildern ist Quicksort algorithmusbedingt "langsam" mit ca. 200-300 Takten/Pixel.

    Bei deinem 7680 x 4320 Testbild gibt der Algorithmus richtig Gas und kommt bei mir auf 15-20 Takte/Pixel!!!

    //EDIT2

    Nachdem nochmals verifiziert, komme ich mit diversen 12MP-Bildern auf ca. 150 Takte/Pixel

  • Einbinden DLL für das Einlesen von Messwerten über die serielle Schnittstelle

    • Andy
    • 5. Dezember 2017 um 17:08

    Hi,

    Zitat von TheDude

    Diese Variable muss allerdings als Referenz ('double*') übergeben werden, andernfalls stürzt das Programm sofort ab.

    Die "Referenz" ist die Adresse der Variablen.

    Erstelle mit $struct=dllstructcreate("double") einen Platzhalter im Speicher, schreibe dort per dllstructsetdata() deinen Wert hinein und hole dir die Adresse per dllstructgetptr(). Diese übergibst du an deine Funktion.

  • GetUniqueColors

    • Andy
    • 26. November 2017 um 14:29
    Zitat von AspirinJunkie

    WAS?!? Wir schwitzen hier Blut und Tränen, rackern unermüdlich um noch das letzte bisschen Quäntchen rauszukitzeln.

    Separates the boys from the men:party:

  • BITMAP-Datei mit FileRead lesen.

    • Andy
    • 22. November 2017 um 20:33

    OT:

    Zitat von DOheim

    Gibt es eigentlich Assembler-Befehle bzw. -Routinen, mit denen man auf Basis von AssembleIt eine Datei einlesen und schreiben kann?

    Genau dafür wurde AssembleIt NICHT gemacht!

    Ziel war und ist, den "inner Loop" von an sich "langsamen" AutoIt-Schleifen und Berechnungen zu beschleunigen und ansonsten sämtliche Vorteile von AutoIt und dessen Funktionen zu nutzen.

    Wie AspirinJunkie erläutert hat, sind so gut wie alle "nativen" AutoIt-Funktionen sehr eng an das Windows-API gekoppelt, ich würde einfach sagen, gewrappert....

    Wenn nicht gerade irgendwelche esoterischen Ausnahmen bestehen, welche größtenteils durch den AutoIt-Datentyp Variant beeinflusst werden, dann laufen die nativen Autoit-Funktionen genau so schnell ab, wie die API-Aufrufe dieser Funktion in beliebigen anderen Programmiersprachen!

    Es macht also seltenst Sinn, eine (AutoIt-)API-Funktion durch den Aufruf der API per DllCall() ersetzen zu wollen.

    BTT:

    DOheim

    Zitat von DOheim

    Ich habe von AutoIt 3.3.8.1 die Compilerdatei Aut2exe.exe in Aut2exe.exe umbenannt und in den Ordner C:\Program Files (x86)\AutoIt3\Aut2Exe kopiert.

    Mit dieser habe ich mein BITMAP-Programm kompiliert und nun läuft es wieder.

    Sehr befriedigend ist das nicht.

    Wieso ist das nicht befriedigend? Ich verwende in der Firma ausschliesslich die 3.3.8.1 und mir käme nicht im entferntesten in den Sinn, dort auf eine der "neuen" AutoItversionen upzugraden, wozu auch?

    Die von mir erstellten Scripte laufen seit Jahren problemlos. Und daher ist dort ist der "Update-Wahn" völlig fehl am Platz!

    Nach einem Update auf ein "neues" AutoIt nicht bzw. viel schlimmer, FALSCH laufende Scripte würden einen immensen wirschaftlichen Schaden bedeuten. Vom Schaden meiner Reputation garnicht zu reden....

    Übrigens könntest du folgendes versuchen, um mit der "neuen" AutoItversion trotzdem glücklich zu werden.

    Erstelle eine eigene Funktion __FILEREAD__(), welche genau das macht bzw. zurückgibt, was du benötigst. Diese Funktion zu erstellen solltest du schaffen, siehe dein Post#15.

    Dann ersetzt du im gesamten Script (in SCITE per ctrl+h) das (AutoIt-native) FileRead durch deine __FILEREAD__-Funktion.

    Würde mich sehr wundern, wenn das nicht zum Erfolg führen würde ;)

  • GetUniqueColors

    • Andy
    • 22. November 2017 um 14:51
    Zitat von Oscar

    Außerdem sollte ich noch erwähnen, dass der größte Teil von den 200 bzw. 185 ms für das "_GDIPlus_BitmapLockBits" unter AutoIt draufgehen (ca. 141 ms).

    Ja, das "sperren" dauert wirklich extrem lange.

    Ich kann mich vage erinnern, dass bei meinen früheren Versuchen unter XP und WIN7, "schnell" Bitmaps zu bearbeiten, sich _GDIPlus_BitmapLockBits() als langsamste Variante herausgestellt hat, um einen Pointer auf die Pixeldaten zu bekommen.

    Jedenfalls, wie auch schon bei dem Beitrag von Mars erwähnt, wenn man das erste Mal im Script das _GDIPlus_BitmapLockBits() aufruft.

    Sämtliche weiteren Aufrufe dieser Funktion erfolgen teilweise bis zu 100x schneller!

    GGf. hat sich das ja unter WIN10 gegenüber XP/WIN7 geändert?! Auf dem aktuellen WIN10 ist (bei mir zumindest) _GDIPlus_BitmapLockBits() SEHR schnell im Vergleich zu allen anderen getesteten Methoden!

    Generell ist die resultierende Geschwindigkeit aber eine Frage der Programmierung einer selbst erstellten Funktion. Natürlich kann man ALLES was zum Aufruf dieser Funktion erforderlich ist, in den Funktions-Code hineinpacken. Das ist dann idiotensicher und Programmierer/Userfreundlich!

    Ich habe mir allerdings angewöhnt, alles was in irgendeiner Weise mit Speicheranforderungen usw. zu tun hat, beim Start des Programms zu erledigen.

    Dann dauert der Start des Programms ggf. eine Sekunde länger (was niemand bemerkt/stört), aber der Ablauf beim teilweise zig-tausendfachen Aufruf einer Funktion wird massiv beschleunigt (DAS merkt man deutlich!).

    Übrigens Mars

    GEILER ASM-Code! Sauschnell und clever umgesetzt!


    Zitat von Oscar

    Aber vielleicht magst Du ja mal über mein ASM-Programm schauen und mir evtl. Verbesserungsvorschläge aufschreiben:

    Da ist nicht viel zu verbessern:klatschen:

    Sehr einfach, strukturiert und nachvollziehbar, SO muss das sein!:thumbup:

    Hast du den Debugger benutzt? Und ggf. Verbesserungsvorschläge?

    //EDIT

    Insgesamt ist festzustellen, dass sparsamer "Verbrauch" von Speicher für die Farbcodes, also BYTE statt UINT/DWORD in diesem Fall extrem viel an Performance bringt.

    IdR ist das bei "Bildbearbeitung", also Filter u.ä., völlig unerheblich, da Pixel für Pixel nacheinander abgearbeitet wird.

    Der Prozessor liest bei einer Anfrage aus dem Speicher die gesamte Cacheline, ggf. auch mehrere Blöcke. Daher ist das "nächste" Pixel immer im Cache. Es "schwuppt"!

    Beim vorliegenden Script allerdings wird beim Lesen/Schreiben der "Farben" aber relativ wild im Speicher umhergesprungen, der Cache wird somit wesentlich seltener getroffen, Cache-Misses bremsen das Programm extrem aus!

    Ich habe das ASM-Programm daher testweise aufgeteilt:

    Die erste Schleife schreibt, wie gehabt, den Farbcode jedes Pixels als "1" in die BYTE-Struct."Wildes" Schreiben an unterschiedliche Adressen in den Speicher....

    Die nächste Schleife durchläuft nun die BYTE-Struct und liest/addiert nacheinander die gesetzten BYTEs auf, zählt also die verwendeten Farben. Da alle Adressen aufeinander folgen, liegt das nächste zu lesende BYTE immer im Cache!

    In der Summe dauert dieses Programm exakt genau so lange wie die "verschachtelte" Version, allerdings kann man die Zeiten der Schleifen per RDTSC besser ausstoppen.

    Der Unterschied ist gewaltig! Die Schreibschleife benötigt 5-7 Mal so lange wie die Leseschleife.

    Oscar

    Ich habe die von dir erstellte DLL mal mit dem Debugger bzw. IDA untersucht.

    Der Compiler werkelt dabei in der Standard-Einstellung, verwendet also relativ großzügig (Speicher-)Variablen anstelle von Prozessorregistern.

    Schau mal, ob es da einen/mehrere Compilerschalter gibt, die das optimieren, dann käme der Code sicher nahe an den selbstgestrickten ASM-Code heran.

    Btw. wie unterscheiden sich bei dir die Laufzeiten von DLL und ASM?

  • Intel RST, Raid Status Abfrage

    • Andy
    • 18. November 2017 um 06:13

    Hi,

    Zitat von Darktower

    Die Kord. werden gefunden, doch wie erwartet bleibt die Maus still.

    Bei anderen Button/Symbolen etc funktioniert es wirklich gut.

    PushTheButton funktioniert bei mir immer noch....

    In den Tray-Optionen kannst du einen "Mausversatz" einstellen, da es öfter vorkommt, dass beim Mouseover eine andere Grafik des "Buttons" eingefügt wird.

    Übrigens wird kein _ImageSearch()-Dreck dort verwendet, die Suche erfolgt entweder mit einer Byte-Suchfunktion aus der Prospeed.dll (aus 2009btw) bzw. auf heutigen Rechnern ausreichend schnell mit nativem AutoIt! Die Prospeed.dll war übrigens mein Grund, wieder mit Assembler anzufangen...

    Die Prospeed.dll kann man einfach weglassen.

    In der Anleitung zu PushTheButton steht eigentlich alles, wenn Unklarheiten bestehen, bitte kurze Info mit Ergänzungsvorschlägen.

  • GetUniqueColors

    • Andy
    • 17. November 2017 um 15:43

    Hi zusammen,

    um Farben in einem Bild zu zählen hatte ich mal eine Funktion in ASM erstellt....ist schon SEHR lange her^^ (aus 2010). Die "schöne" Variante habe ich leider nicht mehr gefunden, aber ggf. hilft ja das anliegende Script weiter.

    Vorgehensweise:

    Ich erstelle eine Struct mit der Anzahl möglicher Farben einer Bitmap, also 256^3 Farben.

    Dann erhöhe ich für jedes Pixel die entsprechende Adresse innerhalb der Struct (was ja die Farbe darstellt) um 1 hoch. Fertig.

    Um jetzt ein Array für die Farben zu bekommen, erstelle ich aus der Struct einen kommaseparierten Text, welcher aus der Farbe in Hex-Darstellung, gefolgt von der Anzahl dieser Farbe, besteht.

    Diese Texterstellung (aus der Adresse die HEX-Darstellung der Farbe erzeugen) ist ca. 90% des ASM-Codes, das eigentliche "zählen" wird von einer kurzen Schleife mit 6 Zeilen ( erledigt.

    Aus diesem so erstellten Text fummelt StringSplit() ein Array.....

    Bei meinem Uralt-Laptop dauert die Farbseparierung in den Textstring ca. 30ms.

    Das folgende Stringsplit, um daraus ein Array zu erstellen, habe ich nie ausgestoppt, da ich das Array nie brauchte....

    C
    #include <GDIPlus.au3>
    #include <memory.au3>
    ;~ #include <assembleit2_64.au3>
    
    #include <array.au3>
    
    #AutoIt3Wrapper_UseX64=n
    
    
    #cs _countpixelcolors                             ;
        Use32                                         ;32Bit!
    
        org $PTR_SOURCE_ASMCODE                       ;only needed for assembleit debugger
    
        mov edi,dword[esp+4]                          ;pointer bitmap
        mov ecx,dword[esp+8]                          ;width bitmap
        mov ebx,dword[esp+12]                         ;height bitmap
        mov esi,dword[esp+16]                         ;pointer pixelstruct
        movd xmm1,dword[esp+20]                       ;pointer textstruct
    
    
        rdtsc                                         ;timer
        push eax
        ;~     _asmdbg_()
    
        mov eax,ebx                                   ;h
        mul ecx                                       ;w*h
        mov edx,eax                                   ;w*h
        movd xmm0, edx                                ;sichern
    
        ;zuerst werden die Pixel durchlaufen, die Farbe jedes Pixels wird an der Speicherstelle seines "Farbwerts" hochgezählt
    
        @pixel_count:                                 ;alle pixel
        mov eax,[edi]                                 ;farbe
        and eax,0xFFFFFF                              ;eliminieren alpha-channel
        ;~          _asmdbg_()
        inc dword [esi+eax*4]                         ;farbwert=farbwert+1, address=colorRGB^^
        ;add dword [esi+eax*4],1                       ;schneller?
        add edi,4
        sub edx,1                                     ;pixelcounter
        jnz near @pixel_count                         ;jump if not zero(<0)
    
    
        ;***********************************************************************************
        ;jetzt werden die gezählten Farbwerte nacheinander als Text in HEX-Darstellung und der Anzahl als Integerwert in eine textstruct geschrieben
        ;Format: 6BF1E5,1234567@crlf
    
        movd xmm4,edi                                 ;sichern
    
        mov edx,0x1000000                             ;anzahl farben
        movd ebx,xmm1                                 ;pointer textstruct
        mov edi,-4                                    ;pointer colorstruct
    
    
        ;~     _asmdbg_()
    
    
        @count_colors:
        sub edx,1                                     ;jedes pixel
        jz @end                                       ;alle pixel durchlaufen
        mov ecx,[esi+edx*4]                           ;ecx=anzahl, edx=farbwert
        cmp ecx,0
        je @count_colors                              ;wenn ungleich null...ist farbe gefunden
    
        ;~     _asmdbg_()
    
        ;hex-werte farbe =6 Bytes
        ;aus den 6 nibbles die 6 bytes machen, nibble+48 (0x30) =ASCII-Ziffer
        ;zuerst aus den beiden bytes ( Bit 4444333322221111) die nibble erweitern zu 0000444400003333 und 0000222200001111
    
        movd xmm3,eax                                 ;sichern wg registerpressure
        movd xmm4,edx
        movd xmm5,esi
        movd xmm6,ecx
    
        mov eax,edx
        mov edi,eax
    
        ;~ _asmdbg_()
    
        ;**************************************************************
        mov edx,0                                     ;6  nibbles
        _int2hex:
        mov ecx,20                                    ;5*4 shiften
        lea esi,[4*edx]                               ;anzahl der zu shiftenden bits
        sub ecx,esi                                   ;ecx=20,ecx
        shr eax,cl                                    ;nach al shiften
        and al,0xF                                    ;obere 4 nibble eliminieren
        cmp al,9                                      ;größer oder kleiner als A?
        jle _groesserA
        add al,7                                      ;A-F
        _groesserA:
        add al,48                                     ;1-9
    
        mov byte[ebx+edx],al                          ;ascii-hexcode in struct
        mov eax,edi
    
        add edx,1
        ;    _asmdbg_()
        cmp edx,6
        jne _int2hex
    
        add ebx,edx                                   ;ein zeichen weiter
        mov byte [ebx],44                             ;komma
        add ebx,1                                     ;ein zeichen weiter
    
        mov edi,ebx                                   ;pointer text
        movd ebx,xmm6                                 ;anzahl integer
        ;************************************************************
        ;aus einer Zahl(Registerinhalt) einen ZiffernString machen: http://dcla.rkhb.de/umwandlung/int2dez.html
        ;und in die Primstruct schreiben
        ;wird dieser Bereich auskommentiert, wird ein String aus nullen und einsen zurückgegeben 0=keine Prim 1=Prim
        ;dann können mit den AutoIt-Stringbefehlen die Primzahlen und deren Anzahl ausgelesen werden
        push   ebx                                    ;alle benötigten Register sichern
        push   ecx                                    ;alle benötigten Register sichern
        mov eax, ebx                                  ;Zahl laden
        mov ebx, 10                                   ; Divisor
        xor ecx, ecx                                  ;ECX=0 (Anzahl der Ziffern)
        Schleife_1:
        xor edx, edx
        div ebx                                       ; EDX:EAX / EBX = EAX Rest EDX
        push dx                                       ; LIFO
        add cl,1                                      ; ADD soll schneller sein als INC
        or  eax, eax                                  ; AX = 0?
        jnz Schleife_1                                ; nein: nochmal
        Schleife_2:
        pop ax                                        ; gepushte Ziffern zurückholen
        or al, 00110000b                              ; Umwandlung in ASCII
        stosb                                         ; Nur AL nach [EDI] (EDI ist ein Zeiger auf den String)
        loop Schleife_2                               ; bis keine Ziffern mehr da sind
        mov byte [edi],0Dh                            ;CR  CarriageReturn, man könnte auch ein Komma (ascii=2C) einsetzen, dazu noch ein nullbyte als EndOfString
        add edi,1                                     ;ein Byte weiter
        pop   ecx                                     ;Register wiederherstellen
        pop   ebx                                     ;Register wiederherstellen
        ;************************************************************Ende Ziffer aus Register
    
    
        movd esi,xmm5
        movd edx,xmm4                                 ;restaurieren
        mov ebx,edi
    
        jmp @count_colors                             ;schleife alle farben
    
    
        @end:
    
        pop ebx                                       ;count clockticks
        rdtsc
        ;~   _asmdbg_()
        sub eax,ebx
    
        ret
    
    #ce
    
    
    
    
    
    AutoItSetOption("GUIOnEventMode", 1)
    
    _GDIPlus_Startup()
    
    
    
    $sFile = FileOpenDialog("Bilder", @ScriptDir, "Bilder (*.jpg;*.bmp;*.png)")
    
    $himage = _GDIPlus_ImageLoadFromFile($sFile)
    Local $iHeight = _GDIPlus_ImageGetHeight($himage)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $iHeight = ' & $iHeight & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    Local $iWidth = _GDIPlus_ImageGetWidth($himage)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $iWidth = ' & $iWidth & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    
    Global $fTimer
    Global Const $iStride = 4 * $iWidth
    Global $tBitmapData = _dllstructcreate64("dword pixel[" & $iStride * $iHeight & "]")
    Global $ptr_bitmap = DllStructGetPtr($tBitmapData) ; mod(DllStructGetPtr($tBitmapData),64)
    Global $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight, $GDIP_PXF32ARGB, $iStride, $ptr_bitmap)
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : DllStructGetPtr($tBitmapData) = ' & DllStructGetPtr($tBitmapData) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    
    $hCtxt = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    
    _GDIPlus_GraphicsDrawImage($hCtxt, $himage, 0, 0)
    _GDIPlus_GraphicsDispose($hCtxt)
    
    
    
    Global $hGUI = GUICreate("ASM Test", $iWidth, $iHeight)
    GUISetState()
    Global $hGFX = _GDIPlus_GraphicsCreateFromHWND($hGUI)
    
    GUISetOnEvent(-3, "_Exit")
    
    
    ;~ $binarycode = _AssembleIt2("retbinary", "_countpixelcolors") ;gibt nur den assemblierten code zurück
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $binarycode = ' & ($binarycode) & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    $binarycode = "0x8B7C24048B4C24088B5C240C8B742410660F6E4C24140F315089D8F7E189C2660F6EC28B0725FFFFFF00FF048683C70483EA010F85EAFFFFFF660F6EE7BA00000001660F7ECBBFFCFFFFFF83EA010F848C0000008B0C9683F90074EF660F6ED8660F6EE2660F6EEE660F6EF189D089C7BA00000000B9140000008D34950000000029F1D3E8240F3C097E020407043088041389F883C20183FA0675D901D3C6032C83C30189DF660F7EF3535189D8BB0A00000031C931D2F7F3665280C10109C075F366580C30AAE2F9C6070D83C701595B660F7EEE660F7EE289FBE96BFFFFFF5B0F3129D8C3"
    
    ;nur für dllcalladdress() benötigt, den binarycode braucht man nur ein mal erstellen
    $tCodeBuffer = _dllstructcreate64("byte[" & StringLen($binarycode) / 2 - 1 & "]") ;reserve Memory for opcodes
    DllStructSetData($tCodeBuffer, 1, $binarycode)
    
    
    $colorstruct = _dllstructcreate64("uint[" & 256 ^ 3 & "]") ;anzahl möglicher Farben
    $ptr_colorstruct = DllStructGetPtr($colorstruct)
    
    $textstruct = _dllstructcreate64("char[" & 18 * $iWidth * $iHeight & "]") ;maximale Anzahl Zeichen
    $ptr_textstruct = DllStructGetPtr($textstruct)
    ;9 Stellen integer
    ;6 Stellen Hexdarstellung
    ;1 Stellen Komma
    ;2 Stellen CRLF
    
    
    
    $t = TimerInit()
    
    
    $ret = DllCallAddress("uint:cdecl", DllStructGetPtr($tCodeBuffer), "ptr", $ptr_bitmap, "int_ptr", $iWidth, "int_ptr", $iHeight, "int_ptr", $ptr_colorstruct, "int_ptr", $ptr_textstruct)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $ret = ' & $ret[0] & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    $ret = $ret[0]
    
    
    ;~ $ret = _AssembleIt2("uint", "_countpixelcolors", "ptr", $ptr_bitmap, "int_ptr", $iWidth, "int_ptr", $iHeight, "int_ptr", $ptr_colorstruct, "int_ptr", $ptr_textstruct)
    ConsoleWrite('Takte: ' & $ret & @CRLF) ;### Debug Console
    
    
    $m = TimerDiff($t)
    ConsoleWrite('Runtime:  = ' & Int($m) & "ms" & @CRLF) ;### Debug Console
    
    
    $taktperpixel = $ret / $iWidth / $iHeight
    ConsoleWrite("taktperpixel = " & Int($taktperpixel) & @CRLF) ;### Debug Console
    
    _GDIPlus_GraphicsDrawImageRect($hGFX, $hBitmap, 0, 0, $iWidth, $iHeight)
    
    $text = DllStructGetData($textstruct, 1)
    $text = StringTrimRight($text, 1) ;letztes LF entfernen
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $text = ' & $text & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    
    
    
    $colorarray = StringSplit($text, @CRLF)
    _ArrayDisplay($colorarray)
    
    
    
    ;~ For $color = 1 To 256 ^ 3                         ;alle farben per AutoIt auslesen
    
    ;~     $anzahl = DllStructGetData($colorstruct, 1, $color);auslesen
    ;~     If $anzahl <> 0 Then ConsoleWrite(Hex($color - 1, 6) & @TAB & $anzahl & @CRLF)
    
    ;~ Next
    
    
    _GDIPlus_GraphicsDrawImageRect($hGFX, $hBitmap, 0, 0, $iWidth, $iHeight)
    
    Do
    Until Not Sleep(100)
    
    
    
    Func _Exit()
        _GDIPlus_BitmapDispose($hBitmap)
        _GDIPlus_GraphicsDispose($hGFX)
        _GDIPlus_Shutdown()
        GUIDelete()
        Exit
    EndFunc   ;==>_Exit
    
    
    Func _dllstructcreate64($struct)                                   ;align auf 16-byte adresse
    ;~ ;msgbox(0,0,"structmem_start")
        Local $temp = DllStructCreate($struct)
        Local $tempsize = DllStructGetSize($temp) + 64
        Local $ptr = DllStructGetPtr($struct)
        Local $a1 = Mod(Number($ptr), 64)
        Local $temp = 0
        local $mem = _MemVirtualAlloc($ptr + $a1, $tempsize, $MEM_COMMIT, $PAGE_EXECUTE_READWRITE)
              $mem_dllstructcreate64_internal=$mem
        Local $a2 = Mod(Number($mem), 64)                              ;rest div 16 adresse = offset
        local $sstruct = DllStructCreate($struct, (Number($mem) - $a2 + 64))
        Return $sstruct                                                ;auf 16 alingned pointer
    EndFunc                                                            ;==>_dllstructcreate64
    Alles anzeigen

Spenden

Jeder Euro hilft uns, Euch zu helfen.

Download

AutoIt Tutorial
AutoIt Buch
Onlinehilfe
AutoIt Entwickler
  1. Datenschutzerklärung
  2. Impressum
  3. Shoutbox-Archiv
Community-Software: WoltLab Suite™