C++Guns – RoboBlog

15.05.2018

C++ Guns: SIMDarray; std::array for SSE

Filed under: Allgemein — Tags: — Thomas @ 09:05

SIMD (Single Instruction, Multiple Data) - besser bekannt als MMX, SSE, AVX u.s.w ist super für die Datenverarbeitung geeignet. So können, je nach Ausstattung der CPU, viele Gleitkommazahlen gleichzeitig verarbeitet werden. So bietet SSE 128 bit, also 16 Byte, große Register. Hier können zwei double Variable gleichzeitig verarbeitet werden. Zum Beispiel für einfach Point2D Operationen wie Addition, Division. Hingegen ist es mit AVX und 256 bit Register Möglich gleich vier double Variablen gleichzeitig zu verarbeiten (Point3D). Seit 2013 gibt es AVX512 mit 512 bit bzw. 64 Byte Register. Willkommen bei PointND.

Die Compiler unterstützen dies auch gut, allerdings wird nicht so ohne weiteres der Assember Code erzeugt, den ich mir vorstelle. Ich möchte nicht in Schleifen tollen SIMD Code erstellt bekommen, sondern bei normalen Operationen von PointND wie + - * /. Einfach, weil die Schleifen in meinem Algorithmen nie trivial sind und ich sehe mehr Optimierungspotential für einfache PointND Operationen.

Einfaches Beispiel

#include <array>

struct Point2D : public std::array<double,2> {
};

inline Point2D operator+(const Point2D& lhs, const Point2D& rhs) {
    return Point2D{lhs[0]+rhs[0], lhs[1]+rhs[1]};
}

auto func(const Point2D& p1, const Point2D& p2) {
    return p1+p2;
}

Der generierte Assembercode enthält zwei Additions-Instruktionen addsd. Die beiden letzten Buchstaben geben den Datentype an und ob es eine gepackte Operation ist, also mehrere Zahlen gleichzeitig. sd steht für single value double precision. Also eine Zahl. Erwartet hätte ich aber addpd - packed value double precision.

operator+(Point2D const&, Point2D const&):
  movsd xmm0, QWORD PTR [rdi]
  addsd xmm0, QWORD PTR [rsi]
  movsd xmm1, QWORD PTR [rdi+8]
  addsd xmm1, QWORD PTR [rsi+8]

Man kann dem Compiler mit der Erweiterung "Vector Instructions" aber etwas auf die Sprünge helfen. Durch die Definition des Attributs vector_size wird SSE Code produziert, so wie ich es mir vorstelle. Hier das Beispiel:

#include <array>

struct Point2D {
    typedef double v2d __attribute__ ((vector_size (16)));
    v2d _data;

    const double& operator[](size_t pos) const {
        return _data[pos];
    }
};

inline Point2D operator+(const Point2D& lhs, const Point2D& rhs) {
    return Point2D{lhs._data + rhs._data};
}

auto func(const Point2D& p1, const Point2D& p2) {
    return p1+p2;
}

Es wird nur noch eine Addition Instruktion erzeugt. Wir haben die Geschwindigkeit der Operation verdoppelt!

func(Point2D const&, Point2D const&):
  movapd xmm0, XMMWORD PTR [rdi]
  addpd xmm0, XMMWORD PTR [rsi]

Darauf lässt sich doch aufbauen und analog zu std::array ein SIMDarray erzeugen. Damit ist es Möglich für beliebige Arithmetische Typen beliebiger Anzahl effizienten SIMD Code zu erzeugen. Gesagt, getan. Die Klasse findet ihr in ACPL core/util/SIMDarray.hpp

https://sourceforge.net/p/acpl/code/ci/master/tree/acpl/acpllib/include/core/util/SIMDarray.hpp

https://gcc.gnu.org/onlinedocs/gcc-8.1.0/gcc/Vector-Extensions.html#Vector-Extensions

No Comments

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress