Предположим, у меня есть вектор двойников C ++ std, который должен быть загружен в регистр AVX2. Это можно просто сделать с помощью команды _mm256_load_pd(&vector1[0])
.
Вектор может иметь любой размер и не должен быть кратным 4. Каким будет наиболее эффективный способ загрузки оставшихся элементов вектора, если размер вектора не кратен 4?
Загрузить вектор в регистр AVX2 с несовпадающим размером
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
Предположим, что размер вектора равен 6, не будет ли ваше решение дважды работать с элементами вектора 2 и 3, когда индекс начинается с 0?
- person vydesaster; 02.03.2020
Да, но с тем же входом. То есть, он выполняет некоторые избыточные вычисления, но сохраняет много сложной логики. Если у вас процессор, на котором AVX-128 быстрее, чем AVX-256, вы можете подумать об инвестировании в эту дополнительную логику (конечно, то же самое для AVX-512 по сравнению с AVX-256).
- person chtz; 02.03.2020
Ах, теперь я понимаю, в чем дело. Я исследую ваше решение. Пока спасибо.
- person vydesaster; 02.03.2020
Заполните свой массив так, чтобы он делился на четыре, что тратит впустую память, но устраняет неэффективность операторов if и ветвления.
person
Idiotic Shrike
schedule
01.03.2020
К сожалению, я не могу сделать это с исходным вектором. Было бы также хорошим решением скопировать несовпадающий вектор в новый совпадающий вектор? Будет ли процесс копирования стоить слишком много времени? Подбор здесь подразумевается с точки зрения размера.
- person vydesaster; 01.03.2020
По какой причине вы не можете этого сделать?
- person Idiotic Shrike; 01.03.2020
Векторы являются частью матрицы. Расширение каждого вектора означало бы расширение матрицы, что привело бы к большому количеству дополнительного кода и переходу к другим функциям, работающим с матрицей.
- person vydesaster; 01.03.2020
Возможно, вы могли бы вставить временные нулевые значения, когда вам нужно вставить векторы в AVX, а затем удалить их, когда вы закончите, но поскольку это происходит в любом эффективном цикле, который вам нужен, это может замедлить работу до точки бесполезности - единственный способ - время сами.
- person Idiotic Shrike; 01.03.2020
Хорошо, спасибо за отзыв. Я дам ему попробовать.
- person vydesaster; 01.03.2020
@vydesaster: Обычный способ справиться с этим - иметь шаг строки (расстояние между двумя строками), который отделен от фактической логической ширины строки (количества столбцов, которые действительно имеют значение). Таким образом, вы выполняете вычисления индексации с шагом строки, но цикл столбца ограничивается шириной. Это очень распространено в компьютерной графике, где это также позволяет передать обрезанный прямоугольник другой функции без копирования. (Шаг строки остается прежним, начало и ширина являются подмножеством полной строки.)
- person Peter Cordes; 02.03.2020
Вы можете использовать _mm256_maskload_pd
инструкция. Второй параметр указывает, какие значения загружать.
person
1201ProgramAlarm
schedule
01.03.2020
Как лучше всего определить маску?
- person vydesaster; 01.03.2020
Но разве загрузка по маске не медленнее по сравнению с загрузкой?
- person vydesaster; 02.03.2020
@ 1201ProgramAlarm: вы должны расширить эту битовую маску до маски векторного элемента; сделать это эффективно - нетривиально. Загрузка со скользящим окном в строку кэша является односторонней, в противном случае - вычислением ALU. Или, поскольку OP имеет AVX512, используйте нулевую маскировку AVX512, чтобы вы могли просто использовать битовую маску.
__m256d _mm256_maskz_load_pd( __mmask8 k, void * m);
Тип __mmask8
- это просто uint8_t
, поэтому он свободно преобразуется из целочисленных типов.
- person Peter Cordes; 02.03.2020
@vydesaster: не способом медленнее, и определенно не с AVX512.
- person Peter Cordes; 02.03.2020
@PeterCordes: Спасибо за отзыв. Вы также видите решение для чистого кода AVX2 без AVX512?
- person vydesaster; 02.03.2020
@vydesaster: Векторизация с невыровненными буферами: использование VMASKMOVPS: создание маски из счетчика несовпадений? Или вообще не использовать этот insn или есть ли инструкция, обратная инструкции movemask в Intel avx2?
- person Peter Cordes; 02.03.2020