【问题标题】:How to transpose a matrix in ARM assembly如何在ARM汇编中转置矩阵
【发布时间】:2014-10-06 12:55:14
【问题描述】:

我正在尝试将特定的 8 个 n 位数组(每个数组有 n 位(大约 70,000))转换为包含 n 个元素的字节数组。

上下文信息:8 个 n 位数组是 8 个通道的 RGB 数据。我需要一个字节来表示 8 个数组的第 n 位位置。这将在 ARM Cortex-M3 处理器上运行,并且需要尽可能快地执行,因为我正在使用生成的数组同时生成 8 个信号。

我想出了一个伪算法(在链接中)来做到这一点,但我担心它对处理器来说可能太昂贵了。

Pseudo Algorithm

我正在寻找执行速度最快的代码。大小是次要的。 我将不胜感激。

这是我实施的,但结果不是那么好。

do{
    for(b=0;b<24;b++){ //Optimize to for(b=24;b!=0;b--)
        m = 1 << b;
        *dataBytes = *dataBytes + __ROR((*s0 & m),32+b-0); //strip 0 data       
        *dataBytes = *dataBytes + __ROR((*s1 & m),32+b-1); //strip 1 data
        *dataBytes = *dataBytes + __ROR((*s2 & m),32+b-2); //strip 2 data
        *dataBytes = *dataBytes + __ROR((*s3 & m),32+b-3); //strip 3 data
        *dataBytes = *dataBytes + __ROR((*s4 & m),32+b-4); //strip 4 data
        *dataBytes = *dataBytes + __ROR((*s5 & m),32+b-5); //strip 5 data
        *dataBytes = *dataBytes + __ROR((*s6 & m),32+b-6); //strip 6 data
        *dataBytes = *dataBytes + __ROR((*s7 & m),32+b-7); //strip 7 data
        dataBytes++;
    }       
    s0 += 3;
    s1 += 3;
    s2 += 3;
    s3 += 3;
    s4 += 3;
    s5 += 3;
    s6 += 3;
    s7 += 3;
}while(n--);

S0 到 7 是 8 个单独的向量,以 24 个一组的形式从中获取位。 N 是组数,m 是掩码,b 是掩码位置。 dataBytes 是结果数组。

【问题讨论】:

  • Stackover 流程​​不适用于算法设计。你试过什么了? CPU 中是否有任何您认为可能有用的功能?当然,你来这里之前看过指令集吗?
  • 您的设备是否提供任何bit-band aliased 内存?那肯定是有用的。否则,对于可以用几十行 C 表示的一堆位操作,我不会为汇编而烦恼——考虑到 M3 的指令集有限以及你根本没有“足够”寄存器的事实可用的,您可以做的事情并不多,而一个好的编译器只需付出更少的努力就无法做到。
  • 感谢您的回复。 @artlessnoise 我不确定您试图传达什么,但是是的,我查看了指令集,并且对如何执行转置有了一个想法。实际上,其目的是让比我更有经验的人提出想法,以验证我自己的想法或改进他们。似乎有几种方法可以进行转置。黑客的喜悦(书)有一个可以执行它的洗牌算法,但我不确定如何将它应用于 70000x8 矩阵。我将发布我当前的代码,这样你就可以看到我试图做什么。
  • @Notlikethat M3 有位带区域,但我不确定如何使用它们。我正在查看组装的原因是因为循环必须迭代超过 24000 次。根据我的阅读,在这些情况下最好尽可能避免嵌套循环。
  • @artlessnoise 感谢您在下面的回复。事实证明,Cortex-M3 不支持 SIMD 指令。只有 M4 可以。事实证明,我最终使用了之前发布的代码的轻微变化。本质上,展开了 24 个内部循环并执行了一些内存对齐优化。表现是可以接受的。我会研究您的建议以进一步改进。

标签: arrays algorithm assembly matrix arm


【解决方案1】:

优化时总是存在两件事,

  1. 内存带宽
  2. CPU 时钟

带宽

您当前的算法一次加载一个字节。您可以通过一次加载至少 32 位来更有效地执行此操作。这将优化 ARM BUS。可以肯定的是,最终算法不会受 BUS 限制,如果是,您已经为此进行了优化。

对于不同的ARM CPU,有pld等指令可以通过提前预取下一个数据元素来尝试优化BUS。这可能适用于您的 Cortex-M,也可能不适用于您的 Cortex-M。另一种技术是尽可能将数据重新定位到更快的内存,例如 TCM。

CPU 速度

像素处理几乎总是由SIMD 类型指令加速。 Cortex-M 有指令标记 SIMD。不要沉迷于标签 SIMD;使用这个概念。如果你已经将多个字节加载到一个单词中,那么你可以使用一个表。

 const unsigned long bits[16] = {
              0,          1,      0x100,      0x101,
        0x10000,    0x10001,    0x10100,    0x10101,
      0x1000000,  0x1000001,  0x1000100,  0x1000101,
      0x1010000,  0x1010001,  0x1010100,  0x1010101
 }

Internet 上的许多 CRC 算法都使用了类似的概念。处理每个半字节(4 位)并一次形成下一个输出的四个字节。可能有一个乘法值可以替换表格,但这取决于您乘法的速度,这取决于 Cortex-M 和/或 ARM 的类型。

一定要在“C”中进行原型制作,然后尽可能转换为汇编程序或使用内联汇编程序。如果您的算法中有许多mov 语句,则表明编译器可能会比您更好地分配寄存器。许多复杂的算法使用代码生成器(用 phython、perl 等编写),它可以展开您最终得到的任何最佳循环,并以算法方式跟踪寄存器。

注意:仔细检查我的表格;这只是第一次破解,我还没有真正编码过这个特定的算法。一次处理多个位可能有更巧妙的方法,但这个想法可能是富有成效的。

【讨论】:

  • 如果你走这条路,将表格放入 TCM 对 Cortex-M 来说无疑是一个好处。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-26
  • 2021-04-23
  • 1970-01-01
  • 1970-01-01
  • 2011-05-15
  • 1970-01-01
  • 2015-06-11
相关资源
最近更新 更多