Friday, 17 February 2017

Exponentiell Gleitender Durchschnitt C ++

Ich habe den folgenden Code: int GetAnalogValue (Input) gibt einen 12-Bit-Wert aus einem Ultraschall-Sensor Messdistanz (0-4095, ANALOGVALUERANGE 4096). Doppelte Kompensation ist ein Mitglied der verkapselten Klasse. ReferenceValue ist der ursprüngliche Referenzwert von referenceInput (normalerweise hat er eine niedrige Fluktuation, mit einem Wert irgendwo in der Mitte des 12-Bit-Bereichs, aber er ändert sich mit der Temperatur). RecordData () wird alle 2 Millisekunden aufgerufen (mehr oder weniger, unter Windows ausgeführt). Also eine hohe Aktualisierungsrate für den Referenzwert, aber ich wollte eine allmähliche Kompensation im Laufe der Zeit. Daher verwendete ich einen exponentiellen gleitenden Durchschnitt, das Delta ist empirisch bestimmt und muss möglicherweise später geändert werden. GetData () wird etwa jede Sekunde aufgerufen, um Daten zu löschen und den Datensatz für die weitere Auswertung zu verwenden. Wir begannen, diesen Code auf Kundenanlagen zu testen, bekamen aber das Feedback, dass Werte nach ein paar Tagen abtreiben. Ich denke, es ist ein numerischer instabiler Algorithmus, der Rundungsfehler ansammelt. Kann jemand mit mehr Erfahrung mit den Feinheiten der Gleitkomma-Arithmetik mir antworten: Kann dieser exponentielle gleitende Mittelalgorithmus verbessert werden, um bessere Stabilität zu erzielen Anmerkungen für zusätzliche Verbesserungen des Codes werden auch begrüßt. Ich habe dieses unter Verwendung des Visual C Profilers profiliert, Und es macht etwa 35 der Laufzeit. Dieser exponentielle gleitende Durchschnitt wird mehr als eine Billion Mal genannt, weil er wiederholt bei der Verarbeitung von mehr als 400 Gigabyte Daten verwendet wird. Die Daten kommen aus einem Raid Level 0 Festplattenlaufwerk-Array, so dass das Lesen der Daten für weniger als 5 der Zeit. Die Größe des Preises ist etwa 100.Ich beschleunigte es ursprünglich um den Faktor 4, indem es so viele Daten wie möglich vorberechnet. Dann konnte ich es nochmal um einen Faktor von ndash steigern PaeneInsula Okt 30 11 um 20:41 Ich konnte die Geschwindigkeit wieder um den Faktor 12 erhöhen, indem ich sie multithreading (die Natur der Daten ist so, dass sie multithreaded in sein kann So dass die Last perfekt ausbalanciert ist.) Und ich habe es läuft auf einem i7 990x (das hat 6 Kerne, hyperthreaded von insgesamt 12), übertaktet. Ndash PaeneInsula Oct 30 11 at 20:51 Sicher, Multithreading kann helfen. Aber Sie können fast sicher die Leistung auf einem einzigen Gewinde-Maschine zu verbessern. Zuerst berechnen Sie es in die falsche Richtung. Nur die modernsten Maschinen können negative Vorwärts-Prefetching. Fast alle Maschinen sind schneller für Einheit Fortschritte. D. h. Ändern Sie die Richtung des Arrays, so dass Sie scannen von niedrig zu hoch anstatt hoch zu niedrig ist fast immer besser. Als nächstes, umschreiben ein bisschen - erlauben Sie mir, die Variablennamen zu verkürzen, um es einfacher zu machen: By the way, Ich werde mit der Verwendung von Stenogrammen p für Preis und s für Glättung, um die Eingabe zu speichern. Ich bin faul. Aber es ist wahrscheinlich schneller zu tun Die Latenz zwischen avgi und avgi-2 ist dann 1 multiplizieren und addieren, anstatt eine Subtraktion und eine Multiplikation zwischen avgi und avgi-1. D. h. Mehr als doppelt so schnell. Im Allgemeinen möchten Sie die Wiederholung so umschreiben, dass avgi in Form von avgj für j so weit zurück berechnet wird, wie Sie gehen können, ohne das Gerät auszufüllen, entweder Ausführungseinheiten oder Register. Sie sind im Grunde mehr multipliziert insgesamt, um weniger Ketten von Vielfachen (und subtrahiert) auf dem kritischen Pfad zu erhalten. Überspringen von avgi-2 bis avgi ist einfach, können Sie wahrscheinlich drei und vier. Genau wie weit, hängt davon ab, was Ihre Maschine ist, und wie viele Register Sie haben. Und die Latenz der Gleitkomma-Addierer und Multiplikator. Oder besser noch, den Geschmack der kombinierten Multiply-Add-Anweisung haben Sie - alle modernen Maschinen haben sie. Z. B. Wenn der MADD oder MSUB 7 Zyklen lang ist, können Sie bis zu 6 andere Berechnungen in seinem Schatten tun, auch wenn Sie nur eine einzige Gleitkommaeinheit haben. Vollständig pipeline. Und so weiter. Weniger, wenn alle anderen Zyklen pipelined, wie für doppelte Genauigkeit auf älteren Chips und GPUs üblich ist. Der Assembler-Code sollte Softwarepipeline sein, so dass unterschiedliche Loop-Iterationen überlappen. Ein guter Compiler sollte das für Sie tun, aber möglicherweise müssen Sie den C-Code umschreiben, um die beste Leistung zu erhalten. Übrigens: Ich meine nicht, dass du ein Array von avg erstellen solltest. Stattdessen würden Sie zwei Durchschnitte benötigen, wenn avgi in Bezug auf avgi-2 berechnet wird, und so weiter. Sie können ein Array von avgi verwenden, wenn Sie wollen, aber ich denke, dass Sie nur 2 oder 4 avgs, genannt, kreativ, avg0 und avg1 (2, 3.) haben müssen, und drehen sie. Diese Art von Trick, Aufteilung eines Akkumulators oder Durchschnitt in zwei oder mehr, kombiniert mehrere Stufen der Wiederholung, ist in Hochleistungs-Code gemeinsam. Ach ja, ss vorberechnen usw. Wenn ich es richtig gemacht habe, wäre dies in unendlicher Genauigkeit identisch. (Bitte überprüfen Sie mich, bitte.) Allerdings in endlichen Präzision FP Ihre Ergebnisse können sich unterscheiden, hoffentlich nur geringfügig, wegen der verschiedenen Rundungen. Wenn das Abrollen korrekt ist und die Antworten signifikant unterschiedlich sind, haben Sie wahrscheinlich einen numerisch instabilen Algorithmus. Du bist derjenige, der es weiß. Hinweis: Fließkomma-Rundungsfehler ändern die niedrigen Bits Ihrer Antwort. Beide, weil der Umzug des Codes, und mit MADD. Ich denke, dass ist wahrscheinlich okay, aber Sie müssen entscheiden. Hinweis: Die Berechnungen für avgi und avgi-1 sind nun unabhängig. So können Sie einen SIMD-Befehlssatz wie Intel SSE2 verwenden, der den Betrieb auf zwei 64-Bit-Werten in einem 128-Bit breiten Register zu einem Zeitpunkt ermöglicht. Das ist gut für fast 2X, auf einer Maschine, die genug ALUs hat. Wenn Sie genug Register haben, um avgi in Bezug auf avgi-4 umzuschreiben (und ich bin sicher, dass Sie auf iA64 tun), dann können Sie 4X breit gehen, wenn Sie Zugang zu einer Maschine wie 256 Bit AVX haben. Auf einem GPU. Können Sie für tiefere Wiederholungen gehen, umzuschreiben avgi in Bezug auf avgi-8, und so weiter. Einige GPUs haben Anweisungen, die AXB oder sogar AXBY als einen Befehl berechnen. Obwohl das für 32-Bit üblicher ist als für 64-Bit-Präzision. An einem gewissen Punkt würde ich wahrscheinlich beginnen zu fragen: wollen Sie dies auf mehrere Preise zu einem Zeitpunkt tun nicht nur dies hilft Ihnen mit Multithreading, wird es auch für den Betrieb auf einem GPU. Und mit breiten SIMD. Minor Late Addition Ich bin ein bisschen peinlich, nicht anzuwenden Horners Rule auf Ausdrücke wie etwas effizienter. Leicht abweichende Ergebnisse mit Rundung. In meiner Verteidigung sollte jeder anständige Compiler dies für Sie tun. Aber Hrners Regel macht die Abhängigkeit Kette tiefer in Form von Multiplikatoren. Möglicherweise müssen Sie die Schleife ein paar Mal entrollen und pipelined. Oder Sie können tun, wo Sie precalculateI wissen, dass dies erreichbar mit Boost wie pro: Aber ich möchte wirklich vermeiden, mit Boost. Ich habe gegoogelt und keine geeigneten oder lesbaren Beispiele gefunden. Grundsätzlich möchte ich den gleitenden Durchschnitt eines laufenden Stroms eines Gleitkommazahlstroms mit den letzten 1000 Zahlen als Datenprobe verfolgen. Was ist der einfachste Weg, dies ich mit der Verwendung einer kreisförmigen Anordnung, exponentiell gleitenden Durchschnitt und ein einfacher gleitender Durchschnitt und fand experimentiert zu erreichen, dass die Ergebnisse aus der kreisförmigen Anordnung am besten für meine Bedürfnisse. Wenn Ihre Bedürfnisse sind einfach, können Sie nur versuchen, mit einem exponentiellen gleitenden Durchschnitt. Setzen Sie einfach, Sie eine Akkumulator-Variable, und wie Ihr Code sieht auf jede Probe, aktualisiert der Code den Akkumulator mit dem neuen Wert. Sie wählen eine konstante Alpha, die zwischen 0 und 1 ist, und berechnen Sie: Sie müssen nur einen Wert von Alpha zu finden, wo die Wirkung einer gegebenen Probe nur für etwa 1000 Proben dauert. Hmm, Im nicht wirklich sicher, dass dies für Sie geeignet ist, jetzt, dass Ive es hier. Das Problem ist, dass 1000 ist ein ziemlich langes Fenster für einen exponentiellen gleitenden Durchschnitt Im nicht sicher, gibt es ein Alpha, die den Durchschnitt über die letzten 1000 Zahlen, ohne Unterlauf in der Gleitkomma Berechnung. Aber, wenn Sie einen kleineren Durchschnitt wünschen, wie 30 Zahlen oder so, dieses ist eine sehr einfache und schnelle Weise, es zu tun. Beantwortet Jun 12 12 at 4:44 1 auf Ihrem Beitrag. Der exponentielle gleitende Durchschnitt kann zulassen, dass das Alpha variabel ist. Somit kann dies dazu verwendet werden, Zeitbasisdurchschnitte (z. B. Bytes pro Sekunde) zu berechnen. Wenn die Zeit seit dem letzten Akkumulator-Update mehr als 1 Sekunde beträgt, lassen Sie Alpha 1.0 sein. Andernfalls können Sie Alpha zulassen (usecs seit letztem update1000000). ndash JXH 12. Juni 12 um 6:21 Grundsätzlich möchte ich den gleitenden Durchschnitt eines laufenden Strom von einem Strom zu verfolgen von Gleitkommazahlen die neuesten 1000-Nummern als Datenprobe unter Verwendung. Beachten Sie, dass im Folgenden die Summe als Elemente als addiert ergänzt wird, wobei kostspielige O (N) - Transversionen vermieden werden, um die Summe zu berechnen, die für den durchschnittlichen Bedarf erforderlich ist. Insgesamt wird ein anderer Parameter von T gebildet, um z. B. Mit einer langen langen, wenn insgesamt 1000 lange s, eine int für char s, oder eine doppelte bis total float s. Dies ist ein wenig fehlerhaft, dass Nennsignale an INTMAX vorbeiziehen könnten - wenn Sie darauf achten, dass Sie ein langes langes nicht signiertes verwenden konnten. Oder verwenden Sie ein zusätzliches Bool-Datenelement, um aufzuzeichnen, wenn der Container zuerst gefüllt wird, während numsamples rund um das Array (am besten dann umbenannt etwas harmlos wie pos). Man nehme an, daß der quadratische Operator (T-Abtastwert) tatsächlich quadratischer Operator (T-Abtastwert) ist. Ndash oPless Jun 8 14 um 11:52 Uhr oPless ahhh. Gut beobachtet. Eigentlich meinte ich, dass es sich um void operator () (T sample) handelt, aber natürlich könntet ihr die beliebige Notation verwenden. Wird beheben, danke. Ndash Tony D Jun 14 14 am 14:27


No comments:

Post a Comment