[offen] Algoritmusoptimierung / ASM / OpenCL von bestehendem Projekt

  • Hallo zusammen, wie im anderen Thread angekündigt suche ich nach einer Beschleunigung eines meiner Projekte.




    Istzustand

    1. Einlesen einer OPD Datei in die Software OmniPC von Olympus (~20 Sekunden)

    2. Export als TXT, diese hat folgenden Aufbau, gekürzt (~60 Sekunden)

    ACHTUNG: Gekürzt auf eine Datenzeile von 21.046! Jede Datenzeile hat 3100 Skalare [siehe USound Qty. (sample)]

    3. Umwandlung mit meinem Tool in dieses Format (~12 Minuten auf meiner VM, ~10 Minuten auf meinem Notebook) <-- hier ist mein Wunsch zur Performancesteigerung versteckt :)


    Ergebnis

    Ich erhalte eine 3D Punktewolke der Prüfdaten, die sich in einem Bruchteil der Zeit auswerten lässt im Vergleich zur Herstellersoftware. Bild darf ich leider nicht zeigen.

    Die Kernroutine funktioniert folgendermaßen

    Da er vermutlich Performance kostet, die Func Ttick()

    Ich habe schon an folgendem gespielt, um den Einfluss zu Prüfen:

    - Zugriffsart auf die Files (bin beim Array zum Einlesen und direktem Schreiben zur Ausgabe gelandet, Vorteil, selbst wenn man abbricht, kann man die bis dahin generierten Daten verwenden)

    - Taktung des Timers der Statusanzeige

    - Diverse andere Möglichkeiten waren langsamer (FileReadLine(), alter Schwede, wie da die Perfomance immer mehr einbricht)


    Heute will ich nochmal eine Maschine mit gleicher Hardware und SSD nutzen, dank AutoIt kann man ja schön portable Apps erstellen.


    Was sagt ihr, ist das Thema interessant für OpenCL, inline ASM oder ist mein Algorithmus Grütze?

  • If $A >= $threshold Then FileWrite($file_out, $X & "," & $Y & "," & $Z & "," & $A & @CRLF) ; write coord with every point

    Steht in $file_out der Pfad zur Datei, oder arbeitest du mit einem Dateihandle?

    Wenn du den Pfad verwendest, dann bremst du dein Skript ziemlich stark aus, da jedes Mal die Datei geöffnet, geschrieben und geschlossen werden muss.

  • Steht in $file_out der Pfad zur Datei, oder arbeitest du mit einem Dateihandle?

    Wenn du den Pfad verwendest, dann bremst du dein Skript ziemlich stark aus, da jedes Mal die Datei geöffnet, geschrieben und geschlossen werden muss.

    Das habe ich so implementiert...

    AutoIt
    1. ; ...
    2. $file_out = FileOpen(GUICtrlRead($outfile), $FO_OVERWRITE + $FO_CREATEPATH)
    3. If $file_out = -1 Then die("Error opening " & GUICtrlRead($outfile) & ".", @ScriptName, @ScriptLineNumber)
    4. ProgressSet(0, "0% - Testing write access ...")
    5. If Not FileWrite($file_out, "X,Y,Z,A" & @CRLF) Then die("Cannot write to file.", @ScriptName, @ScriptLineNumber)
    6. say("File acces initialized.", @ScriptName, @ScriptLineNumber)
    7. ; ...

    Damit schreibe ich mir gleich den header ins CSV.


    Die handles werden auch nur zur Laufzeit auf und auch wieder zu gemacht. Auch die Abbruch und Fehler Funcs prüfen das ab.

    AutoIt
    1. ; ...
    2. If $file_in <> -1 Then FileClose($file_in)
    3. If $file_out <> -1 Then FileClose($file_out)
    4. ; ...
  • Ok, da du uns ja nicht den gesamten Code zeigen kannst müssen wir hier mal auch die Standardzeitfresser abklappern.


    Was sagt ihr, ist das Thema interessant für OpenCL, inline ASM oder ist mein Algorithmus Grütze?

    Nun ja, dein Algorithmus kann auf jeden Fall schlecht sein und du würdest mit ASM trotzdem ordentlich was rausholen können.

    Besonders könntest du mehrere Punkte mit SSE und co. gleichzeitig abarbeiten und das ganze noch schneller machen.


    Ich würde an deiner Stelle nochmal überdenken ob du die Berechnungen effizient anstellst und ob du vielleicht Redundanz noch enthalten hast.


    Eventuell blockieren auch nicht-relevante Funktionen die Geschwindigkeit, z.B. das ProgressSet.

    Ich weiß ja nicht wie oft deine Tick-Funktion aufgerufen wird (warum nicht lieber alle 100ms den Status updaten?) also kann ich mehr oder weniger nur raten.

  • Ja, sorry. ProgrssSet wird mit jedem Zyklus getriggert. Also echt oft und nicht besonders sinnvoll. Beim triggern wird allerdings nur einmal auf die Uhr gesehen und das gegen die gewünschte Updategeschwindigkeit verglichen. Das timerscript ist ungekürzt. Dennoch schafft es ~125.000 Punkte / sek über den Gesamten Umwandlungszeitraum.



    Mein Notebook hat einen i5-3320M.


    Ich hab gestern noch überlegt, dass man Eventuell irgendwie mit Matrixberechnung was machen kann. Nur habe ich das auch noch nie gemacht. Vom Vorgehen würde man dann erstmal alle X, dann alle Y, ... in Spalten schreiben, dann die Skalare stumpf untereinander, wenn counts und steps korrekt sind, muss dass ja übereinander passen.


    Also die Zielmatrix hat ja vier Spalten (X, Y, Z, A), dann jede Spalte in einem eigenen Prozess befüllen lassen. Ich wüsste, wie man das über multi-processing hinbekommt. Nur bei diesen Datenmengen ist die Kommunikation zwischen den Prozessen schwierig.

  • Habe den Einfluss von Timer und Dateisystem grob getestet:




    Wundersames Ergebnis, vom Demnach ist er vernachlässigbar. Ja, nur ein Durchlauf pro verändertem Setting, aber für einen Daumenwert reicht es mir. Ich habe die CPU nebenbei nicht anderweitig gequält.


    Ich teste jetzt eine Andere Art zur Befüllung der Ausgabematrix. Einzelne Schleifen, die hintereinander (statt ineinander) das Zielarray füllen.

  • Guck mal ob das was bringt...

  • Habe beides eingebaut, komme jedoch komischerweise selbst mit dem originalcode nicht mehr über 100.000 pps, werde mal neu starten, notebook läuft 24/7, die VM mit Xeon hat eben 140.000 geschafft. :-|


    Dann habe ich erfahren, dass wir ein windows HTCondor cluster im unternehmen haben. jetzt bin ich ja total hin und hergerissen. condor will mich in richtung consolenapp haben, ich würde gern richtung shared memory und multiprocessing, damit ich auch außerhalb des büros messdaten direkt auswerten kann... werde mir mal einen kaffee holen und dann vermutlich beides einbauen. dann ärgern, das FB tot und ich zu faul zum lernen von c+-# bin.

  • Hi,


    zunächst, kannst du einen Beispiel-Datensatz/Datei der Ursprungsdaten und dazu dann die passende Ergebnisdaten bereitstellen, das würde definitiv weiterhelfen!

    Es müssen ja nur einige Zeilen sein, im Startpost die "Nullen" nutzen nichts^^


    Aufgrund der Datenmengen fällt OpenCL imho aus....


    Ob eine DLL aus (Power/Turbo/Visual)-Basic oder einer andern Hochsprache sein muss, würde ich beinahe bezweifeln. So wie ich das sehe, ist der Flaschenhals der "inner Loop" For $Z = $Ustart bla...

    Ist das so?

    Außerdem wage ich stark zu bezweifeln, dass das FilereadtoArray() performant ist.

    Es ist um mehrere Zehnerpotenzen schneller (gerade bei großen Dateien), diese direkt "Blockweise" einzulesen.

    Gibt es ggf. die Möglichkeit, ein anderes Dateiformat als eine Textdatei generieren zu lassen? Denn allein das Number($Aline[$depthindex]) verursacht mir körperliche Schmerzen, ich vermute mal ganz stark, da gehen 90% (wenn nicht sogar noch mehr) der Gesamtlaufzeit des Scriptes drauf...

    Weiterhin würde ich das Ergebnis, also $X & "," & $Y & "," & $Z & "," & $A & @CRLF in eine Textvariable schreiben, und diese alle x-tausend Zeilen an die Ergebnisdatei hängen. Je nach Speicherbedarf würde ggf. sogar einmal schreiben ausreichen....


    Kommentiere doch mal die Zeilen

    1. $A = Number($Aline[$depthindex]) ; pick scalar of position
    2. If $A >= $threshold Then FileWrite($file_out, $X & "," & $Y & "," & $Z & "," & $A & @CRLF) ; write coord with every point

    einzeln ( ! ) aus und poste die jeweiligen Ergebniszeiten.

  • zunächst, kannst du einen Beispiel-Datensatz/Datei der Ursprungsdaten und dazu dann die passende Ergebnisdaten bereitstellen, das würde definitiv weiterhelfen!

    Es müssen ja nur einige Zeilen sein, im Startpost die "Nullen" nutzen nichts^^

    Alle weiteren Zeilen der Ausgangsdatei haben den Aufbau, nur das sie nicht 0.00 sind sondern eine Zahl zwischen 0.00 und 250.00.


    Eingabedateiaufbau (Messoftware gibt nur diese Datei her, keine Einstellmöglichkeit)

    Außerdem wage ich stark zu bezweifeln, dass das FilereadtoArray() performant ist.

    Es ist um mehrere Zehnerpotenzen schneller (gerade bei großen Dateien), diese direkt "Blockweise" einzulesen.

    Hier habe ich auch viel mit den vorhandenen AutoIt Möglichkeiten getestet. _FileReadToArray($file_in, $lines, $FRTA_NOCOUNT) liest die 315 MB TXT in 2 Sekunden in ein Array.


    Mache mich jetzt mal an die Analyse der beiden von Dir angesprochenen Zeilen. Das Number() kann vermutlich echt weg, da wir bei der US Schreibweise bleiben.


    Danke für Deine Hilfe. :)


    Die Ausgabedatei wird in eine CAD Software eingelesen und produziert dann so etwas hier allerdings mit viiiel mehr Punkten, so dass es plastisch wird.


    Edit: Number() muss bleiben, sonst kann ich es nicht mit dem threshold abgleichen. :(

  • Kommentiere doch mal die Zeilen

    1. $A = Number($Aline[$depthindex]) ; pick scalar of position
    2. If $A >= $threshold Then FileWrite($file_out, $X & "," & $Y & "," & $Z & "," & $A & @CRLF) ; write coord with every point

    einzeln ( ! ) aus und poste die jeweiligen Ergebniszeiten.

    :) Hab ich gemacht, Auswirkungen im Bereich der Messungenauigkeit :/


    Ich hab die Schleifen mal von allem befreit, nur den Ttick in die äußerste, dann schaft AutoIt das mit 1 Mio Koordinaten / sek. Werde jetzt nach und nach die zwingend notwendigen Funktionen wieder einfügen... Habe mehr an der komandozeilentauglichkeit gearbeitet (fertig :)). Ab Dienstag geht es dann weiter an der GUI (Algorithmus und Multiprocessing).

  • Kannst Du genauere Angaben zu den Messwerten machen?

    Wie viele Vor-/Nachkommastellen sind da möglich (von/bis)?

    Man könnte das erheblich beschleunigen, wenn man das mit Integerzahlen darstellen könnte.

    Die drei ineinander verschachtelten For...Next-Schleifen sind in AutoIt jedenfalls schnarchlahm. BTW: welche Werte haben $Istep, $Sstep und $Ustep?

  • Siehe screenshots, im Produkt >67 Mio Koordinatenzuweisungen. Ich bin noch am brüten, wie man die Zielmatrix aus einzelnen Spalten zusammensetzt, die ich vorher in einzelnen Skripten parallel berechnen lasse. Es ist machbar und wenn ich genug Zeit dafür habe nächste Woche, setze ich das um. Dann lande ich bei ~1 min Verarbeitungszeit. Das wäre dann TOP.


    Werte sind alles float. Man könnte evtl. alles mit 1.000 multiplizieren, auf int bringen und wieder durch 1.000 teilen. Das dauert aber dann ja auch wieder... :/

  • Werte sind alles float. Man könnte evtl. alles mit 1.000 multiplizieren, auf int bringen und wieder durch 1.000 teilen. Das dauert aber dann ja auch wieder...

    Genau DAS meine ich:(. Stell doch mal einen Datensatz zur Verfügung, wie gesagt keine Megabytes groß, sondern nur für ca. 20 Ergebniszeilen. Anhand dessen kann man dann sein eigenes Script bzw. einen Vorschlag erarbeiten und vorstellen. Alles andere ist reines rumgestochere...

    Ich bin noch am brüten, wie man die Zielmatrix aus einzelnen Spalten zusammensetzt, die ich vorher in einzelnen Skripten parallel berechnen lasse.

    Brüte nicht, stell einen Datensatz zur Verfügung!


    Wenn ich mir die Schleifen so ansehe, wäre OpenCL ggf doch eine Lösung, da die Daten als FLOAT vorliegen, das ist PRÄDISTINIERT für OpenCL, da für FLOAT-Verarbeitung optimiert.

    Über verschiedene Threads brauchst du dir dann auch keinen Kopf zu machen, macht alles OCL für dich....


    Floatverarbeitung schreit geradezu nach SSE/SIMD, selbst mit den "billigen" 128-Bit-SSE-Registern (für 32-Bit-Code) werden 4 Floats gleichzeitig in EINEM PROZESSORTAKT bearbeitet, und du hälst die Verarbeitungszeit von einer Minute für TOP:Face:Und Assembler braucht man dafür auch nicht, die Intrinsics für einen C-Compiler sind genau dafür abgekupfert^^. Wobei ich wetten würde, dass die Schleifenkonstruktion nach C (oder eine andere Compilersprache) übertragen und RICHTIGE Datentypen (SSE/SIMD) vorausgesetzt die Berechnung in einigen Sekunden abwickeln...

    Wenn du einen 64-Bit-Prozessor und Betriebssystem zur Verfügung hast (davon gehe ich aus), sind ohne weiteres auch die "breiten" 256- bzw. 512 Bit breiten Register ansprechbar, da sind dann 16 Float-Operationen in einem Prozessortakt erschlagen. Wie gesagt, nicht brüten, sondern Arbeitsmaterial zur Verfügung stellen, lass doch die anderen sich einen Kopf machen, denn DAFÜR ist ein Forum da!


    Wobei ich da gerne den optimierten (!) 64-Bit SIMD/SSE C-Code mit einigen Zeilen Assembler versuchen würde zu toppen nur um den F5-Drückern eins reinzuwürgen:rofl: