【问题标题】:Maximum speed from IOS/iPad/iPhone来自 IOS/iPad/iPhone 的最大速度
【发布时间】:2012-06-27 04:03:13
【问题描述】:

我使用OpenCViOS 完成了计算密集型应用程序。当然,它很慢。但它比我的 PC 原型慢 200 倍。所以我正在优化它。从最初的 15 秒开始,我就可以达到 0.4 秒的速度。我想知道我是否找到了所有东西以及其他人可能想要分享的东西。我做了什么:

  1. 将 OpenCV 中的“double”数据类型替换为“float”。 Double 是 64 位,而 32 位 CPU 无法轻松处理它们,所以 float 给了我一些速度。 OpenCV 经常使用 double。

  2. 在编译器选项中添加了“-mpfu=neon”。副作用是模拟器编译器不再工作的新问题,任何东西都只能在本机硬件上测试。

  3. 用 90 个值查找表替换了 sin()cos() 实现。加速是巨大的!这与 PC 有点相反,PC 的这种优化不会带来任何加速。有代码以度为单位工作,这个值被转换为sin()cos() 的弧度。这段代码也被删除了。但是查找表完成了这项工作。

  4. 启用"thumb optimizations"。一些博客文章建议完全相反,但这是因为拇指在armv6 上通常会使事情变慢。 armv7 没有任何问题,让事情变得更快更小。

  5. 为了确保拇指优化和-mfpu=neon 最多可以工作并且不引入崩溃,我完全删除了 armv6 目标。我所有的代码都编译为armv7,这也被列为应用商店的要求。这意味着iPhone 的最小值将是3GS。我认为放弃旧的是可以的。无论如何,旧设备的 CPU 速度较慢,如果安装在旧设备上,CPU 密集型应用程序会提供糟糕的用户体验。

  6. 我当然用-O3 flag

  7. 我从 OpenCV 中删除了 "dead code"。通常在优化 OpenCV 时,我会看到我的项目显然不需要的代码。例如,通常有一个额外的"if()" 来检查像素大小是 8 位还是 32 位,我知道我只需要 8 位。这会删除一些代码,为优化器提供更好的机会来删除更多内容或替换为常量。代码也更适合缓存。

还有其他技巧和想法吗?对我来说,启用拇指并用查找替换三角函数是助推器,让我感到惊讶。也许您知道更多可以让应用飞起来的事情?

【问题讨论】:

    标签: iphone ios xcode opencv


    【解决方案1】:

    如果您要进行大量浮点计算,那么使用 Apple 的 Accelerate 框架会让您受益匪浅。它旨在使用浮点硬件对向量进行并行计算。

    我也会一一解决你的观点:

    1) 这不是因为 CPU,而是因为从 armv7 时代开始,浮点处理器硬件中只会计算 32 位浮点运算(因为苹果更换了硬件)。 64 位的将改为在软件中计算。作为交换,32 位操作变得更快。

    2) NEON 是新的浮点处理器指令集的名称

    3) 是的,这是一种众所周知的方法。另一种方法是使用我上面提到的 Apple 框架。它提供了并行计算 4 个值的 sin 和 cos 函数。这些算法在组装和 NEON 中进行了微调,因此它们可以在使用最少电池的同时提供最佳性能。

    4) thumb 的新 armv7 实现没有 armv6 的缺点。禁用建议仅适用于 v6。

    5) 是的,考虑到现在 80% 的用户使用的是 iOS 5.0 或更高版本(armv6 设备在 4.2.1 结束支持),这在大多数情况下是完全可以接受的。

    6) 当您在发布模式下构建时,这会自动发生。

    7) 是的,不过效果不会像上面的方法那么大。

    我的建议是查看 Accelerate。这样您就可以确保您充分利用了浮点处理器的全部功能。

    【讨论】:

    【解决方案2】:

    我对以前的帖子提供了一些反馈。这解释了我在第 7 点中试图提供的关于死代码的一些想法。这意味着更广泛的想法。我需要格式化,所以不能使用评论表。此类代码在 OpenCV 中:

    for( kk = 0; kk < (int)(descriptors->elem_size/sizeof(vec[0])); kk++ ) {
        vec[kk] = 0;
    }
    

    我想看看它在装配时的样子。为了确保我可以在汇编中找到它,我将它包装成这样:

    __asm__("#start");
    for( kk = 0; kk < (int)(descriptors->elem_size/sizeof(vec[0])); kk++ ) {
        vec[kk] = 0;
    }
    __asm__("#stop");
    

    现在我按“产品 -> 生成输出 -> 程序集文件”,我得到的是:

        @ InlineAsm Start
        #start
        @ InlineAsm End
    Ltmp1915:
        ldr r0, [sp, #84]
        movs    r1, #0
        ldr r0, [r0, #16]
        ldr r0, [r0, #28]
        cmp r0, #4
        mov r0, r4
        blo LBB14_71
    LBB14_70:
    Ltmp1916:
        ldr r3, [sp, #84]
        movs    r2, #0
    Ltmp1917:
        str r2, [r0], #4
        adds    r1, #1
    Ltmp1918:
    Ltmp1919:
        ldr r2, [r3, #16]
        ldr r2, [r2, #28]
        lsrs    r2, r2, #2
        cmp r2, r1
        bgt LBB14_70
    LBB14_71:
    Ltmp1920:
        add.w   r0, r4, #8
        @ InlineAsm Start
        #stop
        @ InlineAsm End
    

    很多代码。我打印出(int)(descriptors-&gt;elem_size/sizeof(vec[0])) 的值,它始终是 64。所以我将其硬编码为 64 并通过汇编程序再次传递:

        @ InlineAsm Start
        #start
        @ InlineAsm End
    Ltmp1915:
        vldr.32 s16, LCPI14_7
        mov r0, r4
        movs    r1, #0
        mov.w   r2, #256
        blx _memset
        @ InlineAsm Start
        #stop
        @ InlineAsm End
    

    正如您现在可能看到的,优化器得到了这个想法,代码变得更短了。它能够对此进行矢量化。关键是编译器总是不知道哪些输入是常数,如果这是像网络摄像头大小或像素深度之类的东西,但实际上在我的上下文中它们通常是常数,我只关心速度。

    我还按照建议将三行替换为:

    __asm__("#start");
    vDSP_vclr(vec,1,64);
    __asm__("#stop");
    

    组件现在看起来:

        @ InlineAsm Start
        #start
        @ InlineAsm End
    Ltmp1917:
        str r1, [r7, #-140]
    Ltmp1459:
    Ltmp1918:
        movs    r1, #1
        movs    r2, #64
        blx _vDSP_vclr
    Ltmp1460:
    Ltmp1919:
        add.w   r0, r4, #8
        @ InlineAsm Start
        #stop
        @ InlineAsm End
    

    不确定这是否比 bzero 快。在我的上下文中,这部分不会花费太多时间,并且两个变体似乎以相同的速度工作。

    我学到的另一件事是使用 GPU。更多信息在这里http://www.sunsetlakesoftware.com/2012/02/12/introducing-gpuimage-framework

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-10-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多