Assembly 正确使用ARM PLD指令(ARM11)

Assembly 正确使用ARM PLD指令(ARM11),assembly,arm,armv6,Assembly,Arm,Armv6,ARM实际上并没有以正确的方式在这条指令上给出太多的用法,但我发现它在其他地方被用来知道它以地址作为在哪里读取下一个值的提示 我的问题是,给定一个256字节的紧凑拷贝循环ldm/stm指令,比如r4-r11 x 8,在每个指令对之间的拷贝之前预取每个缓存线是更好的,还是根本不这样做,因为所讨论的memcpy不同时读取和写入同一内存区域。非常确定我的缓存线大小是64字节,但可能是32字节-在这里编写最终代码之前等待确认。来自第17.4章(注意:ARM11的某些细节可能不同): memcpy()的最

ARM实际上并没有以正确的方式在这条指令上给出太多的用法,但我发现它在其他地方被用来知道它以地址作为在哪里读取下一个值的提示

我的问题是,给定一个256字节的紧凑拷贝循环
ldm/stm
指令,比如r4-r11 x 8,在每个指令对之间的拷贝之前预取每个缓存线是更好的,还是根本不这样做,因为所讨论的
memcpy
不同时读取和写入同一内存区域。非常确定我的缓存线大小是64字节,但可能是32字节-在这里编写最终代码之前等待确认。

来自第17.4章(注意:ARM11的某些细节可能不同):

memcpy()的最佳性能是 使用整个缓存的LDM实现 行,然后写入这些值 使用整个缓存线的STM。 商店的排列更为合理 这比荷载的对齐更重要。 应使用PLD指令 如果可能的话。有四个PLD 加载/存储单元中的插槽。可编程逻辑器件 指令优先于指令 自动预取器,无成本 就整数管道而言 演出PLD的精确定时 memcpy()可以实现的最佳功能的说明 系统之间略有不同,但PLD 到前面三条缓存线的地址 当前复制行的 有用的起点


Linux内核中提供了一个合理通用的复制循环示例,该循环使用cacheline大小的
LDM
/
STM
块和/或
PLD
(如果可用)。这实现了Igor上面提到的关于预加载使用的内容,并说明了阻塞

请注意,在ARMv7(其中缓存线大小通常为64字节)上,不可能将完整缓存线作为一个单独的操作(只有14个reg可以使用,因为无法触摸
SP
/
PC
)。因此,您可能需要使用两对/四对
LDM
/
STM

才能真正获得“最快”的ARM asm代码,您需要在系统上测试不同的方法。就ldm/stm循环而言,这一循环似乎对我最有效:

  // Use non-conflicting register r12 to avoid waiting for r6 in pld

  pld [r6, #0]
  add r12, r6, #32

1:
  ldm r6!, {r0, r1, r2, r3, r4, r5, r8, r9}
  pld   [r12, #32]
  stm r10!, {r0, r1, r2, r3, r4, r5, r8, r9}
  subs r11, r11, #16
  ldm r6!, {r0, r1, r2, r3, r4, r5, r8, r9}
  pld   [r12, #64]
  stm r10!, {r0, r1, r2, r3, r4, r5, r8, r9}
  add r12, r6, #32
  bne 1b

上面的块假设您已经设置了r6、r10、r11,并且该循环对r11的字(而不是字节)进行倒计时。我已经在Cortex-A9(iPad2)上测试过了,在那个处理器上似乎有很好的效果。但要小心,因为在Cortex-A8(iPhone4)上,霓虹灯环路似乎比ldm/stm快,至少对于更大的副本是如此。

在这种情况下,更好是什么意思?更快=在这种情况下更好。前面有3条缓存线。。。因此,对于64字节缓存线上的256字节副本,听起来我应该继续预取所有256字节。嗯,我没想到去看手册。可能应该有。我的系统是一个32字节的cachline线路,而且我们的总线速度很慢。因此,在块指令之间交错以隐藏延迟是一种可行的方法。结果还表明,一次复制超过16个字节会使缓存的速度过快,也会减慢速度。ARM可以成为一种漂亮的语言,但它仍然需要适当的内存子系统来很好地利用它。有关实际的代码示例和基准测试,请参阅。我发现这很有用,也很有趣:您可以在其中使用SP,您只需将其正确存储在某个位置,并确保您的上下文切换代码没有延迟到足以从您的执行域(USR/SYS)使用SP。如果您“将SP存储在某个位置”,以便“释放”reg以与
LDM
/
STM
一起使用,则“在某个位置”通常最终成为一个全局位置或
PC
相对位置。这两种方法都使代码不具有线程安全性,而后者需要可写代码(这通常不是真的)。在某些情况下可以接受,但在其他情况下不可以。还请注意,您需要为
LDM
/
STM
源/目标地址提供一个寄存器。在任何情况下,如果使用非vFP/Neon,您都不能在ARMv6/7上使用一条指令将整个缓存线加载到regs。您能解释一下这是如何使我的代码线程不安全的吗?为什么它需要可写代码?我认为“adr”指令是为了这个特定的目的而制定的,这样我就不必手动处理与pc相关的负载。带有常量的LDR也是一条与pc相关的指令,这会使代码不安全吗?您可以使用ARM寄存器加载/存储整个缓存线,具体取决于cpu缓存线的大小。我使用过32字节缓存线和128字节缓存线的cpu,这都取决于目标。这是因为以PC相对方式访问的地址(并且在
adr
的范围限制内)在进程的地址空间内仍然是唯一的(因此是一个全局变量),对于
PC
的大多数值,代码中的一个位置(至少在Linux上,它通常映射为不可写)。@user1075375:您似乎不理解全局变量的概念—一个进程的所有线程所共有的存储位置。如果没有序列化/锁定,它们的使用总是线程不安全的。如果您的代码依赖于这样一个特定的位置,那么无论您如何检索指向该位置的指针,您的代码都无法在多线程进程中运行。