Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fastest way of bitwise AND between two arrays on iPhone?

I have two image blocks stored as 1D arrays and have do the following bitwise AND operations among the elements of them.

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 ;
}

Actually, I have to do this job about 220,000 times, so it becomes very slow on iphone devices.

How could I accelerate this job on iPhone ?

I heard that NEON could be useful, but I'm not really familiar with it. In addition it seems that NEON doesn't have bitwise AND...

like image 274
wlee Avatar asked Jun 14 '11 02:06

wlee


2 Answers

Option 1 - Work in the native width of your platform (it's faster to fetch 32-bits into a register and then do operations on that register than it is to fetch and compare data one byte at a time):

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 ;
}

Option 2 - Use a heuristic to get an approximate result using fewer calculations (a good approach if the absolute difference between 101 overlaps and 100 overlaps is not important to your application):

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;
}

Option 3 - Rewrite your function in inline assembly code. You're on your own for this one.

like image 68
aroth Avatar answered Oct 21 '22 12:10

aroth


Your code is Rambo for the CPU - its worst nightmare :

  • byte access. Like aroth mentioned, ARM is VERY slow reading bytes from memory
  • random access. Two absolutely unnecessary multiply/add operations in addition to the already steep performance penalty by its nature.

Simply put, everything is wrong that can be wrong.

Don't call me rude. Let me be your angel instead.

First, I'll provide you a working NEON version. Then an optimized C version showing you exactly what you did wrong.

Just give me some time. I have to go to bed right now, and I have an important meeting tomorrow.

Why don't you learn ARM assembly? It's much easier and useful than x86 assembly. It will also improve your C programming capabilities by a huge step. Strongly recommended

cya

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

Ok, here is an optimized version written in C with ARM assembly in mind.

Please note that both the pitches AND a_lenx have to be multiples of 4. Otherwise, it won't work properly.

There isn't much room left for optimizations with ARM assembly upon this version. (NEON is a different story - coming soon)

Take a careful look at how to handle variable declarations, loop, memory access, and AND operations.

And make sure that this function runs in ARM mode and not Thumb for best results.

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;
}
like image 21
Jake 'Alquimista' LEE Avatar answered Oct 21 '22 12:10

Jake 'Alquimista' LEE