SSE: реципрочно, ако не е нула

Как мога да взема реципрочната (обратната) стойност на плаващите числа с инструкции за SSE, но само за ненулеви стойности?

Фон по-долу:

Искам да нормализирам масив от вектори, така че всяко измерение да има една и съща средна стойност. В C това може да се кодира като:

float vectors[num * dim]; // input data

// step 1. compute the sum on each dimension
float norm[dim];
memset(norm, 0, dim * sizeof(float));
for(int i = 0; i < num; i++) for(int j = 0; j < dims; j++)
    norm[j] += vectors[i * dims + j];
// step 2. convert sums to reciprocal of average
for(int j = 0; j < dims; j++) if(norm[j]) norm[j] = float(num) / norm[j];
// step 3. normalize the data
for(int i = 0; i < num; i++) for(int j = 0; j < dims; j++)
    vectors[i * dims + j] *= norm[j];

Сега от съображения за ефективност искам да направя това с помощта на SSE intinsics. Setp 1 и стъпка 3 са лесни, но останах на стъпка 2. Изглежда не намирам никакъв примерен код или очевидна SSE инструкция за вземане на рециплината на стойност, ако не е нула . За разделянето _mm_rcp_ps върши работа и може би го комбинира с условно преместване, но как да получите маска, показваща кой компонент е нула?

Нямам нужда от кода на алгоритъма, описан по-горе, само от функцията "обратна, ако не е нула":

__m128 rcp_nz_ps(__m128 input) {
    // ????
}

Благодаря!


person Antoine    schedule 15.05.2012    source източник


Отговори (1)


__m128 rcp_nz_ps(__m128 input) {
    __m128 mask = _mm_cmpeq_ps(_mm_set1_ps(0.0), input);
    __m128 recip = _mm_rcp_ps(input);
    return _mm_andnot_ps(mask, recip);
}

Всяка лента от mask е зададена или на b111...11, ако входът е нула, и на b000...00 в противен случай. И-не с тази маска замества елементи от реципрочната стойност, съответстваща на нулев вход с нула.

person Stephen Canon    schedule 15.05.2012
comment
По дяволите, това беше бързо. Работех сам и ти ме изпревари. +1 - person Mysticial; 15.05.2012
comment
Благодаря. Има ли начин да тествате вместо да сравнявате и да избягвате използването на регистър, зададен на нула? Просто се чудя... - person Antoine; 16.05.2012
comment
Освен това на последния ред има правописна грешка: въвеждането трябва да е recip. - person Antoine; 16.05.2012
comment
@Antoine: Регистърът, зададен на нула, всъщност не ви струва нищо, обикновено. Всеки приличен компилатор просто ще излъчи xorps, което се разпознава от процесора като идиома за нулиране и е изключително ефективно (на много процесори всъщност дори не е необходимо да се изпълнява). - person Stephen Canon; 16.05.2012
comment
За разлика от това, което подсказва името, andnot(a,b) не е (a & ~b), а (~a & b). - person Antoine; 19.07.2012