# 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 kinematic

f, 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 bringt

Entwicklungssystem:
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 vektorisieren

2. 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 kann

kann 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 liegen

Ich 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