2017-01-22 32 views
2

Я только начинаю путь использования simd intrinsics. Мой профилировщик показал, что значительное количество времени тратится на интерполяцию вершин. Я нацелен на AVX2 и пытаюсь найти оптимизацию для следующего - учитывая, что у меня есть 3 vector2s, которые нуждаются в интерполяции, я предполагаю, что я могу загрузить их в один __m256 и умножить и добавить эффективно. Вот код, который я пытаюсь преобразовать - стоит ли это делать как 256-битную операцию? Векторы не выровнены.Барицентрическая интерполяция AVX2 вершинного компонента

Vector2 Interpolate(Vector3 uvw, Vector2 v0, Vector2 v1, Vector2 v2) 
{ 
    Vector2 out; 
    out = v0 * uvw.x; 
    out += v1 * uvw.y; 
    out += v2 * uvw.z; 

    return out; 
} 

struct Vector2 { float x; float y; } ; 
struct Vector3 { float x; float y; float z; } ; 

Мой вопрос заключается в следующем - как мне загрузить три невыровненное Vector2 в один регистр 256bit, так что я могу сделать умножение и добавить?

Я использую VS2013.

+2

Слишком много раздражающего перемещения данных было бы необходимо. Если вы передали вершины (все те, которые нуждаются в интерполяции, а не только 3) и коэффициенты масштабирования в виде массивов, вы могли бы написать разумный код. – harold

+0

@harold. Сколько элементов за один раз стоило бы? 16 комплектов? 256 наборов? – Steven

+2

Как насчет 'struct Vector2_block {float8 x; float8 y; }; 'и' struct Vector3_block {float8 x; float8 y; float8 z; }; 'и затем вы работаете с 8 вершинами одновременно. –

ответ

1

мне было скучно, так что я написал, не проверял (но скомпилирован, как Clang и 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 сериализуют умножения без необходимости, но это не имеет значения, поскольку оно не является частью зависимой от цикла связи.

Прогнозируемая пропускная способность этого (при достаточно малом массиве) составляет 2 итерации за 9 циклов, узких мест в нагрузках. На практике, вероятно, немного хуже, были некоторые разговоры о простых магазинах, крадущих р2 или р3 изредка, такого рода вещи, я не совсем уверен. Во всяком случае, достаточно времени для 18 «FMA», но есть только 12 (8 и 4 мульпа), поэтому может быть полезно переместить некоторые дополнительные вычисления здесь, если они есть.

+0

Спасибо - я попробую, когда я смогу перенастроить исходные данные в потоки. Прямо сейчас raytracer действительно настроен на результат «на луч». – Steven

+0

Это, как правило, то, что я закончил делать - я переписал код, чтобы создать сразу восемь лучей, а затем сделал все, что соответствовало этому. Он работает по большей части, но есть несколько дорогостоящих операций, в которых я беру коллекцию удаленных объектов с разрозненными данными и должен их перетасовывать, но это еще один вопрос. Еще раз спасибо. – Steven

 Смежные вопросы

  • Нет связанных вопросов^_^