Самый быстрый способ побитового И между двумя массивами на 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.