kleine Funktion vektorisieren?
-
bisher haben mir hier schon einigen Leute sehr gut geholfen - Danke an camper,SeppJ,Schlangenmensch und die anderen
Wie kann man die calculate-Methode vektorisieren? (Es sind ein paar von dieser Art aber mir würde diese als "Einstieg" in die Thematik helfen)
#include <chrono> #include <array> #include <iostream> typedef double realtype_t; struct point3d_t { point3d_t(const realtype_t& x, const realtype_t& y, const realtype_t& z) :_data{ x, y, z }{} std::array<realtype_t, 3> _data; realtype_t x() const { return _data[0]; } realtype_t y() const { return _data[1]; } realtype_t z() const { return _data[2]; } }; struct kinematic { kinematic(const point3d_t& a, const point3d_t& b, const point3d_t& c, const point3d_t& d, const point3d_t& e): _a(a), _b(b), _c(c), _d(d), _e(e) { } point3d_t _a; point3d_t _b; point3d_t _c; point3d_t _d; point3d_t _e; point3d_t calculate(realtype_t f, realtype_t g, realtype_t h) const { realtype_t sin_h = std::sin(h); realtype_t cos_h = std::cos(h); return point3d_t ( _a.x() + _b.x() - (-_e.x() + _c.y() + _d.y()) * sin_h + (_c.x() + _d.x()) * cos_h, _a.y() + _b.y() + f + (-_e.x() + _c.y() + _d.y()) * cos_h + (_c.x() + _d.x()) * sin_h, _a.z() + _b.z() + g + _c.z() + _d.z() ); } }; int main(int argc, char** argv) { constexpr size_t WORK_COUNT = 70000000; constexpr size_t AVERAGE_COUNT = 10; std::cout << "work_count: " << WORK_COUNT << std::endl; std::cout << "average_count: " << AVERAGE_COUNT << std::endl; //irgendwelche Werte - nur eben nicht optimierbar point3d_t a(1 / argc, 2 / argc, 3 / argc); point3d_t b(11 / argc, 22 / argc, 33 / argc); point3d_t c(111 / argc, 222 / argc, 333 / argc); point3d_t d(1111 / argc, 2222 / argc, 3333 / argc); point3d_t e(11111 / argc, 2222 / argc, 3333 / argc); const auto k = kinematic(a,b,c,d,e); double anti_optimizer_value = 0.0; double complete_duration = 0.0; for (size_t t = 0; t < AVERAGE_COUNT; ++t) { auto start = std::chrono::high_resolution_clock::now(); for (size_t i = 0; i < WORK_COUNT; ++i) { //irgendwelche Werte - nur eben nicht optimierbar realtype_t f = i+argc; realtype_t g = i / f; realtype_t h = i / g; const auto r = k.calculate(f, g, h); anti_optimizer_value += r.x() + r.y() + r.z(); } auto end = std::chrono::high_resolution_clock::now(); auto diff = end - start; auto milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(diff); complete_duration += milliseconds.count(); std::cout << "duration: " << milliseconds.count() << " ms" << std::endl; } double average = complete_duration / AVERAGE_COUNT; std::cout << "average: " << average << " ms (dummy: " << anti_optimizer_value << ")" << std::endl; return 0; }
"Legende"
3d Punkte (a|b|c|e)[3]={X,Y,Z} = als floats (oder doubles)
-> runtime Konstant (veraendern sich nicht beim Aufruf von calculate)
-> kommen aus Konfiguration
-> bisher Members von kinematicf, g, h = float (oder double) Wert
-> runtime Variant (aendern sich bei jedem Aufruf von calculate)ich bin an kein Speicherlayout bei den 3d Punkten, Members, Parameter etc. gebunden -> alles ist möglich
bisher stelle ich meinen Rechentyp per template-Parameter um alles auf float oder double -> double ist aber nicht zwingend noetig
calulate ist eine Methode - könnte aber auch eine freie Funktion sein falls das was bringtEntwicklungssystem:
VS2015 - zwingend
(VS2017,gcc6.3,clang4/5 - sind (nur) zum spielen vorhanden)Mein Wissen:
-Einfach verwenden != Schnell - man kann viel Falsch machen was dann auch zu noch schlechterer Performanz fuehren kann
-der Microsoft-Kompiler kann Autovektorisieren wenn man ihm hilft
-Programmieren mit Instrinsics ist wie Assembler kann aber auch viel bringen - jeder Kompiler hat da sein eigenes Süppchen
-Es gibt Libraries z.B. boost.SIMD die bei der Vektorisierung von Code helfen - es gibt da ein paar Libs aber deren Qualität usw.
kann ich schlecht einschaetzen (stupide Aufrufe von boost::simd::sincos usw. wirken sich bisher eher negativ auf Performanz aus -
was aber auch an meinem Speicher-Layout/Ablauf hängen kann
-die calculate-Methode ist so "einfach" das man da wohl mit Autovektorisierung oder Instrinsics arbeiten kann
-der VStudio/Analyze/Performance Analyser sagt das ich die meiste Zeit in sin/cos verbringen - und scheinbar ist das auch ein Problem
beim Vektorisieren - vielleicht hilft boost::simd::sincos(single/vector) bei richtiger Anwendung?Eingrenzung:
1. bisher bekomme ich ich nur einen (f,g,h) Satz - d.h. ich koennte wirklich nur die Punktberechnung
in sich vektorisieren2. in Zukunft (aber erst viel spaeter) will ich mein Programm umbauen damit man auch Blöcke(z.B. aus n (f,g,h) bekommt und
calculate dann noch eine for-Schleife enthaelt - aber da muss ich erst mal pruefen ob das funktionieren kannkann man hier überhaupt was optimieren oder frisst eben sin/cos schon alles weg - boost.SIMD bietet eine kombinierte/vektorisierte sincos-Funktion
die liefert die gleichen Ergebnisse ist aber (bisher) langsamer als die Microsoft Routinen - kann aber auch wie gesagt an falsche Anwendung liegenIch könnte wohl auch erstmal die runtime-Konstanten im Kinematic-ctor vorberechnen und mich nur auf sin/cos konzentrieren - aber ich glaube das bringt nicht so viel oder?
hat jemand Ideen, Tips?
-
die unveraenderlichen Anteile im ctor berechnet - hat aber keine messbare Auswirkung - geht im Rauchen unter
struct kinematic { kinematic(const point3d_t& a, const point3d_t& b, const point3d_t& c, const point3d_t& d, const point3d_t& e): _a(a), _b(b), _c(c), _d(d), _e(e), //--- _x_fix_part(_a.x() + _b.x()), _y_fix_part(_a.y() + _b.y()), _xy_fix_part0((-_e.x() + _c.y() + _d.y())), _xy_fix_part1((_c.x() + _d.x())), _z_fix_part(_a.z() + _b.z() + _c.z() + _d.z()) //--- { } point3d_t _a; point3d_t _b; point3d_t _c; point3d_t _d; point3d_t _e; realtype_t _x_fix_part; realtype_t _y_fix_part; realtype_t _xy_fix_part0; realtype_t _xy_fix_part1; realtype_t _z_fix_part; point3d_t calculate(realtype_t f, realtype_t g, realtype_t h) const { realtype_t sin_h = std::sin(h); realtype_t cos_h = std::cos(h); return point3d_t ( _a.x() + _b.x() - (-_e.x() + _c.y() + _d.y()) * sin_h + (_c.x() + _d.x()) * cos_h, _a.y() + _b.y() + (-_e.x() + _c.y() + _d.y()) * cos_h + (_c.x() + _d.x()) * sin_h + f, _a.z() + _b.z() + _c.z() + _d.z() + g ); } point3d_t calculate2(realtype_t f, realtype_t g, realtype_t h) const { realtype_t sin_h = std::sin(h); realtype_t cos_h = std::cos(h); return point3d_t ( _x_fix_part - _xy_fix_part0 * sin_h + _xy_fix_part1 * cos_h, _y_fix_part + _xy_fix_part0 * cos_h + _xy_fix_part1 * sin_h + f, _z_fix_part + g ); } };
-
der gezeigte Code ist nur ein kleiner kompilierbarer Benchmark - braucht ca. 12-14sek auf meinen System - soll nur Aufzeigen wie die calculate-Methode genutzt wird