Самый быстрый способ побитового И между двумя массивами на iPhone?

У меня есть два блока изображений, которые хранятся как массивы 1D и выполняют следующие побитовые операции И среди элементов из них.

int compare(unsigned char *a, int a_pitch, 
            unsigned char *b, int b_pitch, int a_lenx, int a_leny) 
{
    int overlap =0 ;

    for(int y=0; y<a_leny; y++) 
        for(int x=0; x<a_lenx; x++) 
        {
            if(a[x + y * a_pitch] & b[x+y*b_pitch]) 
                overlap++ ;
        }
    return overlap ;
}

На самом деле, я должен выполнить эту работу около 220 000 раз, поэтому на устройствах iphone очень медленно.

Как я могу ускорить эту работу на iPhone?

Я слышал, что NEON может быть полезен, но я не очень-то знаком с ним. Кроме того, кажется, что NEON не имеет побитового AND...

Ответ 1

Вариант 1 - работайте в собственной ширине вашей платформы (быстрее загружать 32-битные данные в регистр, а затем выполнять операции над этим регистром, а не собирать и сравнивать данные по одному байту за раз):

int compare(unsigned char *a, int a_pitch, 
            unsigned char *b, int b_pitch, int a_lenx, int a_leny) 
{
    int overlap = 0;
    uint32_t* a_int = (uint32_t*)a;
    uint32_t* b_int = (uint32_t*)b;

    a_leny = a_leny / 4;
    a_lenx = a_lenx / 4;
    a_pitch = a_pitch / 4;
    b_pitch = b_pitch / 4;

    for(int y=0; y<a_leny_int; y++) 
        for(int x=0; x<a_lenx_int; x++) 
        {
            uint32_t aVal = a_int[x + y * a_pitch_int];
            uint32_t bVal = b_int[x+y*b_pitch_int];
            if (aVal & 0xFF) & (bVal & 0xFF)
                overlap++;
            if ((aVal >> 8) & 0xFF) & ((bVal >> 8) & 0xFF)
                overlap++;
            if ((aVal >> 16) & 0xFF) & ((bVal >> 16) & 0xFF)
                overlap++;
            if ((aVal >> 24) & 0xFF) & ((bVal >> 24) & 0xFF)
                overlap++;
        }
    return overlap ;
}

Вариант 2 - используйте эвристику, чтобы получить приблизительный результат, используя меньшее количество вычислений (хороший подход, если абсолютная разница между 101 перекрытием и 100 перекрытиями не важна для вашего приложения):

int compare(unsigned char *a, int a_pitch, 
            unsigned char *b, int b_pitch, int a_lenx, int a_leny) 
{
    int overlap =0 ;

    for(int y=0; y<a_leny; y+= 10) 
        for(int x=0; x<a_lenx; x+= 10) 
        {
            //we compare 1% of all the pixels, and use that as the result
            if(a[x + y * a_pitch] & b[x+y*b_pitch]) 
                overlap++ ;
        }
    return overlap * 100;
}

Вариант 3 - перепишите свою функцию в встроенном ассемблере. Вы сами по себе для этого.

Ответ 2

Ваш код - Рэмбо для CPU - его худший кошмар:

  • доступ к байтам. Как упоминалось выше, ARM очень медленно читает байты из памяти
  • случайный доступ. Две абсолютно ненужные операции умножения/добавления в дополнение к уже крутому снижению производительности по своей природе.

Проще говоря, все неправильно, что может быть неправильно.

Не называй меня грубым. Позволь мне быть твоим ангелом вместо этого.

Во-первых, я дам вам рабочую версию NEON. Затем оптимизированная версия C показывает вам, что вы сделали неправильно.

Просто дай мне немного времени. Сейчас я должен лечь спать, и завтра у меня будет важная встреча.

Почему вы не изучаете сборку ARM? Это намного проще и полезно, чем сборка x86. Он также улучшит ваши возможности программирования C огромным шагом. Настоятельно рекомендуется

суа

=============================================== ===============================

Хорошо, вот оптимизированная версия, написанная на C с сборкой ARM.

Обратите внимание, что оба тона и a_lenx должны быть кратными 4. В противном случае он не будет работать должным образом.

В этой версии нет оптимизаций для сборки ARM. (NEON - другая история - скоро)

Внимательно изучите, как обрабатывать объявления переменных, цикл, доступ к памяти и операции И.

И убедитесь, что эта функция работает в режиме ARM, а не Thumb для достижения наилучших результатов.

unsigned int compare(unsigned int *a, unsigned int a_pitch, 
            unsigned int *b, unsigned int b_pitch, unsigned int a_lenx, unsigned int a_leny) 
{
    unsigned int overlap =0;
    unsigned int a_gap = (a_pitch - a_lenx)>>2;
    unsigned int b_gap = (b_pitch - a_lenx)>>2;
    unsigned int aval, bval, xcount;

    do
    {
        xcount = (a_lenx>>2);
        do
        {
            aval = *a++;
            // ldr      aval, [a], #4
            bval = *b++;
            // ldr      bavl, [b], #4
            aval &= bval;
            // and      aval, aval, bval

            if (aval & 0x000000ff) overlap += 1;
            // tst      aval, #0x000000ff
            // addne    overlap, overlap, #1
            if (aval & 0x0000ff00) overlap += 1;
            // tst      aval, #0x0000ff00
            // addne    overlap, overlap, #1
            if (aval & 0x00ff0000) overlap += 1;
            // tst      aval, #0x00ff0000
            // addne    overlap, overlap, #1
            if (aval & 0xff000000) overlap += 1;
            // tst      aval, #0xff000000
            // addne    overlap, overlap, #1
        } while (--xcount);

        a += a_gap;
        b += b_gap;
    } while (--a_leny);

    return overlap;
}

Ответ 3

Прежде всего, почему двойной цикл? Вы можете сделать это с помощью одного цикла и нескольких указателей.

Кроме того, вам не нужно вычислять шаг x + y * для каждого пикселя; просто увеличивайте два указателя на единицу. Увеличение на единицу намного быстрее, чем шаг x + y *.

Почему именно вам нужно выполнить эту операцию? Я бы удостоверился, что нет оптимизаций/изменений высокого уровня, доступных перед тем, как смотреть в низкоуровневое решение, такое как NEON.