Как написать код, который компилятор может оптимизировать для сравнения с SIMD?

std::array<int, 4> a = {1, 1, 1, 1};
std::array<int, 4> b = { 1, 2, 3, 4 };
std::array<int, 4> c;
bool res = false;
for (int i = 0; i < a.size(); i++) {
    a[i] = rand() % 10;
}

for (int i = 0; i < 4; i++) {
    c[i] = a[i] + b[i];
}

Умный компилятор может хорошо скомпилировать вышеуказанное в SIMD. Но то, как написать сравнение, как показано ниже, также может быть хорошо скомпилировано в SIMD;

res = a[0] <= b[0] && a[1] <= b[1] && a[2] <= b[2] && a[3] <= b[3]; // not compile to SIMD

person bitnick    schedule 03.03.2018    source источник
comment
Какой компилятор и какие параметры компилятора вы используете?   -  person Basile Starynkevitch    schedule 03.03.2018
comment
может быть, __attribute__ ((vector_size (16))), если вы используете gcc?   -  person phuclv    schedule 03.03.2018
comment
Visual Studio 2015, x64, полная оптимизация (/Ox), расширенные векторные расширения 2 (/arch:AVX2), @BasileStarynkevitch   -  person bitnick    schedule 03.03.2018
comment
Я бы не надеялся на многое, это требует movmskps-вывода результата сравнения из вектора и скалярного сравнения, это не то, что я когда-либо видел, как MSVC делает сам по себе.   -  person harold    schedule 03.03.2018
comment
Измерьте производительность. В противном случае вы не сможете сказать, хорошо ли вы сделали Работу. Google Benchmark — хороший инструмент. Я думаю, что openmp, предложенный @nemequ, займет больше времени, чем последовательное выполнение, из-за накладных расходов   -  person schorsch312    schedule 03.03.2018


Ответы (1)


Как насчет такого:

int res = 0;
#pragma omp simd reduction(+:res)
for (int i = 0 ; i < 4 ; i++) {
  res += a[i] < b[i];
}

?

Если вы можете правильно выровнять свой ввод (и добавить выровненное предложение в прагму openmp), это должно быть довольно быстро. Особенно, если ваш ввод действительно длиннее 4 элементов.

res будет 0-4 вместо 0 или 1, но это, вероятно, не проблема. Инструкции SIMD, как правило, обрабатывают горизонтальные сложения, но не горизонтальные побитовые и.

person nemequ    schedule 03.03.2018
comment
Инструкции SIMD, как правило, обрабатывают горизонтальные добавления Ну, не x86. Существует инструкция hadd, но она выполняет только горизонтальные пары, а не полное сокращение. И что более важно, это медленнее, чем отдельное перемешивание + добавление инструкций. stackoverflow.com/questions/6996764/. В любом случае, на x86 лучший asm для того, что хочет OP, - это pcmpgtd xmm0, xmm1 (a,b) / pmovmskb eax, xmm0 / cmp eax, 0xffff / je condition_true (т.е. проверьте, что каждый элемент a сравнивается больше, чем соответствующий элемент b, поэтому маска сравнения все- один) - person Peter Cordes; 04.03.2018
comment
Итак, что вам на самом деле нужно, так это умный компилятор, который знает, как использовать SIMD-сравнения, в противном случае я не уверен, что есть способ заставить его выдать этот asm. И кстати, горизонтальное И на x86 так же просто, как горизонтальное добавление, для небулевых векторов, где вы не можете просто извлечь вектор в целочисленное растровое изображение. Кроме того, у NEON нет эквивалента pmovmskb, поэтому преобразование логического вектора в целочисленное растровое изображение требует больше усилий. - person Peter Cordes; 04.03.2018
comment
вместо res += a[i] < b[i]; я думаю, что лучше использовать res &= a[i] < b[i]; для получения логического результата вместо общей суммы - person phuclv; 04.03.2018
comment
Петр, я полностью согласен, но вопрос в написании кода, который компилятор может автоматически векторизовать. То, что я написал, является самым близким, что я могу придумать без большого количества тестов :( - person nemequ; 04.03.2018
comment
Lưu Vĩnh Phúc, &= не сработает. Возможно, вы имели в виду |=? Как я упоминал в своем ответе, я решил использовать += вместо этого по какой-то причине, но |= будет работать… это может просто быть немного медленнее. - person nemequ; 04.03.2018