sin/cos in/um atan2 anders ausdruecken/optimieren?



  • sorry für die blöde frage, aber was willst du da eigentlich genau berechnen?

    den Kontaktpunkt einer 2-Glider 5-Achs-Kinematik - ich habe aber bisher nur geprüft wie weit sich die Berechnungen aus dem Original-Code reduzieren lassen (bei 100% gleichen Daten zum Original)
    im wesentlichen sind das jetzt sin/cos von 2 Achsen und atan2 darauf - von dem m
    athematischen Zusammenhang erkennt man jetzt aber glaube ich nicht mehr viel

    meine letzte Idee war es jetzt noch den sin und cos von atan2(cos(c),sin(c))/2.0 auszuspezialisieren - dann werden alle weiteren sin/cos durch ein paar sqrts(die scheinbar weniger Power brauchen) ersetzt - bringt nochmal fast 5 sek bei 70Mio Durchläufen 🙂



  • Gast3 schrieb:

    double v2 = (M_PI/2 - c)/2;

    Danke camper sieht schon mal ein wenig besser aus

    http://cpp.sh/6tbio

    kann man die Vorzeichnen noch angleichen - die sin(v2) und cos(v2) gehen nur
    noch multiplikativ ein - da ist das Vorzeichen wichtig 🙂

    Die Vorzeichen sind entweder beide richtig oder beide falsch. Was letztlich damit zu tun hat, dass deine sin_v(c) und cos_v(c) keine richtigen sinus/cosinus-Funktionen sind.
    Wenns drauf ankommt, muss man eben beachten, in welchem Quadranten c liegt:

    double v2 = remainder(M_PI / 2 - c, 2 * M_PI) / 2;
    

  • Mod

    Versuch doch mal von den Mathematica-Umformungen weg zu kommen (die wahrscheinlich nicht funktionieren, da du wahrscheinlich mit komplexen Zahlen rechnest) und denk darüber nach, was die Formel tun soll. Denkst du, ich hätte für meine Antwort gerechnet und umgeformt? Ich habe nur gesehen, dass die Kombination sin, cos einen Winkel in ein Koordinatenpaar umwandelt, atan2 ein Koordinatenpaar zurück in einen Winkel umwandelt und du dann einfach nur wieder sin und cos von dem halben Winkel nimmst, man sich die ganze Umformerei auch hätte sparen können. Das ganze Geplänkel danach, wo du sin und cos vertauscht und/oder Minuszeichen einführst geht doch nur noch darum, wie genau du deine Winkel definierst. Also denk mal darüber nach, welchen Winkel genau du hier aus welchen Gründen halbieren möchtest, dann hast du deine Lösung sofort.



  • Versuch doch mal von den Mathematica-Umformungen weg zu kommen (die wahrscheinlich nicht funktionieren, da du wahrscheinlich mit komplexen Zahlen rechnest) und denk darüber nach, was die Formel tun soll.

    ich hab auch schon langsam gemerkt das Mathematica und Konsorten keine Wunder vollbringen - bisher habe ich es einfach vermieden mich tiefer einzuarbeiten - ist nur ein On-Off Projekt von mir und normalerweise mache ich gar nichts mit Mathe - oder ist 10 Jahre her das ich mal was gemacht habe - d.h. ich muss mich in jedes Detail wieder reinarbeiten - aber ihr öffnet mir gerade die Augen



  • Danke camper - nochmal 5sek 🙂

    jetzt bleiben noch 2 Dinge:

    kann man hier noch weiter reduzieren (ich suche gerade nach der remainder Implementierung)

    double v = remainder(M_PI / 2 - c, 2 * M_PI) / 2;
    double cos_v = cos(v);
    double sin_v = sin(v);
    

    mein nächstes Privat-Projekt - anständig in Trigonometrie einarbeiten und eure Punkte hier nachvollziehen



  • remainder macht Divison und rundet u.a. - da kann ich wohl nichts rausziehen


  • Mod

    Viele Rechnenwerke können Sinus und Cosinus gemeinsam berechnen. Da besteht noch Handoptimierungspotential, sofern dein Compiler diese Optimierung nicht schafft.



  • c kann jeder beliebige Winkel sein, also
    c = φ + n * 2π ; -π <= φ < π; n ganz
    Der halbe Winkel v muss dagegen im Bereich -π/2 <= v < π/2 liegen. Das n stört hier und und remainder entfernt es.

    Ich weis ja nun nicht wo dein c herkommt. Evtl. kennst du ja den Faktor n bereits anderweitig (genaugenommen reicht es ja sogar zu wissen, ob es gerade ist oder nicht), das könnte z.B. vollständigen Umdrehungen in deiner Steuerung entsprechen. Wenn es bekannt ist, kannst du auch einfach entsprechend mit 2π multiplizieren und von c abziehen, und dafür auf remainder verzichten.



  • SeppJ schrieb:

    Viele Rechnenwerke können Sinus und Cosinus gemeinsam berechnen. Da besteht noch Handoptimierungspotential, sofern dein Compiler diese Optimierung nicht schafft.

    es gibt den alten x86 FPU FSINCOS - der ist aber sehr langsam verglichen mit den neueren SSE2(+) Implementationen, und der Microsoft-Kompiler fasst sin/cos nicht zusammen weil ja der 1. Aufruf errno setzen könnte - gcc ruft eine zusammengefasste routine auf, clang kann man da mit fast-math-sub-optionen nachhelfen

    https://godbolt.org/g/vh9dqT

    ich habe auch schon versuche mit boost.SIMD gemacht - war aber viel langsamer - werde da aber noch die Autor kontaktieren

    SeppJ schrieb:

    Ich weis ja nun nicht wo dein c herkommt.

    Das ist direkt eine Achsenposition - die kann alles von double min/max annehmen (nach meinem bisherigen Wissen)



  • ich verstehe auch überhaupt nicht warum diese Proposal zum hinzufuegen von std::sincos (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0081r0.pdf) in der Luft haengen geblieben ist - alle: egal ob Intel, Microsoft, clang, gcc, libc etc... jeder hat n Varianten (single/double/gpu/x86/x64(sse...)) - nur keine davon ist per Standard erreichbar

    oder dieser Hẹckmeck mit den math.h Routinen die errno setzen - und Optimierung verhindern - teilweise machen es die gcc-Routinen, manche nicht, clang hat wieder andere Builtins/Optimierungen - auf einer anderen Platform/Stdlib wieder total anders und, und, und

    ich hatte mal Kontakt mit einem der clang-cl Entwickler und der hat nur gemeint man soll den ganzen Müll in math.h mit komplett neuen Funktionen in einem neuen Namespace überlagern und die alten Zöpfe langsam abschneidbar machen
    - irgendwie stört sich niemand so richtig am dem Ist-Zustand - nur die Leute die damit kämpfen - und die sind scheinbar nicht laut genug



  • oder floating-Point(0) Optimierung nichts als #pragmas - sondern nur von aussen per --fast-math-optionen, kaum bis nichts bei Microsoft, einfach ein sehr unordentliches Süppchen da draussen



  • hatte kurz Zugriff auf einen Dual Linux/Windows Rechner und einen sincos-Vergleich gemacht

    https://godbolt.org/g/uC9V2r

    Windows 7 x64, VS2017, x64/Release: 13,7 sek (kein sincos vorhanden)
    Standardsettings fuer Release

    Linux Ubuntu 17.04 x64, gcc 6.3, x64/Release: 10.4 sek (mit sincos per default)
    g++ -O2
    mit -march=native ist es langsamer geworden so 12.5 sek?



  • @camper:

    so jetzt bin ich mal wieder dazu gekommen alles zu testen

    dein tip mit remainder und atan2 hat für einen Fall wunderbar funktioniert

    double v_atan2 = atan2(cos_c, sin_c) / 2.0;
    double v_remainder = -remainder(c - M_PI / 2.0, M_PI * 2.0) / 2.0;
    

    aber für einen anderen Fall ist das Ergebnis nicht immer richtig

    double v_atan2 = atan2(-cos_c, -sin_c) / 2.0;
    double v_remainder = remainder(c - M_PI / 2.0, M_PI*2.0) / 2.0;
    

    cpp.sh/4fqsl

    kannst du mir nochmal erklären wie das mit dem reminder funktioniert damit
    ich das korrigieren kann



  • Gast3 schrieb:

    aber für einen anderen Fall ist das Ergebnis nicht immer richtig

    double v_atan2 = atan2(-cos_c, -sin_c) / 2.0;
    double v_remainder = remainder(c - M_PI / 2.0, M_PI*2.0) / 2.0;
    

    Deine Umformung ist inkorrekt. -cos_c und -sin_c entsteht, indem du c um π verschiebst.

    double v_remainder = remainder(-M_PI / 2.0 - c, M_PI*2.0) / 2.0;
    

    ist eine Möglichkeit.

    Bezgl.längerer Laufzeit mit -march=native (bzw. -mavx) ggf. https://www.c-plusplus.net/forum/344147 beachten.



  • Deine Umformung ist inkorrekt. -cos_c und -sin_c entsteht, indem du c um π verschiebst.

    Danke jetzt funktioniert es richtig - und ist auch gleich noch ein bisschen schneller geworden

    die Nutzung von remainder an dieser Stelle sollte auch die Präzision(weniger Rundungsfehler usw.) gegenüber atan2/sin/cos erhöhen oder sehe ich das falsch?

    sollte ich die Kinematik-Berechnung doch noch mal komplett frisch betrachten wird das eine oder andere aus den Diskussionen hier bestimmt mit einfliessen

    Danke



  • jetzt habe die ganze Kinematik noch mal durchgearbeitet und hab das ganze drastisch reduziert (kein If/Else, nur noch eine Rechnung)

    für mein gefordertes Ergebnis brauche ich jetzt nur noch:

    (
    x * sin(c) + y * cos(c),
    z * sin(a) - cos(a) * (x * cos(c) - y * sin(c)),
    x * sin(a) * cos(c) - y * sin(a) * sin(c) + z * cos(a)
    ) + offsets()

    einfacher geht wohl nicht mehr und hat die Berechnung nochmal um Faktor 2 beschleunigt

    jetzt sind die einzigen Zeitfresse sin und cos

    hat jemand vielleicht einen Tip ob es performantere Implementationen gibt
    die nicht total weit weg sind von der Präzision der Standardimplementationen?

    habe mir folgende angeschaut:

    http://gruntthepeon.free.fr/ssemath/ (original)
    https://github.com/RJVB/sse_mathfun (erweitert um SS3 und SSE4)
    eine abgewandelte Form von ssemath wird auch in Eigen verwendet:
    Eigen/src/Core/arch/SSE/MathFunctions.h
    Eigen/src/Core/arch/AVX/MathFunctions.h
    dann soll es noch was von Intel oder AMD geben

    btw: der x86 FSINCOS Befehl ist nicht schneller und nicht das was ich suche, nur eine SSE2+ Implementation kann da noch helfen - die eingebauten sin/cos sind schon SSE2+

    Erfahrungswerte?



  • und Boost.SIMD

    ssemath ist teilweise sehr ungenau und Boost.SIMD ist ein paar Sekunden langsamer



  • dann gibts es noch sincos Implementationen in

    Intel Integrated Performance Primitives (IPP)
    Intel Math Kernel Library (MKL)
    AMD Math Library (LibM)
    AMD Core Math Library (ACML)



  • ganz vergessen zu schreiben:

    die Sin+Cos Berechnungen brauche ca. 60-70% der Zeit (bei 70Mio Testläufen)


Anmelden zum Antworten