{"id":4967,"date":"2021-09-18T09:04:59","date_gmt":"2021-09-18T08:04:59","guid":{"rendered":"http:\/\/roboblog.fatal-fury.de\/?p=4967"},"modified":"2022-08-03T13:53:23","modified_gmt":"2022-08-03T12:53:23","slug":"vektorisieren-leicht-gemacht","status":"publish","type":"post","link":"http:\/\/roboblog.fatal-fury.de\/?p=4967","title":{"rendered":"Vektorisieren leicht gemacht"},"content":{"rendered":"<p>Als Beispiel sollen ein paar reduzierte Zeilen Code aus der Datei computeFluxes.h [1] aus dem Vplna-OP2 [2] Projekt dienen, welche auf unterschiedliche Arten vektorisiert werden sollen. Um so nah wie m\u00f6glich am original Code zu bleiben, werden die Pointer Argumente der Funktionen, welche Arrays darstellen, wo es m\u00f6glich ist nicht durch einen passerenden Typen ersetzt. Zu erst werden die GCC Vector Extensions benutzt, danach der Vektorisierer von GCC und zum Schluss OpenMP mit der SIMD Direktiven. Mit OpenMP ist es nicht nur m\u00f6glich mehrere Tasks auf viele CPU Kerne laufen zu lassen, es lassen sich auch einzelne Schleifen vektorisieren. Untersucht wird der erzeugte ASM Code mit godbolt [3]<\/p>\n<p>Compiliert wird mit GCC 11.2 -O2. Die st\u00e4rke Optimierung -O3 schaltet zwar den Vektorisierer ein, aber erzeugt auch ASM Code der nicht mehr so leicht nachzuvollziehen ist. Der Vektorisierer kann manuell mit -ftree-vectorize [4] aktiviert werden. Daher wird der Quelltest mit dem Optionen -O2 -ftree-vectorize untersucht.<\/p>\n<h1>Zusammenfassung<\/h1>\n<p>Die GCC Vector Extensions bieten f\u00fcr dieses Beispiel die beste Art zu Vektorisieren, da der C++ Code am besten verst\u00e4ndlich ist und ein hohen Grad an Vektorisierung erm\u00f6glicht. Es m\u00fcssen keine Schleifen von Hand eingebaut oder mit OpenMP spezialisiert werden. Es ist M\u00f6glich den Code \"einfach so hin zu schreiben\". Es ist noch zu erw\u00e4hnen, dass die \"GCC Vector Extensions\" Variante nur vier Zeilen C++ Code ben\u00f6tigt!<\/p>\n<p>Der direkte Vergleich der Beispiele zeigt es deutlich. Verglichen wird die Anzahl relevanter C++ Code Zeilen (LOC), die totale Anzahl ASM Instruktionen, Anzahl SIMD Instruktionen die Vektorisiert werden k\u00f6nnten, Anzahl SIMD Instruktionen die vectorisiert wurden in Prozent. Sowie die Anzahl Sprung Anweisungen (Schleifen). Da bei jedem Sprung die Branch Prediction falsch liegen kann sind Spr\u00fcnge als Performance Kritisch anzusehen.<\/p>\n<table border>\n<tr>\n<td>Variante<\/td>\n<td>LOC<\/td>\n<td>Anzahl Instruktionen<\/td>\n<td>Anzahl SIMD Instruktionen<\/td>\n<td>Vektorisiert %<\/td>\n<td>Sprung Anweisung<\/td>\n<\/tr>\n<tr>\n<td>GCC Vector Extensions<\/td>\n<td><font color=\"green\">4<\/font><\/td>\n<td>28<\/td>\n<td>25<\/td>\n<td>84.0<\/td>\n<td>Nein<\/td>\n<\/tr>\n<tr>\n<td>-O2 -ftree-vectorize Original Code<\/td>\n<td>12<\/td>\n<td>35<\/td>\n<td>34<\/td>\n<td>0.0<\/td>\n<td>Nein<\/td>\n<\/tr>\n<tr>\n<td>-O2 -ftree-vectorize H\u00e4ndisch Schleifen eingef\u00fcgt<\/td>\n<td>13<\/td>\n<td>30<\/td>\n<td>27<\/td>\n<td>85.2<\/td>\n<td>Nein<\/td>\n<\/tr>\n<tr>\n<td>OpenMP<\/td>\n<td>15<\/td>\n<td>37<\/td>\n<td>26<\/td>\n<td>11.5<\/td>\n<td>Ja<\/td>\n<\/tr>\n<\/table>\n<p>Es kann niemals eine 100%ige Vektorisierung stattfinden, da es wie indiesem Beispiel immer Stellen gibt, bei denen einzelne Skalare in unterschiedlichen Register stehen zusammen gez\u00e4hlt werden m\u00fcssen. Der Unterschied zwischen 84% und 85.2% ist nicht relevant, da dieses Beispiel nur sehr wenige Instruktionen hat. Ein Prozent w\u00fcrde weniger als eine Instruktion entsprechen.<\/p>\n<h1>GCC Vector Extensions<\/h1>\n<p>GCC bietet mit Vector Extension die M\u00f6glichkeit SIMD Register direkt zu nutzen. Einfache Operationen wie Addition, Multiplikation sind darauf definiert. Damit l\u00e4sst sich eine einfache Vector Klasse erstellen die es erm\u00f6glicht, lesbaren Code zu schreiben. Zusammen mit -ftree-vectorize ergibt sich die bestm\u00f6gliche Abdeckung an Vektorizationen.<\/p>\n<p>Das Scalarprodukt ist in Zeilen 5-16 makiert.<\/p>\n<table>\n<tr>\n<td>\nGCC -O2 -ftree-vectorize<\/p>\n<pre class=\"brush: cpp; highlight: [9]; title: ; notranslate\" title=\"\">\r\nfloat computeFluxes(const Vec4D&amp; cellLeft, const float  *alphaleft,\r\n                    const Vec2D&amp; leftcellCenters, const Vec2D&amp; edgeCenters,\r\n                    const std::array&lt;Vec2D,4&gt;&amp; leftGradient\r\n) {\r\n    Vec4D leftCellValues = cellLeft;\r\n    \r\n    Vec2D dxyl = edgeCenters - leftcellCenters;\r\n\r\n    leftCellValues += alphaleft&#x5B;0] * dot_product(dxyl, leftGradient);\r\n \r\n    return leftCellValues&#x5B;0] + leftCellValues&#x5B;1] + leftCellValues&#x5B;2] + leftCellValues&#x5B;3]; \r\n}\r\n\r\n\r\n<\/pre>\n<\/td>\n<td>\n<pre class=\"brush: plain; highlight: [5,6,7,8,9,10,11,12,13,14,15,16]; title: ; notranslate\" title=\"\">\r\ncomputeFluxes(Vec4D const&amp;, float const*, Vec2D const&amp;, Vec2D const&amp;, std::array&lt;Vec2D, 4ul&gt; const&amp;):\r\n        movq    (%rdx), %xmm1\r\n        movq    (%rcx), %xmm0\r\n        subps   %xmm1, %xmm0\r\n        movups  (%r8), %xmm2\r\n        movups  16(%r8), %xmm4\r\n        movaps  %xmm2, %xmm1\r\n        shufps  $221, %xmm4, %xmm2\r\n        shufps  $136, %xmm4, %xmm1\r\n        movaps  %xmm0, %xmm3\r\n        shufps  $0xe5, %xmm0, %xmm0\r\n        shufps  $0, %xmm0, %xmm0\r\n        shufps  $0, %xmm3, %xmm3\r\n        mulps   %xmm0, %xmm2\r\n        mulps   %xmm3, %xmm1\r\n        addps   %xmm2, %xmm1\r\n        movss   (%rsi), %xmm0\r\n        shufps  $0, %xmm0, %xmm0\r\n        mulps   %xmm0, %xmm1\r\n        addps   (%rdi), %xmm1\r\n        movaps  %xmm1, %xmm0\r\n        movaps  %xmm1, %xmm2\r\n        shufps  $85, %xmm1, %xmm0\r\n        addss   %xmm1, %xmm0\r\n        unpckhps        %xmm1, %xmm2\r\n        shufps  $255, %xmm1, %xmm1\r\n        addss   %xmm2, %xmm0\r\n        addss   %xmm1, %xmm0\r\n        ret\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<h1>Vektorisieren mit -O2 -ftree-vectorize Original Code<\/h1>\n<p>Das erste Listening zeigt auf der linken Seite die zu untersuchenden Codezeilen. Auf der rechten Seite die erzeugte ASM Ausabe. Die verwendeten ASM Befehle movss, addss, subss, mulss bedeuten: Kopieren, Addieren, Subtrahieren, Multiplizieren. Jeweils mit dem K\u00fcrzel \"ss\" welches man sich als \"scalar single (precision)\" merken kann. Die vektorisierte Variante w\u00e4re dementsprechend \"ps\" f\u00fcr \"packed single (precision)\".<\/p>\n<p>Es f\u00e4llt auf, dass keine \"ps\" Befehle vorkommen. Auch mit eingeschalteten Vektorisierer mittels -O3 oder -ftree-vectorize werden keine \"ps\" ASM Befehle erzeugt, da schlicht keine Schleifen im Code vorhanden sind, die vektorisiert werden k\u00f6nnen.<\/p>\n<p>Die beispielhaft markierten Zeilen 11 und 12 im C++ Code entsprechend den ASM Befehlen 2-5. <\/p>\n<table>\n<tr>\n<td>\nGCC -O2<\/p>\n<pre class=\"brush: cpp; highlight: [11,12]; title: ; notranslate\" title=\"\">\r\nfloat computeFluxes(const float *cellLeft, const float *alphaleft,\r\n                    const float *leftcellCenters, const float *edgeCenters,\r\n                    const float *leftGradient\r\n) {\r\n    float leftCellValues&#x5B;4];\r\n    leftCellValues&#x5B;0] = cellLeft&#x5B;0];\r\n    leftCellValues&#x5B;1] = cellLeft&#x5B;1];\r\n    leftCellValues&#x5B;2] = cellLeft&#x5B;2];\r\n    leftCellValues&#x5B;3] = cellLeft&#x5B;3];\r\n\r\n    float dxl = (edgeCenters&#x5B;0] - leftcellCenters&#x5B;0]);\r\n    float dyl = (edgeCenters&#x5B;1] - leftcellCenters&#x5B;1]);\r\n\r\n    leftCellValues&#x5B;0] += alphaleft&#x5B;0] * ((dxl * leftGradient&#x5B;0])+(dyl * leftGradient&#x5B;1]));\r\n    leftCellValues&#x5B;1] += alphaleft&#x5B;0] * ((dxl * leftGradient&#x5B;2])+(dyl * leftGradient&#x5B;3]));\r\n    leftCellValues&#x5B;2] += alphaleft&#x5B;0] * ((dxl * leftGradient&#x5B;4])+(dyl * leftGradient&#x5B;5]));\r\n    leftCellValues&#x5B;3] += alphaleft&#x5B;0] * ((dxl * leftGradient&#x5B;6])+(dyl * leftGradient&#x5B;7]));\r\n\r\n    return leftCellValues&#x5B;0] + leftCellValues&#x5B;1] + leftCellValues&#x5B;2] + leftCellValues&#x5B;3]; \r\n}\r\n<\/pre>\n<\/td>\n<td>\n<pre class=\"brush: plain; highlight: [2,3,4,5]; title: ; notranslate\" title=\"\">\r\ncomputeFluxes(float const*, float const*, float const*, float const*, float const*):\r\n        movss   (%rcx), %xmm1\r\n        movss   4(%rcx), %xmm2\r\n        subss   (%rdx), %xmm1\r\n        subss   4(%rdx), %xmm2\r\n        movss   (%r8), %xmm0\r\n        movss   4(%r8), %xmm3\r\n        movss   12(%r8), %xmm5\r\n        movss   (%rsi), %xmm4\r\n        mulss   %xmm2, %xmm3\r\n        mulss   %xmm1, %xmm0\r\n        mulss   %xmm2, %xmm5\r\n        addss   %xmm3, %xmm0\r\n        movss   8(%r8), %xmm3\r\n        mulss   %xmm1, %xmm3\r\n        mulss   %xmm4, %xmm0\r\n        addss   (%rdi), %xmm0\r\n        addss   %xmm5, %xmm3\r\n        movss   20(%r8), %xmm5\r\n        mulss   %xmm2, %xmm5\r\n        mulss   %xmm4, %xmm3\r\n        addss   4(%rdi), %xmm3\r\n        mulss   28(%r8), %xmm2\r\n        addss   %xmm3, %xmm0\r\n        movss   16(%r8), %xmm3\r\n        mulss   %xmm1, %xmm3\r\n        mulss   24(%r8), %xmm1\r\n        addss   %xmm5, %xmm3\r\n        addss   %xmm2, %xmm1\r\n        mulss   %xmm4, %xmm3\r\n        addss   8(%rdi), %xmm3\r\n        mulss   %xmm4, %xmm1\r\n        addss   12(%rdi), %xmm1\r\n        addss   %xmm3, %xmm0\r\n        addss   %xmm1, %xmm0\r\n        ret\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<h1>-O2 -ftree-vectorize H\u00e4ndisch Schleifen eingef\u00fcgt<\/h1>\n<p>Die markierten Zeilen im ersten Listening 1 werden durch eine Schleife ersetzt und mittels -ftree-vectorize vektorisiert. Dabei fiel auf, dass der Vektorisierer erst ab einer Schleifenl\u00e4nge von 3 aktiv wird. Ob dies eine Compiler Einstellung oder eine allgemeine Beschr\u00e4nkung von SIMD ist, ist nicht bekannt.<br \/>\nDie nun markierten Zeilen 2-4, insbesondere Zeile 4, zeigt nun die \"ps\" Version der Subtraktion.<\/p>\n<p>Die Zeilen 16-19 zeigen eine weitere M\u00f6glichkeit zum Vektorisieren.<\/p>\n<table>\n<tr>\n<td>\nGCC -O2 -ftree-vectorize<\/p>\n<pre class=\"brush: cpp; highlight: [11,12,13,14,16,17,18,19]; title: ; notranslate\" title=\"\">\r\nfloat computeFluxes(const float *cellLeft, const float *alphaleft,\r\n                    const float *leftcellCenters, const float *edgeCenters,\r\n                    const float *leftGradient\r\n) {\r\n    float leftCellValues&#x5B;4];\r\n    leftCellValues&#x5B;0] = cellLeft&#x5B;0];\r\n    leftCellValues&#x5B;1] = cellLeft&#x5B;1];\r\n    leftCellValues&#x5B;2] = cellLeft&#x5B;2];\r\n    leftCellValues&#x5B;3] = cellLeft&#x5B;3];\r\n\r\n    float dxyl&#x5B;3];\r\n    for(int i=0; i &lt; 3; ++i) {\r\n        dxyl&#x5B;i] = (edgeCenters&#x5B;i] - leftcellCenters&#x5B;i]);\r\n    }\r\n    \r\n    leftCellValues&#x5B;0] += alphaleft&#x5B;0] * ((dxyl&#x5B;0] * leftGradient&#x5B;0])+(dxyl&#x5B;1] * leftGradient&#x5B;1]));\r\n    leftCellValues&#x5B;1] += alphaleft&#x5B;0] * ((dxyl&#x5B;0] * leftGradient&#x5B;2])+(dxyl&#x5B;1] * leftGradient&#x5B;3]));\r\n    leftCellValues&#x5B;2] += alphaleft&#x5B;0] * ((dxyl&#x5B;0] * leftGradient&#x5B;4])+(dxyl&#x5B;1] * leftGradient&#x5B;5]));\r\n    leftCellValues&#x5B;3] += alphaleft&#x5B;0] * ((dxyl&#x5B;0] * leftGradient&#x5B;6])+(dxyl&#x5B;1] * leftGradient&#x5B;7]));\r\n \r\n    return leftCellValues&#x5B;0] + leftCellValues&#x5B;1] + leftCellValues&#x5B;2] + leftCellValues&#x5B;3]; \r\n}\r\n<\/pre>\n<\/td>\n<td>\n<pre class=\"brush: plain; highlight: [2,3,4]; title: ; notranslate\" title=\"\">\r\ncomputeFluxes(float const*, float const*, float const*, float const*, float const*):\r\n        movq    (%rdx), %xmm0\r\n        movq    (%rcx), %xmm2\r\n        subps   %xmm0, %xmm2\r\n        movss   4(%r8), %xmm3\r\n        movss   12(%r8), %xmm5\r\n        movss   (%r8), %xmm0\r\n        movss   (%rsi), %xmm4\r\n        movaps  %xmm2, %xmm1\r\n        shufps  $0xe5, %xmm2, %xmm2\r\n        mulss   %xmm1, %xmm0\r\n        mulss   %xmm2, %xmm3\r\n        mulss   %xmm2, %xmm5\r\n        addss   %xmm3, %xmm0\r\n        movss   8(%r8), %xmm3\r\n        mulss   %xmm1, %xmm3\r\n        mulss   %xmm4, %xmm0\r\n        addss   (%rdi), %xmm0\r\n        addss   %xmm5, %xmm3\r\n        movss   20(%r8), %xmm5\r\n        mulss   %xmm2, %xmm5\r\n        mulss   %xmm4, %xmm3\r\n        addss   4(%rdi), %xmm3\r\n        mulss   28(%r8), %xmm2\r\n        addss   %xmm3, %xmm0\r\n        movss   16(%r8), %xmm3\r\n        mulss   %xmm1, %xmm3\r\n        mulss   24(%r8), %xmm1\r\n        addss   %xmm5, %xmm3\r\n        addss   %xmm2, %xmm1\r\n        mulss   %xmm4, %xmm3\r\n        addss   8(%rdi), %xmm3\r\n        mulss   %xmm4, %xmm1\r\n        addss   12(%rdi), %xmm1\r\n        addss   %xmm3, %xmm0\r\n        addss   %xmm1, %xmm0\r\n        ret\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<p>Die Zeilen 16-19 im 2. Listening wurde durch eine Schleife ersetzt. Diese entsprechen jetzt Zeile 16-18 im 3. Listening. Die Schleife wurde vektorisiert, wie im ASM Output Zeile 5-22 zu sehen ist.<br \/>\nEs wurden einige shufps (shuffel packed single precision) Befehle erzeugt, um die Werte innerhalb eines SIMD Registers zu tauschen. Die erzeugte ASM Ausabe besteht nun haupts\u00e4chlich aus \"ps\" Befehlen und ist hiermit vektorisiert.<\/p>\n<table>\n<tr>\n<td>\nGCC -O2 -ftree-vectorize<\/p>\n<pre class=\"brush: cpp; highlight: [16,17,18]; title: ; notranslate\" title=\"\">\r\nfloat computeFluxes(const float *cellLeft, const float *alphaleft,\r\n                    const float *leftcellCenters, const float *edgeCenters,\r\n                    const float *leftGradient\r\n) {\r\n    float leftCellValues&#x5B;4];\r\n    leftCellValues&#x5B;0] = cellLeft&#x5B;0];\r\n    leftCellValues&#x5B;1] = cellLeft&#x5B;1];\r\n    leftCellValues&#x5B;2] = cellLeft&#x5B;2];\r\n    leftCellValues&#x5B;3] = cellLeft&#x5B;3];\r\n\r\n    float dxyl&#x5B;3];\r\n    for(int i=0; i &lt; 3; ++i) {\r\n        dxyl&#x5B;i] = (edgeCenters&#x5B;i] - leftcellCenters&#x5B;i]);\r\n    }\r\n\r\n    for(int i=0; i &lt; 4; ++i) {\r\n        leftCellValues&#x5B;i] += alphaleft&#x5B;0] * ((dxyl&#x5B;0] * leftGradient&#x5B;i*2])+(dxyl&#x5B;1] * leftGradient&#x5B;i*2+1]));\r\n    }\r\n \r\n    return leftCellValues&#x5B;0] + leftCellValues&#x5B;1] + leftCellValues&#x5B;2] + leftCellValues&#x5B;3]; \r\n}\r\n<\/pre>\n<\/td>\n<td>\n<pre class=\"brush: plain; highlight: [5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22]; title: ; notranslate\" title=\"\">\r\ncomputeFluxes(float const*, float const*, float const*, float const*, float const*):\r\n        movq    (%rdx), %xmm1\r\n        movq    (%rcx), %xmm0\r\n        subps   %xmm1, %xmm0\r\n        movups  (%r8), %xmm2\r\n        movups  16(%r8), %xmm4\r\n        movaps  %xmm2, %xmm1\r\n        shufps  $136, %xmm4, %xmm2\r\n        shufps  $221, %xmm4, %xmm1\r\n        movaps  %xmm0, %xmm3\r\n        shufps  $0xe5, %xmm0, %xmm0\r\n        shufps  $0, %xmm0, %xmm0\r\n        mulps   %xmm0, %xmm1\r\n        movaps  %xmm3, %xmm0\r\n        shufps  $0, %xmm0, %xmm0\r\n        mulps   %xmm0, %xmm2\r\n        movss   (%rsi), %xmm0\r\n        shufps  $0, %xmm0, %xmm0\r\n        addps   %xmm2, %xmm1\r\n        mulps   %xmm0, %xmm1\r\n        movups  (%rdi), %xmm0\r\n        addps   %xmm0, %xmm1\r\n        movaps  %xmm1, %xmm0\r\n        movaps  %xmm1, %xmm2\r\n        shufps  $85, %xmm1, %xmm0\r\n        addss   %xmm1, %xmm0\r\n        unpckhps        %xmm1, %xmm2\r\n        shufps  $255, %xmm1, %xmm1\r\n        addss   %xmm2, %xmm0\r\n        addss   %xmm1, %xmm0\r\n        ret\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<h1>Vektorisieren mit OpenMP<\/h1>\n<p>OpenMP besitzt seit Version 4.0 die SIMD Direktive welche ebenfalls \"ps\" ASM Befehle erzeugen kann. Listenin 4 zeigt die OpenMP Version mit entsprechenden ASM Output.<\/p>\n<p>Die Vektorisierung der ersten Schleife funktioniert nun auch mit einer L\u00e4nge von 2 statt 3, wie in Zeile 10 des ASM Output zu sehen ist.<br \/>\nDie Vektorisierung der zweiten Schleife schl\u00e4gt allerdings fehl. Zu erkennen an den \"ss\" Instruktionen und dem Label .L2 welcher der R\u00fccksprungort der for() Schleife ist.<\/p>\n<table>\n<tr>\n<td>\nGCC -O2 -fopenmp<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\nfloat computeFluxes(const float *cellLeft, const float *alphaleft,\r\n                    const float *leftcellCenters, const float *edgeCenters,\r\n                    const float *leftGradient\r\n) {\r\n    float leftCellValues&#x5B;4];\r\n    leftCellValues&#x5B;0] = cellLeft&#x5B;0];\r\n    leftCellValues&#x5B;1] = cellLeft&#x5B;1];\r\n    leftCellValues&#x5B;2] = cellLeft&#x5B;2];\r\n    leftCellValues&#x5B;3] = cellLeft&#x5B;3];\r\n\r\n    float dxyl&#x5B;2];\r\n#pragma omp simd    \r\n    for(int i=0; i &lt; 2; ++i) {\r\n        dxyl&#x5B;i] = (edgeCenters&#x5B;i] - leftcellCenters&#x5B;i]);\r\n    }\r\n\r\n#pragma omp simd        \r\n    for(int i=0; i &lt; 4; ++i) {\r\n        leftCellValues&#x5B;i] += alphaleft&#x5B;0] * ((dxyl&#x5B;0] * leftGradient&#x5B;i*2])+(dxyl&#x5B;1] * leftGradient&#x5B;i*2+1]));\r\n    }\r\n    \r\n    return leftCellValues&#x5B;0] + leftCellValues&#x5B;1] + leftCellValues&#x5B;2] + leftCellValues&#x5B;3]; \r\n}\r\n<\/pre>\n<\/td>\n<td>\n<pre class=\"brush: plain; highlight: [10]; title: ; notranslate\" title=\"\">\r\ncomputeFluxes(float const*, float const*, float const*, float const*, float const*):\r\n        movss   4(%rdi), %xmm0\r\n        movq    (%rcx), %xmm1\r\n        movq    %rdi, %rax\r\n        movss   (%r8), %xmm2\r\n        movss   (%rsi), %xmm3\r\n        movss   %xmm0, -20(%rsp)\r\n        movq    (%rdx), %xmm0\r\n        movq    8(%rdi), %rdi\r\n        subps   %xmm0, %xmm1\r\n        movss   4(%r8), %xmm0\r\n        movq    %rdi, -16(%rsp)\r\n        movaps  %xmm1, %xmm4\r\n        shufps  $0xe5, %xmm1, %xmm1\r\n        mulss   %xmm4, %xmm2\r\n        mulss   %xmm1, %xmm0\r\n        addss   %xmm2, %xmm0\r\n        mulss   %xmm3, %xmm0\r\n        addss   (%rax), %xmm0\r\n        movl    $1, %eax\r\n        movss   %xmm0, -24(%rsp)\r\n.L2:\r\n        movss   (%r8,%rax,8), %xmm0\r\n        movss   4(%r8,%rax,8), %xmm2\r\n        mulss   %xmm4, %xmm0\r\n        mulss   %xmm1, %xmm2\r\n        addss   %xmm2, %xmm0\r\n        mulss   %xmm3, %xmm0\r\n        addss   -24(%rsp,%rax,4), %xmm0\r\n        movss   %xmm0, -24(%rsp,%rax,4)\r\n        addq    $1, %rax\r\n        cmpq    $4, %rax\r\n        jne     .L2\r\n        movss   -24(%rsp), %xmm0\r\n        addss   -20(%rsp), %xmm0\r\n        addss   -16(%rsp), %xmm0\r\n        addss   -12(%rsp), %xmm0\r\n        ret\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<h1>Anhang<\/h1>\n<p>Die f\u00fcr Vector Klasse f\u00fcr die GCC Vector Extensions Variante.<\/p>\n<pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\r\n#include &lt;array&gt;\r\n\r\nstruct Vec2D {\r\n    typedef float vec_type __attribute__ ((vector_size (2*sizeof(float))));\r\n    vec_type _data;\r\n\r\n    const float&amp; operator&#x5B;](int i) const {\r\n        return _data&#x5B;i];\r\n    }   \r\n};\r\n\r\ninline Vec2D operator-(const Vec2D&amp; lhs, const Vec2D&amp; rhs) {\r\n    return Vec2D{lhs._data - rhs._data};\r\n}\r\n\r\nstruct Vec4D {\r\n    typedef float vec_type __attribute__ ((vector_size (4*sizeof(float))));\r\n    vec_type _data;\r\n\r\n    float&amp; operator&#x5B;](int i) {\r\n        return _data&#x5B;i];\r\n    }\r\n};\r\n\r\ninline Vec4D&amp; operator+=(Vec4D&amp; lhs, const Vec4D&amp; rhs) {\r\n    lhs._data += rhs._data;\r\n    return lhs;\r\n}\r\n\r\ninline Vec4D operator*(const float lhs, const Vec4D&amp; rhs) {\r\n    return Vec4D{lhs * rhs._data};\r\n}\r\n\r\ninline Vec4D dot_product(const Vec2D&amp; v1, const std::array&lt;Vec2D,4&gt;&amp; v2) {\r\n    Vec4D result;\r\n    for(int i=0; i &lt; 4; ++i) {\r\n        result&#x5B;i] = ((v1&#x5B;0] * v2&#x5B;i]&#x5B;0]) + (v1&#x5B;1] * v2&#x5B;i]&#x5B;1]));\r\n    }\r\n    return result;\r\n}\r\n<\/pre>\n<p>[1] https:\/\/github.com\/reguly\/volna\/blob\/master\/sp\/computeFluxes.h<br \/>\n[2] https:\/\/gmd.copernicus.org\/articles\/11\/4621\/2018\/<br \/>\n[3] https:\/\/godbolt.org\/<br \/>\n[4] https:\/\/gcc.gnu.org\/onlinedocs\/gcc-11.2.0\/gcc\/Optimize-Options.html#index-ftree-vectorize<br \/>\n[5] https:\/\/gcc.gnu.org\/onlinedocs\/gcc\/Vector-Extensions.html<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Als Beispiel sollen ein paar reduzierte Zeilen Code aus der Datei computeFluxes.h [1] aus dem Vplna-OP2 [2] Projekt dienen, welche auf unterschiedliche Arten vektorisiert werden sollen. Um so nah wie m\u00f6glich am original Code zu bleiben, werden die Pointer Argumente der Funktionen, welche Arrays darstellen, wo es m\u00f6glich ist nicht durch einen passerenden Typen ersetzt. [&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-4967","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\/4967","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=4967"}],"version-history":[{"count":16,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/4967\/revisions"}],"predecessor-version":[{"id":5050,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=\/wp\/v2\/posts\/4967\/revisions\/5050"}],"wp:attachment":[{"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=4967"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=4967"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/roboblog.fatal-fury.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=4967"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}