Mir war langweilig, so ich es geschrieben habe, nicht getestet (aber kompiliert, die beide Clang und GCC aus dieser vernünftigen Code machen)
void interpolateAll(int n, float* scales, float* vin, float* vout)
{
// preconditions:
// (n & 7 == 0) (not really, but vout must be padded)
// scales & 31 == 0
// vin & 31 == 0
// vout & 31 == 0
// vin format:
// float v0x[8]
// float v0y[8]
// float v1x[8]
// float v1y[8]
// float v2x[8]
// float v2y[8]
// scales format:
// float scale0[8]
// float scale1[8]
// float scale2[8]
// vout format:
// float vx[8]
// float vy[8]
for (int i = 0; i < n; i += 8) {
__m256 scale_0 = _mm256_load_ps(scales + i * 3);
__m256 scale_1 = _mm256_load_ps(scales + i * 3 + 8);
__m256 scale_2 = _mm256_load_ps(scales + i * 3 + 16);
__m256 v0x = _mm256_load_ps(vin + i * 6);
__m256 v0y = _mm256_load_ps(vin + i * 6 + 8);
__m256 v1x = _mm256_load_ps(vin + i * 6 + 16);
__m256 v1y = _mm256_load_ps(vin + i * 6 + 24);
__m256 v2x = _mm256_load_ps(vin + i * 6 + 32);
__m256 v2y = _mm256_load_ps(vin + i * 6 + 40);
__m256 x = _mm256_mul_ps(scale_0, v0x);
__m256 y = _mm256_mul_ps(scale_0, v0y);
x = _mm256_fmadd_ps(scale_1, v1x, x);
y = _mm256_fmadd_ps(scale_1, v1y, y);
x = _mm256_fmadd_ps(scale_2, v2x, x);
y = _mm256_fmadd_ps(scale_2, v2y, y);
_mm256_store_ps(vout + i * 2, x);
_mm256_store_ps(vout + i * 2 + 8, y);
}
}
Verwendet Format der Z-Boson, wenn ich ihn richtig verstanden. In jedem Fall ist es ein schönes Format, aus einer SIMD-Perspektive. Etwas unbequem aus einer C++ Perspektive.
Die FMAs serialisieren die Multiplikationen unnötigerweise, aber das sollte keine Rolle spielen, da sie nicht Teil einer Loop-getragenen Abhängigkeit ist.
Der vorhergesagte Durchsatz (unter der Annahme einer ausreichend kleinen Array) ist 2 Iterationen pro 9 Zyklen, Engpässe durch die Lasten. In der Praxis wahrscheinlich etwas schlechter, es gab einige Gespräche über einfache Läden stehlen gelegentlich p2 oder p3, so etwas, ich bin mir nicht wirklich sicher. Wie auch immer, das ist genug Zeit für 18 "FMAs", aber es gibt nur 12 (8 und 4 Mulps), so dass es nützlich sein kann, hier ein paar zusätzliche Berechnungen vorzunehmen, wenn es welche gibt.
Zu viel lästige Datenbewegung wäre notwendig. Wenn Sie die Scheitelpunkte (all jene, die interpolieren müssen, nicht nur 3) und die Skalierungsfaktoren als Arrays übergeben, könnten Sie tatsächlich vernünftigen Code schreiben – harold
@harold Wie viele Elemente zu einer Zeit würde es sich lohnen? 16 Sätze? 256 Sätze? – Steven
Wie wäre es mit 'struct Vector2_block {float8 x; float8 y; }; 'und' struct Vector3_block {float8 x; float8 y; float8 z; }; 'und dann operierst du 8 Eckpunkte gleichzeitig. –