Эффективно вычислить два разнородных числа в неоне руки - PullRequest
0 голосов
/ 03 июля 2018

У меня есть массив из 16 целых чисел, и я хотел бы найти пару целых из этого массива, которые имеют максимальное различие между собой. Различие может быть вычислено с помощью этого (псевдо) кода:

int diss(uint32_t x, uint32_t y)
{   // it could do square for each byte of the number instead.
    return
    abs(((x >> 24) & 0xFF) - ((y >> 24) & 0xFF)) + 
    abs(((x >> 16) & 0xFF) - ((y >> 16) & 0xFF)) + 
    abs(((x >>  8) & 0xFF) - ((y >>  8) & 0xFF)) + 
    abs(((x >>  0) & 0xFF) - ((y >>  0) & 0xFF));
}

void findDissimilar(uint32_t buf[16], uint32_t& x, uint32_t& y)
{
    int maxDiss = 0;
    for (int i=0; i<16; ++i)
    {
        for (int j=0; j<16; ++j)
        {
            int d = diss(buf[i], bud[j]);
            if (d > maxDiss)
            {
                maxDiss = d;
                x = buf[i];
                y = buf[j];
            }
        }
    }
}

На входе buf уже находится в неоновых регистрах, если это имеет значение. На выходе я должен получить два целых числа (в неоновой рег, возможно, это лучше). Как я могу сделать это эффективно в руке неон, какие подходы я должен попробовать? Просто чтобы уточнить, суть вопроса заключается в оптимизации findDissimilar.

1 Ответ

0 голосов
/ 03 июля 2018

diss тривиально вычислить в неоне, возможно, он может быть реализован следующим образом (непроверенный код):

uint32x4_t diss_x4(uint32x4_t x4, uint32x4_t y4)
{
    uint8x16_t diff = vabdq_u8(vreinterpretq_u8_u32(x4), vreinterpretq_u8_u32(x4));
    uint16x8_t m0 = vmull_u8(vget_low_u8(diff), vget_low_u8(diff));
    uint16x8_t m1 = vmull_u8(vget_high_u8(diff), vget_high_u8(diff));
    uint16x4_t s0 = vpadd_u16(vget_low_u8(m0), vget_high_u8(m0));
    uint16x4_t s1 = vpadd_u16(vget_low_u8(m1), vget_high_u8(m1));
    uint16x4_t sx = vpadd_u16(s0, s1);
    return vmovl_u16(sx);
}

но не так тривиально для findDissimilar. Я думаю, что лучший подход заключается в следующем: - загрузить все 16 дюймов в 4 q-регистра {q0, q1, q2, q3}. - первый регистр q0 содержит { buf[0], buf[1], buf[2], buf[3] } - затем я могу выполнить цикл 15 раз и извлечь из 4 входных регистров q значения qext, такие как vextq_u32 (q0, q1, 1) для первой итерации и так далее. - вычислить минимумы между q0 и qext.

тогда тот же процесс должен быть выполнен для q1, q2, q3.

Возможно, если я отменю перемежение {q0, q1, q2, q3} байт, его можно оптимизировать еще лучше.

...