Загрузить вектор в регистр AVX2 с несовпадающим размером

Предположим, у меня есть вектор двойников C ++ std, который должен быть загружен в регистр AVX2. Это можно просто сделать с помощью команды _mm256_load_pd(&vector1[0]).
Вектор может иметь любой размер и не должен быть кратным 4. Каким будет наиболее эффективный способ загрузки оставшихся элементов вектора, если размер вектора не кратен 4?


person vydesaster    schedule 01.03.2020    source источник
comment
Что вы собираетесь делать с реестром? Имеет ли значение, в какой позиции внутри регистра хранятся остальные элементы?   -  person chtz    schedule 02.03.2020
comment
@chtz: У меня есть 4 или 5 разных векторов, в которых я хотел бы выполнять сложение и умножение поэлементно. Все векторы имеют одинаковую длину. Не имеет значения, в какой позиции внутри регистра хранятся остальные элементы.   -  person vydesaster    schedule 02.03.2020


Ответы (3)


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

Упрощенный пример (требуется особая обработка, если vect.size()<4)

// load last four elements for later use
__m256d last_input = _mm256_loadu_pd(vect.data()+vect.size()-4);
for(size_t i=0; i<vect.size()-4; i+=4) { // main loop
  __m256d input = _mm256_loadu_pd(vect.data()+i);
  _mm256_storeu_pd(some_operation(input), output.data()+i);
}
// process and store last elements (possibly overlapping with previous store):
_mm256_storeu_pd(some_operation(last_input, output.data()+vect.size()-4);

Обязательно скомпилируйте с оптимизацией и на gcc / clang с -march=native (в противном случае невыровненные загрузки / хранилища могут неэффективно разделиться).

person chtz    schedule 02.03.2020
comment
Предположим, что размер вектора равен 6, не будет ли ваше решение дважды работать с элементами вектора 2 и 3, когда индекс начинается с 0? - person vydesaster; 02.03.2020
comment
Да, но с тем же входом. То есть, он выполняет некоторые избыточные вычисления, но сохраняет много сложной логики. Если у вас процессор, на котором AVX-128 быстрее, чем AVX-256, вы можете подумать об инвестировании в эту дополнительную логику (конечно, то же самое для AVX-512 по сравнению с AVX-256). - person chtz; 02.03.2020
comment
Ах, теперь я понимаю, в чем дело. Я исследую ваше решение. Пока спасибо. - person vydesaster; 02.03.2020

Заполните свой массив так, чтобы он делился на четыре, что тратит впустую память, но устраняет неэффективность операторов if и ветвления.

person Idiotic Shrike    schedule 01.03.2020
comment
К сожалению, я не могу сделать это с исходным вектором. Было бы также хорошим решением скопировать несовпадающий вектор в новый совпадающий вектор? Будет ли процесс копирования стоить слишком много времени? Подбор здесь подразумевается с точки зрения размера. - person vydesaster; 01.03.2020
comment
По какой причине вы не можете этого сделать? - person Idiotic Shrike; 01.03.2020
comment
Векторы являются частью матрицы. Расширение каждого вектора означало бы расширение матрицы, что привело бы к большому количеству дополнительного кода и переходу к другим функциям, работающим с матрицей. - person vydesaster; 01.03.2020
comment
Возможно, вы могли бы вставить временные нулевые значения, когда вам нужно вставить векторы в AVX, а затем удалить их, когда вы закончите, но поскольку это происходит в любом эффективном цикле, который вам нужен, это может замедлить работу до точки бесполезности - единственный способ - время сами. - person Idiotic Shrike; 01.03.2020
comment
Хорошо, спасибо за отзыв. Я дам ему попробовать. - person vydesaster; 01.03.2020
comment
@vydesaster: Обычный способ справиться с этим - иметь шаг строки (расстояние между двумя строками), который отделен от фактической логической ширины строки (количества столбцов, которые действительно имеют значение). Таким образом, вы выполняете вычисления индексации с шагом строки, но цикл столбца ограничивается шириной. Это очень распространено в компьютерной графике, где это также позволяет передать обрезанный прямоугольник другой функции без копирования. (Шаг строки остается прежним, начало и ширина являются подмножеством полной строки.) - person Peter Cordes; 02.03.2020

Вы можете использовать _mm256_maskload_pd инструкция. Второй параметр указывает, какие значения загружать.

person 1201ProgramAlarm    schedule 01.03.2020
comment
Как лучше всего определить маску? - person vydesaster; 01.03.2020
comment
Но разве загрузка по маске не медленнее по сравнению с загрузкой? - person vydesaster; 02.03.2020
comment
@ 1201ProgramAlarm: вы должны расширить эту битовую маску до маски векторного элемента; сделать это эффективно - нетривиально. Загрузка со скользящим окном в строку кэша является односторонней, в противном случае - вычислением ALU. Или, поскольку OP имеет AVX512, используйте нулевую маскировку AVX512, чтобы вы могли просто использовать битовую маску. __m256d _mm256_maskz_load_pd( __mmask8 k, void * m); Тип __mmask8 - это просто uint8_t, поэтому он свободно преобразуется из целочисленных типов. - person Peter Cordes; 02.03.2020
comment
@vydesaster: не способом медленнее, и определенно не с AVX512. - person Peter Cordes; 02.03.2020
comment
@PeterCordes: Спасибо за отзыв. Вы также видите решение для чистого кода AVX2 без AVX512? - person vydesaster; 02.03.2020