【问题标题】:Do I get a performance bonus if I try to use arm math assembler commands instead of c如果我尝试使用 arm 数学汇编程序命令而不是 c,我会获得性能奖励吗?
【发布时间】:2012-12-21 20:20:07
【问题描述】:

我的应用程序中有循环执行数学乘法和加法计算。

我知道一些事实:

  • android 设备支持 armv6 及更高版本的处理器
  • armv6 不支持 NEON 命令

如果我将开始使用汇编程序数学命令而不是 c 数学命令,我是否会提高 armv6 上的应用程序性能?

更新

我需要用数学运算更快地执行循环,这是使用汇编程序而不是 c 的正确方法。

更新

我有这个计算:

Ry0 = (b0a0 * buffer[index] + b1a0 * Rx1 + b2a0 * Rx2 - a1a0 * Ry1
                    - a2a0 * Ry2);

它是双二阶传递函数。

我可以强制使用 asm 更快地执行这个计算吗?

更新

  • 缓冲区大小为 192000
  • 变量为浮点型

【问题讨论】:

  • 其他人如何衡量您的应用程序的性能?尝试两种方法,然后比较结果。
  • 不,编译器会很容易地解决这个问题。在 1980 年代,人类曾经胜过编译器,但那是很久以前的事了。
  • 这是 21 世纪,编译器比黑暗时代要聪明得多,所以 pro-tip 不要试图智取编译器并认为你是比这些软件工具更聪明......只是说:)
  • @BoPersson:我完全不同意。我使用极其优化的代码并编写了大量的 ARM 程序集。在定期编写优化程序集方面,我击败了 GCC、LLVM 和 RVCT。这需要相当多的练习和大量的时间,但是说人类没有超越编译器是不正确的。
  • 编译器永远无法以无限的运行时间超越人类。不幸的是,没有人有无限的运行时间。 C 标准狂热者喜欢指出许多细微差别。如果算法不需要它们,那么总是有可能胜过编译器。两全其美的是使用内联汇编器。 ARM 有各种 multiply-accumulate 指令。如果编译器不生成这些,inline assembler 可以轻松处理这种情况。

标签: android c assembly arm


【解决方案1】:

编译器非常擅长他们的工作,所以除非你知道你的编译器正在产生什么,并且知道你可以做得更好,否则可能不会。

如果不确切知道您的代码做了什么,就不可能给出更好的答案。

编辑:总结这次讨论: 提高性能的第一步不是开始编写汇编程序。第一步是找到最有效的算法。完成后,您可以查看汇编程序编码。

【讨论】:

  • 也许多一点上下文:缓冲区的大小是多少,不同的变量是什么类型?编译器生成什么代码?您是否看过它,并发现了可以改进的地方?
  • 我是 asm 的新手,我不看编译器的代码,怎么做
  • @testCoder - 检查编译器的文档,它应该有一个命令行开关来输出汇编程序。此外,您可以将其设置为每个源文件有 1 个函数,将它们编译为对象(.o 或 .obj),然后将其反编译为 asm 级别 - 请注意,这些是高级操作!
  • 请注意,这不是我们在 StackOverflow 上经常看到的常见“我希望我的代码运行得更快”的问题之一。 IIR 是一种信号处理功能,它发生在性能非常理想且需要额外努力的情况下。这是证明不信任编译器的理由之一。
  • 是的,但是改进它仍然需要比编译器做得更好(或者找到其他可以做到这一点的人)。我很抱歉不是 DSP 专家,但就像你的回答所说,第一步是不要开始编写汇编程序。第一步是找到最有效的算法。完成后,您可以查看汇编程序编码。
【解决方案2】:

无限脉冲响应 (IIR) 函数很难以高性能实现,因为每个输出元素都密切依赖于前一个输出元素。这迫使从输出到输出的延迟。这种依赖链击败了常见的高性能技术(例如 SIMD、条带挖掘和超标量执行)。

最初在汇编中工作并不是解决此问题的好方法。在某些时候,组装工作可能会有所帮助。但是,您有一个基本问题需要解决:在完成之前的输出、将其乘以一个系数并添加附加算术的结果之前,您无法产生新的输出。因此,您可以使用此公式做的最好的事情是产生一个输出,就像处理器从头到尾执行乘法和加法一样频繁,即使假设其他工作可以并行完成。

在数学上可以重写 IIR,以使输出依赖于过去更远的其他输出和输入,而不是前一个输出。这使用了更多的算术,但提供了并行执行更多算术的可能性,从而获得更高的吞吐量。

在 iPhone 或其他 iOS 设备上,您可以简单地调用 Accelerate 框架中的 vDSP_deq22。 Accelerate 是 Apple 库,因此在 Android 上不可用。但是,也许有人已经实现了类似的东西。

一种方法是测量每个输出占用多少处理器周期(计算多个,将时间除以输出数量,乘以处理器速度)与加法乘法的延迟(以周期为单位)(来自您正在使用的处理器型号)。如果所花费的时间与延迟相同,那么就不可能在该处理器上更快地执行此算术,您必须接受它或找到具有不同数学的替代解决方案。

【讨论】:

【解决方案3】:

您可以通过查看编译器的功能来获得一些额外的速度,但这应该是您做的最后一件事。首先好好看看你的算法和变量类型。

由于您的目标是 ARMv6,我要做的第一件事就是从浮点运算切换到定点运算。 ARMv6 通常没有或非常慢的硬件浮点支持。 ARMv7 通常更好,但对于 ARM,定点运算通常比浮点实现快很多。

【讨论】:

    【解决方案4】:

    Android 支持 ARMv5TE 和 ARMv7-A。阅读有关支持的 CPU ARCH 和 ABI 的 NDK 文档,地址为$NDK/docs/CPU-ARCH-ABIS.html

    ARMv5TE 是默认的,不给你任何硬件浮点支持,你可以看到Android NDK页面更多关于这个。您应该为您的应用程序添加 ARMv7-A 支持,以获得硬件的最佳支持。

    ARMv6 介于两者之间,如果你想针对这些设备,你必须做一些Android.mk 诡计。

    如今,如果您正在编写现代应用程序,您可能会针对具有 ARMv7-A 处理器类型(具有 VFPv3 和 NEON)的较新设备。如果你只是想支持 ARMv6,你应该使用 ARMv5TE 来覆盖这些。如果您想利用 ARMv6 提供的一些额外功能,那么您将完全失去对 ARMv5TE 的支持。

    我使用 NDK r8c 编译了您的简单代码行,它可以生成如下所示的二进制文件。最好的 ARM VFP 允许您的语句是 multiply and accumulate 指令,即 fmac,编译器可以轻松发出这些。

    00000000 <f>:
       0:   ee607aa2    fmuls   s15, s1, s5
       4:   ed9f7a05    flds    s14, [pc, #20]
       8:   ee407a07    fmacs   s15, s0, s14
       c:   ee417a03    fmacs   s15, s2, s6
      10:   ee417ae3    fnmacs  s15, s3, s7
      14:   eeb00a67    fcpys   s0, s15
      18:   ee020a44    fnmacs  s0, s4, s8
      1c:   e12fff1e    bx  lr
    

    最好将您的语句分成几块以获得双重发布,但您可以在 C 中执行此操作。

    你不能仅仅使用汇编来创造奇迹,但是编译器也可以创造一个巨大的废话。 GCC 和 ARM 不如 GCC 和 Intel。尤其是在矢量化、NEON 使用方面。如果您需要高性能的例程,最好检查编译器生成的内容。

    【讨论】:

    • NEON 对 IIR 帮助不大,因为每个输出对前一个输出的依赖会强制序列化。
    猜你喜欢
    • 2015-07-09
    • 2011-02-24
    • 2015-11-11
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-04
    • 1970-01-01
    相关资源
    最近更新 更多