【问题标题】:Fastest way of bitwise AND between two arrays on iPhone?iPhone上两个数组之间按位与的最快方法?
【发布时间】:2011-09-14 08:52:45
【问题描述】:

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

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

【问题讨论】:

    标签: iphone optimization neon


    【解决方案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;
    }
    

    【讨论】:

    • 你能把 a_gap 和 b_gap 的用法解释清楚一点吗?您的代码看起来很有趣,但我还没有想到使用“差距”。
    • += gap 用作一种“回车”。 pitch 是整个图像的宽度,而 lenx 指定实际正在处理的图像的宽度。
    • 嗯,这对我帮助很大。谢谢。 :)
    【解决方案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 - 用内联汇编代码重写您的函数。这个你自己来。

    【讨论】:

    • 感谢您的回答。选项 1 似乎是一个很好的修改。我会尝试的。选项 2 在我的问题中是不可接受的..
    • 我尝试了选项 1。速度有所提高,但性能提升并不大。也许我必须考虑使用 NEON 直接实现..
    • 我对您的第一个选项有一些疑问。 a_leny = a_leny / 4; a_pitch = a_pitch / 4; b_pitch = b_pitch / 4;为什么这些值被划分。?它们被用于 y 方向
    • 它们被划分是因为它们以字节为单位指定坐标/长度/索引,而算法由 32 位整数索引。要将字节指定的偏移量转换为 32 位整数指定的偏移量,需要除以 4。
    【解决方案3】:

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

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

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

    【讨论】:

    • 你好。它是一种图像处理工作。我尝试使用带有单个 for 循环的指针来实现该函数,但速度并没有那么快。
    猜你喜欢
    • 2015-12-13
    • 2011-03-22
    • 1970-01-01
    • 2011-01-18
    • 1970-01-01
    • 1970-01-01
    • 2021-03-30
    • 1970-01-01
    • 2017-12-22
    相关资源
    最近更新 更多