Ich habe eine ziemlich lange Liste von Gleitkomma-Positivzahlen ( std::vector<float>
, Größe ~ 1000). Die Nummern sind in absteigender Reihenfolge sortiert. Wenn ich sie in der Reihenfolge summiere:
for (auto v : vec) { sum += v; }
Ich denke, ich kann ein numerisches Stabilitätsproblem haben, da nahe am Ende des Vektors sum
viel größer sein wird als v
. Die einfachste Lösung wäre, den Vektor in umgekehrter Reihenfolge zu durchlaufen. Meine Frage ist: Ist das genauso effizient wie der Vorwärtsfall? Wird mir mehr Cache fehlen?
Gibt es eine andere intelligente Lösung?
c++
floating-point
precision
Ruggero Turra
quelle
quelle
Antworten:
Also teste es. Derzeit haben Sie ein hypothetisches Problem, das heißt, überhaupt kein Problem.
Wenn Sie testen und die Hypothese zu einem tatsächlichen Problem wird, sollten Sie sich darum kümmern, es tatsächlich zu beheben.
Das heißt, Gleitkommapräzision kann Probleme verursachen, aber Sie können überprüfen, ob dies wirklich für Ihre Daten gilt, bevor Sie dies vor allem anderen priorisieren.
Eintausend Floats sind 4 KB groß - es passt in den Cache eines modernen Massenmarktsystems (wenn Sie eine andere Plattform im Sinn haben, sagen Sie uns, was es ist).
Das einzige Risiko besteht darin, dass der Prefetcher Ihnen beim Rückwärtslaufen nicht hilft, aber Ihr Vektor befindet sich möglicherweise bereits im Cache. Sie können dies erst dann wirklich feststellen, wenn Sie sich im Kontext Ihres vollständigen Programms profilieren. Es macht also keinen Sinn, sich darüber Gedanken zu machen, bis Sie ein vollständiges Programm haben.
Machen Sie sich keine Sorgen über Dinge, die zu Problemen werden könnten, bis sie tatsächlich zu Problemen werden. Es lohnt sich höchstens, mögliche Probleme zu erwähnen und Ihren Code so zu strukturieren, dass Sie später die einfachste Lösung durch eine sorgfältig optimierte ersetzen können, ohne alles andere neu zu schreiben.
quelle
Ich habe ein Benchmarking durchgeführt Ihren Anwendungsfall mit einem und die Ergebnisse (siehe beigefügtes Bild) weisen auf die Richtung hin, in der es keinen Leistungsunterschied macht, vorwärts oder rückwärts zu schleifen.
Möglicherweise möchten Sie auch auf Ihrem Hardware + Compiler messen.
Die Verwendung von STL zur Ausführung der Summe ist so schnell wie das manuelle Durchlaufen von Daten, aber viel aussagekräftiger.
Verwenden Sie Folgendes für die umgekehrte Akkumulation:
während für die Vorwärtsakkumulation:
quelle
state
Schleife ist zeitgesteuert.Ja, es ist effizient. Die Verzweigungsvorhersage und die Smart-Cache-Strategie Ihrer Hardware sind auf sequentiellen Zugriff abgestimmt. Sie können Ihren Vektor sicher akkumulieren:
quelle
Zu diesem Zweck können Sie den Reverse-Iterator ohne Transpositionen in Ihrem
std::vector<float> vec
:Oder machen Sie den gleichen Job mit dem Standardalgortithmus:
Die Leistung muss gleich sein und nur die Bypass-Richtung Ihres Vektors ändern
quelle
Wenn Sie unter numerischer Stabilität Genauigkeit verstehen, kann dies zu Genauigkeitsproblemen führen. Abhängig vom Verhältnis der größten zu den kleinsten Werten und Ihren Anforderungen an die Genauigkeit des Ergebnisses kann dies ein Problem sein oder auch nicht.
Wenn Sie eine hohe Genauigkeit wünschen, ziehen Sie die Kahan-Summierung in Betracht - dies verwendet einen zusätzlichen Float zur Fehlerkompensation. Es gibt auch eine paarweise Summierung .
Eine detaillierte Analyse des Kompromisses zwischen Genauigkeit und Zeit finden Sie in diesem Artikel .
UPDATE für C ++ 17:
Einige der anderen Antworten erwähnen
std::accumulate
. Seit C ++ 17 gibt es Ausführungsrichtlinien, mit denen Algorithmen parallelisiert werden können.Zum Beispiel
Dies sollte das Summieren großer Datenmengen auf Kosten nicht deterministischer Rundungsfehler beschleunigen (ich gehe davon aus, dass der Benutzer die Thread-Partitionierung nicht bestimmen kann).
quelle