Übung 8: Parallelisierung mit SIMD (Stefan Bosse) [8.2025]

Parallelisierung mit SIMD

In dieser Übung soll eine Einführung in die Nutzung von Vektoroperationen erfolgen. Vektoroperationen erfüllen das Single-Instruction Multiple-Data Stream Paradigma. Bei SIMD werden die gleiche (i.A. arithmetische, Boolesche, logische oder relationale) Operation auf verschiedene Daten gleichzeitig angewendet. Die Daten müssen homogen sein, daher die Anwendung auf Vektoren.

Die Lua Virtual Machine (lvm) bot bisher Parallelität auf Kontrollpfadebene. Schon bei der Parallelisierung wurde deutlich dass zusätzliche Kommunikation und Nachrichten die mögliche Beschleunigung teils deutlich reduzieren kann. D.h. man parallele Programm sinnvoll gestalten um einen Vorteil durch (physische) Parallelisierung zu bekommen. Bei der Datenpfadparalleliät wird diese Randbedingung nochmals verschärft. Es muss genauestens der zusätzliche Aufwand - wie z.B. das Kopieren von Daten von einem Puffer in einen anderen - bedacht und analysiert werden.

Die modernen (Dekstop und Server) Mikroprozessoren stellen Vektoroperationen zur Verfügung, die in (C) Programmen direkt über den Compiler genutzt werden können.

Je ach verfügbarer Prozessorerweiterung gibt es unterschiedlich breite Vektordatenformate, i.A. 128 und 256 Bit, mittlerweile auch 512 Bits. Die Datenwortbreite ist immer gleich. Es werden verschiedene Datentypen der Vektorelemente unterstützt, i.A. 16 und 32 Bit Integer, 32 und 64 Bit Fliesskomma (float und double Datentypen).

#label

Die Vektoroperationen arbeiten auf (speicherbasierten) Registern fester Breite. Beispiele in C sind:

#include <immintrin.h>
__v4sf x4; // 4 x Single Precisio Float
__v8sf x8; // 8 x Single Precision Float
c=__mm_add_ps(a,b)

Lua verarbeitet i.A. Daten mit doppelter Float Präzision (double), Arrays sind heterogen und können nicht direkt für Vektoroperationen genutzt werden. Es gibt aber das Array Module welches typisierte und gepackte Arrays unterstützt. Diese wird hier verwendet.

Die Verwendung der Vektoroperationen in C/C++ Programmen kann direkt erfolgen. Bei einer VM muss es aber zunächst eine C-Schnittstelle geben, d.h. z.B. eine Lua Funktion ruft eine C Funktion über eine durch die VM definierte API auf. Dieser Aufruf ist kostenintensiv!

Ein typischer Lua Aufruf einer C Funktion:

#include "lua.h"
#include "lauxlib.h"

int foo(ua_State *L ) {
  int arg1 = luaL_checkint( L, 1 ),
      arg2 = luaL_checkint( L, 2 );
  lua_pushnumber( L, 1234 );
  return 1;
}

Die Ein- und Ausgabewerte werden über den Stack übergeben. Der Rückgabewert einer Lua-C Funktion gibt die Anzahl der Elemente auf dem Stack als Rückgabe an.

Man muss dann ein binäres Modul (Shared Object Library) name.so erzeugen und kann diese dann mittels require name laden. Unten ist ein Beispiel gezeigt.

Frage 1. Wann würden sich wohl Vektoroperationen für ein Lua Programm lohnen? Welche Eigenschaften müsste ein Algorithmus haben der mit Vektoroperationen beschleunigt werden soll?


Nachfolgend ist ein Beispielmodul in C gezeigt welches einfache Vektoroperationen in Lua zur Verfügung stellt. Es gibt Operationen die auf einzelnen Vektoren operieren und welche die auf Arrays in multiplen Vielfachen der Vektoren operieren (also eine Batchverarbeitung).

Aufgabe 2. Untersuche die parallelen Einzeloperationen (z.B. v4add) im Vergleich zur reinen Lua Berechnung und der sequenziellen C Implementierung (z.B. v4addS). Ist eine Beschleunigung erzielbar, wo liegt die "Reibung"?


128 Bit Vektoroperationen

C Code (simd.c) für 128 Bit Vektoroperationen

Lua Code (test.lua) für 128 Bit Vektoroperationen

In der nachfolgenden Shell (temporärer Ordner) kann (auf dem eigene Rechner außerhalb des Web Browsers) die Lua-C Module kompiliert (make) und ausgeführt/getestet werden (run). Die Quellkodedateien (s.o.) werden automatisch synchronisiert. Derzeit nur unter Linux ausführbar! Lokal muss wex (min. Version 1.10) gestartet werden.

Compile & Test
Type help or hit TAB for a list of commands.

Aufgabe 3. Jetzt untersuche die Array-basierte v4prodsum Funktion, wähle die Größe der Arrays (float) groß genug um einen Effekt zu sehen. Welche typische Beschleunigung (unter Berücksichtigung des Lua-C Overhwads) kann erzielt werden?


256 Bit Vektoroperationen

Jetzt kommt ein Problem aus uns zu. Wenn man obigen Code einfach mit __v8sf und den entsprechenden 256 Bit Operationen __mm256_xxx ersetzt wird es bei der Ausführung zu Speicherfehlern kommen. Der Grund: Die Vektoren müssen aus Effizienzgründen im Speicher ausgerichtet sein. Bei __v4sf reichen 16 Byte Ausrichtungen, d.h. addr modulo 16 = 0, bei __v8sf wird aber eine Ausrichtung von 32 Byte gefordert, d.h. addr modulo 32 = 0.

C Code (simd.c) für 256 Bit Vektoroperationen

Lua Code (test.lua) für 256 Bit Vektoroperationen

Compile & Test
Type help or hit TAB for a list of commands.


Created by the NoteBook Compiler Ver. 1.38.1 (c) Dr. Stefan Bosse (Tue Aug 12 2025 15:51:55 GMT+0200 (Central European Summer Time))