我很無聊,所以我寫的,沒有測試過(但是編譯,既鏘和GCC做出合理的代碼本)
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);
}
}
使用z玻色子的格式,如果我理解正確了。無論如何,從SIMD的角度來看,這是一個很好的格式。從C++的角度來看,這有點不方便。
FMAs會不必要地序列化乘法,但這應該不重要,因爲它不是循環承載的依賴項的一部分。
預測的吞吐量(假設一個足夠小的陣列)是每9個週期2次迭代,由負載瓶頸。在實踐中可能會稍微惡化一些,有些人談論簡單的商店偶爾會偷盜p2或p3,但我不太確定。無論如何,對於18個「FMA」來說足夠的時間,但只有12個(8和4個mulps),所以如果有的話,在這裏移動一些額外的計算可能是有用的。
太多煩人的數據移動將是必要的。如果你通過頂點(所有那些需要內插的頂點,不只是3個)和縮放因子作爲數組,你實際上可以編寫合理的代碼 – harold
@harold一次有多少個元素會使它值得呢? 16套? 256套? – Steven
'struct Vector2_block {float8 x; float8 y; };'和'struct Vector3_block {float8 x; float8 y; float8 z; };'然後你一次操作8個頂點。 –