11

我有两个图像块存储为一维数组,并在它们的元素之间进行了以下按位与运算。

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 似乎没有按位与...

4

3 回答 3

2

选项 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 - 用内联汇编代码重写您的函数。你自己来做这个。

于 2011-06-14T03:49:41.183 回答
1

您的代码是 CPU 的 Rambo - 这是最糟糕的噩梦:

  • 字节访问。就像提到的aroth一样,ARM从内存中读取字节非常慢
  • 随机访问。除了本质上已经很严重的性能损失之外,还有两个绝对不必要的乘法/加法操作。

简而言之,一切都是错误的,可能是错误的。

不要说我粗鲁。让我做你的天使吧。

首先,我将为您提供一个有效的 NEON 版本。然后是一个优化的 C 版本,可以准确地向您展示您做错了什么。

给我一点时间。我现在要睡觉了,明天我有一个重要的会议。

你为什么不学习ARM汇编?它比 x86 汇编更容易和有用。它还将大大提高您的 C 编程能力。强力推荐

青色

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

好的,这是一个用 C 语言编写的优化版本,并考虑了 ARM 汇编。

请注意,pitch 和 a_lenx 都必须是 4 的倍数。否则,它将无法正常工作。

在此版本上使用 ARM 程序集进行优化的余地不多。(NEON 是一个不同的故事 - 即将推出)

仔细看看如何处理变量声明、循环、内存访问和 AND 操作。

并确保此功能在 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;
}
于 2011-11-01T15:15:30.927 回答
0

首先,为什么要双循环?你可以用一个循环和几个指针来完成。

此外,您不需要为每个像素计算 x+y*pitch;只需将两个指针加一即可。加一比 x+y*pitch 快很多。

为什么您需要执行此操作?在研究像 NEON 这样的低级解决方案之前,我会确保没有可用的高级优化/更改。

于 2011-06-14T03:42:54.480 回答