Как избежать неправильной загрузки памяти с помощью SIMD-инструкций
Я загружаю элементы из памяти, используя инструкции загрузки SIMD, скажем, используя Altivec, предполагая, что выровненные адреса:
float X[SIZE];
vector float V0;
unsigned FLOAT_VEC_SIZE = sizeof(vector float);
for (int load_index =0; load_index < SIZE; load_index+=FLOAT_VEC_SIZE)
{
V0 = vec_ld(load_index, X);
/* some computation involving V0*/
}
Теперь, если SIZE не кратен FLOAT_VEC_SIZE, возможно, что V0 содержит некоторые недопустимые элементы памяти в последней итерации цикла. Один из способов избежать этого - сократить цикл на одну итерацию, другой - замаскировать потенциально недопустимые элементы. Есть ли здесь еще один полезный прием? Учитывая вышесказанное, внутреннее большинство в наборе вложенных циклов. Таким образом, любая дополнительная инструкция без SIMD будет сопровождаться снижением производительности!
2 ответа
В идеале вы должны дополнить ваш массив кратным vec_step(vector float)
(т.е. несколько из 4 элементов), а затем замаскировать любые дополнительные нежелательные значения из обработки SIMD или использовать скалярный код для работы с последними несколькими элементами, например
const INT VF_ELEMS = vec_step(vector float);
const int VEC_SIZE = (SIZE + VF_ELEMS - 1) / VF_ELEMS; // number of vectors in X, rounded up
vector float VX[VEC_SIZE]; // padded array with 16 byte alignment
float *X = = (float *)VX; // float * pointer to base of array
for (int i = 0; i <= SIZE - VF_ELEMS; i += VF_ELEMS)
{ // for each full SIMD vector
V0 = vec_ld(0, &X[i]);
/* some computation involving V0 */
}
if (i < SIZE) // if we have a partial vector at the end
{
#if 1 // either use SIMD and mask out the unwanted values
V0 = vec_ld(0, &X[i]);
/* some SIMD computation involving partial V0 */
#else // or use a scalar loop for the remaining 1..3 elements
/* small scalar loop to handle remaining points */
#endif
}
Иногда заполнение нулями не вариант, как в случае с константным массивом. С другой стороны, добавление скалярного кода может привести к смешиванию векторных и скалярных результатов, например, при записи результатов вычислений; маскировка нежелательных значений выглядит как лучшее решение. Обратите внимание, что это предполагает адреса с 16-байтовым выравниванием. Игрушечный пример, очищающий последние три элемента вектора SIMD
vector bool int V_MASK = (vector bool int) {0,0,0,0};
unsigned int all_ones = 0xFFFFFFFFFFFFFFFF;
unsigned int * ptr_mask = (unsigned int *) &V_MASK;
ptr_mask[0]= all_ones;
vector float XV = vec_ld(0,some_float_ptr);
XV = vec_and(XV,V_MASK);