{"id":3453,"date":"2018-05-10T12:09:21","date_gmt":"2018-05-10T11:09:21","guid":{"rendered":"http:\/\/roboblog.fatal-fury.de\/?p=3453"},"modified":"2018-05-10T23:26:18","modified_gmt":"2018-05-10T22:26:18","slug":"c-guns-perfekt-generiertes-assember-von-meiner-geometry-library","status":"publish","type":"post","link":"http:\/\/roboblog.fatal-fury.de\/?p=3453","title":{"rendered":"C++ Guns: perfekt generiertes Assember von meiner Geometry Library"},"content":{"rendered":"<p>Schaut euch mal folgenden Anfang einer Line2D intersect Funktion an. Erstaunlich wie sauber und perfekt der Assember Code aus meinem C++ Code generiert wird. Da ist absolut kein Overhead zu erkennen. Keine Funktionsaufrufe, kein unn\u00f6tiges Laden und zwischenspeichern von Daten. Keine tempor\u00e4re Objekte auf dem Stack. \u00dcberhaupt keine Stack Nutzung. Es gibt im Code f\u00fcnf Subtraktionen und zwei Multiplikationen. Das resultiert im Assember Code mit f\u00fcnf Subtraktionen, zwei Multiplikationen und vier explizite Kopier-Befehle f\u00fcr doubles. Besser geht es doch gar nicht mehr. Und das alles ist nur mit Optimerungslevel O1 compiliert. Nicht O2, nicht fanzy O3, nein, nur O1. GNU GCC alle g\u00e4ngigen Versionen. Clang kann es nat\u00fcrlich nicht mit O1, nur mit O2, aber dann sieht der ASM Code nicht mehr so sch\u00f6n symmetrisch aus ;) Bei Intel funktioniert es auch mit O1, die ASM Code Anordnung ist etwas anders. Microsoft... nein.<br \/>\nUnd die wichtigsten Zeilen vom C++ Code sind doch auch ausdrucksstark. <\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;array&gt;\r\n\r\nstruct Point2D : public std::array&lt;double,2&gt; {\r\n    inline const auto&amp; x() const { return at(0); }\r\n    inline const auto&amp; y() const { return at(1); }\r\n};\r\n\r\ninline const Point2D operator-(const Point2D&amp; p1, const Point2D&amp; p2) {\r\n    return Point2D{p1.x()-p2.x(), p1.y()-p2.y()};\r\n}\r\n\r\nstruct Line2D : public std::array&lt;Point2D, 2&gt; {\r\n    inline const Point2D&amp; p1() const { return at(0); }\r\n    inline const Point2D&amp; p2() const { return at(1); }\r\n};\r\n\r\nauto func(const Line2D&amp; line1, const Line2D&amp; line2) {\r\n    Point2D a = line1.p2() - line1.p1();\r\n    Point2D b = line2.p1() - line2.p2();\r\n    const auto denominator = a.y() * b.x() - a.x() * b.y();\r\n    return denominator;\r\n}\r\n<\/pre>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nfunc(Line2D const&amp; line1, Line2D const&amp; line2):\r\n  movsd (%rsi),   %xmm0 # line2 x1\r\n  subsd 16(%rsi), %xmm0 # line2 x2\r\n  movsd 24(%rdi), %xmm1 # line1 y2\r\n  subsd 8(%rdi),  %xmm1 # line1 y1\r\n  mulsd %xmm1,    %xmm0\r\n  movsd 8(%rsi),  %xmm1 # line2 y1\r\n  subsd 24(%rsi), %xmm1 # line2 y2\r\n  movsd 16(%rdi), %xmm2 # line1 x2\r\n  subsd (%rdi),   %xmm2 # line1 x1\r\n  mulsd %xmm2,    %xmm1\r\n  subsd %xmm1,    %xmm0\r\n  ret \r\n<\/pre>\n<p>\u00dcbrigends bekommt man den selben Assember Code auch mit float statt double. Und f\u00fcr int bleibt die Struktur auch die selbe, es werden nur die normalen Register benutzt. Nur bei long double kommen ein paar Kopierbefehle dazu, da die normalen floating point Register der CPU genutzt werden, statt SSE. Da SSE aber 128bit floating point Zahlen verarbeiten kann, muss ich wohl die CPU beim Compiler angeben.<\/p>\n<p>\/\/ Nachtrag<br \/>\nEinen setz ich noch drauf: SSE mit GNU gcc Vector Instructions Extension. Point2D mit double als Type l\u00e4sst sich wunderbar per SSE verarbeiten. So k\u00f6nnen zwei Subtraktionen\/Multiplikationen parallel ausgef\u00fchrt werden. Damit l\u00e4sst sich extrem kompakte (und super effizienter) ASM Code generieren.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;smmintrin.h&gt;\r\n\r\nusing v2sd = double __attribute__ ((vector_size (16)));\r\nstruct  Point2D : public std::tuple&lt;v2sd&gt; {\r\n....\r\n}\r\n\r\nauto func(const Line2D&amp; line1, const Line2D&amp; line2) {\r\n    const Point2D a = line1.p2() - line1.p1();\r\n    const Point2D b = (line2.p1() - line2.p2());\r\n    const auto denominator = a.y()*b.x() - a.x()*b.y();\r\n    return denominator;\r\n}\r\n<\/pre>\n<p>Nur noch drei Substraktionen. Leider kommen so unpack Anweisungen dazu.<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nfunc(Line2D const&amp;, Line2D const&amp;):\r\n  movapd 16(%rdi), %xmm1\r\n  subpd (%rdi), %xmm1\r\n  movapd (%rsi), %xmm2\r\n  subpd 16(%rsi), %xmm2\r\n  movsd %xmm2, %xmm0\r\n  movapd %xmm1, %xmm4\r\n  unpckhpd %xmm4, %xmm4\r\n  mulsd %xmm4, %xmm0\r\n  unpckhpd %xmm2, %xmm2\r\n  mulsd %xmm2, %xmm1\r\n  subsd %xmm1, %xmm0\r\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Schaut euch mal folgenden Anfang einer Line2D intersect Funktion an. Erstaunlich wie sauber und perfekt der Assember Code aus meinem C++ Code generiert wird. Da ist absolut kein Overhead zu erkennen. Keine Funktionsaufrufe, kein unn\u00f6tiges Laden und zwischenspeichern von Daten. Keine tempor\u00e4re Objekte auf dem Stack. \u00dcberhaupt keine Stack Nutzung. Es gibt im Code f\u00fcnf [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[17],"class_list":["post-3453","post","type-post","status-publish","format-standard","hentry","category-allgemein","tag-cpp"],"_links":{"self":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3453","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3453"}],"version-history":[{"count":9,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3453\/revisions"}],"predecessor-version":[{"id":3472,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/3453\/revisions\/3472"}],"wp:attachment":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3453"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3453"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3453"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}