10

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

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

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

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

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

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

  6. 我当然用-O3 flag

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

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

4

2 回答 2

13

如果你正在做大量的浮点计算,那么使用 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。这样,您可以确保您充分利用了浮点处理器的全部功能。

于 2012-06-27T04:22:54.657 回答
1

我对以前的帖子提供了一些反馈。这解释了我在第 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

很多代码。我 printf-d 输出的值(int)(descriptors->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

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

我还按照建议尝试了 Accelerate,将三行替换为:

__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

于 2012-07-03T08:35:21.960 回答