ARM NEON:由于内存访问带宽有限而预测性能问题的工具?

Posted

技术标签:

【中文标题】ARM NEON:由于内存访问带宽有限而预测性能问题的工具?【英文标题】:ARM NEON: Tools to predict performance issues due to memory access limited bandwidth? 【发布时间】:2014-02-11 12:04:41 【问题描述】:

我正在尝试优化 C 代码的关键部分以在 ARM 设备中进行图像处理,最近发现了 NEON。

在这里和那里阅读了提示,我得到了相当不错的结果,但有些东西让我无法理解。我发现整体性能在很大程度上取决于内存访问及其完成方式。

这是最简单的方法(简单我的意思是,如果可能的话,不必在模拟器或模拟器中运行整个编译的代码,而是可以提供小块汇编并分析它们的东西),以便了解内存访问如何“瓶颈”子程序?

我知道如果不在特定硬件和特定条件下运行它就无法完全做到这一点,但目的是有一个“比较”试错工具来进行试验,即使结果只是近似值。

(类似于this 循环盘点的好工具)

【问题讨论】:

不幸的是,每个系统的内存都有不同的周期。为了进行适当的分析,您需要准确的时间。对于 DDR SDRAM,这可能非常复杂,因为页面内的突发、跨存储区、读取后写入等具有不同的时序。为了减少对硬件的依赖(或者甚至是分配器放置东西的地方),您需要您的算法在使用前将数据传输到 L2 或 L1。 谢谢!我本来是这样想的。但决定问,因为我不需要确切的时间,只是想在 ARM-NEON 平台上测试它们之前给我一些“提示”关于某些更改是否可以受益或“杀死”我的子例程,但我想它仍然是一般的。顺便说一句,当您说将数据传输到 L2 或 L1 时,您的意思是使用预加载 (PLD) 吗?如果还有更多,请问在哪里可以找到? 是的,PLD 是执行此操作的正常方式。参见Cortex-A8 Neon color conversion 等。大多数ARM 文档都说PLD 就像不等待完成的虚假内存访问。当 CPU 执行其他操作时,它只会在后台为该地址填充 L1L2 行。这些线的大小可以根据特定的芯片而有所不同。有一些方法可以动态查找,但大多数人会针对特定系统对其进行硬编码以简化汇编程序。 一些 ARM 有一个 PLD 作为 NOP,就像 ARM926。所以对于它来说,PLD 只是指令污染。 memcpy() for the cortex-A8 上的 ARM 引用,它将 PLD 限制为 L2 大小。我想 L1 未命中并没有那么昂贵。请注意,它们提前预取。您的算法将受 CPU 或内存限制。知道内存带宽可以告诉你是哪一个。知道 零周期 代码时序会限制 CPU。两者都知道,你应该知道该在哪一边工作。 谢谢,memcpy 链接非常有趣!写作部分有什么建议吗? 【参考方案1】:

我想您可能已经回答了自己的问题。内存是一种系统级效应,许多 ARM 实施者(Apple、Samsung、Qualcomm 等)以不同的方式实施系统并产生不同的结果。

但是,当然,您可以针对某个系统进行优化,并且它可能会在其他系统上运行良好,因此实际上归结为找出一种可以快速迭代和测试/模拟系统级效果的方法。这确实会变得复杂,因此您可能会为系统级模拟器(例如 ARM 的 RealView 中包含的)支付一些费用。或者我可能会推荐一些开源硬件,比如 Panda Board 并使用 valgrind 的缓存研磨。使用 panda board 上的 linux,您可以编写一些脚本来自动化您的测试。

这可能会很麻烦,但如果针对 ARM 进行优化将成为您职业生涯的一部分,那么(与您的薪水相比相对较低)软件/硬件投资和时间是值得的。

注意 1:我建议不要使用 PLD。这非常依赖于系统调整,如果你让它在一个 ARM 实现上运行良好,它可能会伤害你的下一代芯片或不同的实现。这可能暗示尝试在系统级别进行优化,除了一些基本的数据本地化和排序的东西可能不值得你努力? (见下方斯蒂芬的评论)。

【讨论】:

关于 PLD,值得注意的是,大多数更现代的内核都有某种形式的“流式预取”;它们将自行识别线性内存访问和预取,而无需明确的预取指令。 是的,我怀疑有类似的事情,但我更愿意问,因为我对这个世界很陌生。无论如何,正如我看到的性能差异(在特定平台上,即我的移动设备上)取决于我使用 PLD 的方式,想知道它是否有一个神奇的规则。另一种可能性(只是想知道)是实时确定特定平台的最佳 PLD 模式,以在 2 或 3 中选择,但不知道如果系统行为也会发生动态变化,这是否有意义。跨度> 是的,在运行时确定如何最好地使用诸如 PLD 之类的系统特定功能确实很有趣。最好的 DSP 库,如 Intel Performance Primitives,会进行 CPU 调度——它们检测正在使用的特定 Intel CPU 变体,然后为它使用最好的程序集。不幸的是,在 ARM 世界中,许多供应商对其 ARM ISA 的实现非常保密,因此(对我而言)似乎很难像英特尔那样设计解决方案。【参考方案2】:

内存访问是一件无法简单地从“小部件”建模以生成有意义的指导的东西。缓存层次结构、存储缓冲区、加载未命中队列、缓存策略等……甚至相对简单处理器在 LSU 下隐藏着大量的“状态”,任何小规模的分析都无法准确捕捉到该状态。也就是说,有一些基本准则可以获得最佳性能:

最大化“有用计算”指令与 LSU 操作的比率。 对齐内存访问(理想情况下为 16B)。 如果您需要在对齐负载或对齐存储之间进行选择,请对齐您的存储。 尽可能尝试写出完整的缓存行。 PLD 主要用于非统一但仍可预测的内存访问模式(这种情况很少见)。 特别是对于 NEON,您应该更喜欢使用 vld1vst1 指令(带有对齐提示)。在大多数微架构上,在大多数情况下,它们是在 NEON 和内存之间移动的最快方式。特别避开v[ld|st][3|4];这是一个有吸引力的麻烦,在大多数情况下比对大多数微架构进行单独的置换要慢。

【讨论】:

感谢您的提示,我将使用它们,看看会发生什么。我坚持的具体例程是图像旋转。在普通 C 中,旋转 240*400 图像大约需要 2-3 毫秒。当我想将密度加倍 (480*800) 时,我预计时间会增加 4 倍,但它需要超过 20 毫秒。然后我决定尝试使用NEON,在其他一些例程中(不是旋转)速度提高了很多。但是在这一个中是一样的,甚至更慢。 你是旋转90度,还是任意旋转? 只有 90 个不同的配置,有时是逆时针和/或翻转。目的是使相机预览(将其转换为 RGB 后)适应屏幕设置并在同一预览上做一些事情 刚测了一下,大概是13msec,但我觉得还是太多了 我怎么知道缓存线的大小?

以上是关于ARM NEON:由于内存访问带宽有限而预测性能问题的工具?的主要内容,如果未能解决你的问题,请参考以下文章

使用NEON优化ARM的卷积运算

如何访问 NEON 指令中的完整 128 位?

GCC 向量扩展和 ARM NEON 的内存对齐问题

最终的 ARM Linux 内存碎片与 NEON Copy 但不是 memcpy

ARM NEON 图像转换优化

ARM NEON:从 NEON 寄存器(Q/D 寄存器)中包含的地址加载数据