为ARMv5优化一个读取1..4字节长小端整数的函数

时间:2012-03-03 17:47:37

标签: arm

x86的代码执行此操作(n只能是1到4,在编译时未知):

static const uint32_t wordmask[] = {
  0u, 0xffu, 0xffffu, 0xffffffu, 0xffffffffu
};
static inline uint32_t get_unaligned_le_x86(const void *p, uint32_t n) {
  uint32_t ret = *(const uint32_t *)p & wordmask[n];
  return ret;
}

对于没有未对齐的32位小端序加载的架构,我有两种变体:

static uint32_t get_unaligned_le_v1(const void *p, uint32_t n) {
  const uint8_t *b = (const uint8_t *)p;
  uint32_t ret;
  ret = b[0];
  if (n > 1) {
    ret |= b[1] << 8;
    if (n > 2) {
      ret |= b[2] << 16;
      if (n > 3) {
        ret |= b[3] << 24;
      }
    }
  }
  return ret;
}

static uint32_t get_unaligned_le_v2(const void *p, uint32_t n) {
  const uint8_t *b = (const uint8_t *)p;
  uint32_t ret = b[0] | (b[1] << 8) | (b[2] << 16) | (b[3] << 24);
  ret &= wordmask[n];
  return ret;
}

在读取硬件上会更好(我使用qemu进行开发),你能建议更快的替代方案吗?如果速度快得多,我就可以使用汇编了。

2 个答案:

答案 0 :(得分:4)

ARM上的条件执行是提高性能的最佳选择。 ARM上的表查找(掩码)肯定会慢一些。这是我的ARMv5实现:

// When called from C, r0 = first parameter, r1 = second parameter
// r0-r3 and r12 can get trashed by C functions
unaligned_read:
  ldrb r2,[r0],#1      ; byte 0 is always read (n=1..4)
  cmp r1,#2
  ldrgeb r3,[r0],#1   ; byte 1, n >= 2
  ldrgtb r12,[r0],#1  ; byte 2, n > 2
  orrge r2,r2,r3,LSL #8
  orrgt r2,r2,r12,LSL #16
  cmp r1,#4
  ldreqb r3,[r0],#1   ; byte 3, n == 4
  movne r0,r2         ; recoup wasted cycle
  orreq r0,r2,r3,LSL #24
  mov pc,lr           ; or "bx lr" for thumb compatibility

更新:将ldreqb修复为ldrgeb

更新2 :通过在最后一个ldr / orr之间插入指令来削减另一个周期

答案 1 :(得分:0)

你需要测试一下。这很难说,因为它不仅取决于处理器架构,还取决于编译器,编译标志和目标系统。

这是另一个可以消除分支和表查找(未经测试的代码)的想法/技巧:

char mask1 = -(n>1); // 0 if n<=1, 0xFF otherwise
char mask2 = -(n>2);
char mask3 = -(n>3);
ret = b[0];
ret |= (b[1] & mask1) << 8;
ret |= (b[2] & mask2) << 16;
ret |= (b[3] & mask3) << 24;

请注意,这是您的第二个功能,可以读取输入的结尾,这可能是也可能不是问题。

我生成了这个代码,看起来不那么糟糕(15个指令,没有分支,没有表查找):

cmp r1, #2
ldrb    r2, [r0, #2]    @ zero_extendqisi2
ldrb    r4, [r0, #1]    @ zero_extendqisi2
movls   r2, #0
cmp r1, #1
ldrb    ip, [r0, #0]    @ zero_extendqisi2
movls   r4, #0
mov r3, r2, asl #16
ldrb    r2, [r0, #3]    @ zero_extendqisi2
cmp r1, #3
orr r0, r3, r4, asl #8
orr r3, r0, ip
movhi   r1, r2
movls   r1, #0
orr r0, r3, r1, asl #24

我要尝试的另一件事是重写你的第二个函数:

  if (n > 1) {
    ret |= b[1] << 8;
  }
  if (n > 2) {
    ret |= b[2] << 16;
  }
  if (n > 3) {
    ret |= b[3] << 24;
  }

由于编译器可以更好地使用conditional executio n,这比条件分支更快。

如果它的运行速度非常快,我会考虑在ARM程序集中编写它。