在C中,访问我的数组索引是更快的还是通过指针访问速度更快?

前端之家收集整理的这篇文章主要介绍了在C中,访问我的数组索引是更快的还是通过指针访问速度更快?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
在C中,访问数组索引更快或者通过指针访问速度更快?
我的意思是说,哪一个会花更少的时钟周期.
数组不是一个常量数组.

解决方法

templatetypedef已经总结了.为他的回应添加一些支持.采取这些示例功能
unsigned int fun1 ( unsigned int *x )
{
    unsigned int ra,rb;

    rb=0;
    for(ra=0;ra<1000;ra++) rb+=*x++;
    return(rb);
}

unsigned int fun2 ( unsigned int *x )
{
    unsigned int ra,rb;
    rb=0;
    for(ra=0;ra<1000;ra++) rb+=x[ra];
    return(rb);
}

现在gcc产生了这个:

00000000 fun1:
   0:   e52d4004    push    {r4}        ; (str r4,[sp,#-4]!)
   4:   e1a03000    mov r3,r0
   8:   e2804efa    add r4,r0,#4000   ; 0xfa0
   c:   e3a00000    mov r0,#0
  10:   e1a02003    mov r2,r3
  14:   e492c004    ldr ip,[r2],#4
  18:   e5931004    ldr r1,[r3,#4]
  1c:   e2823004    add r3,r2,#4
  20:   e080000c    add r0,ip
  24:   e1530004    cmp r3,r4
  28:   e0800001    add r0,r1
  2c:   1afffff7    bne 10 
  30:   e49d4004    pop {r4}        ; (ldr r4,[sp],#4)
  34:   e12fff1e    bx  lr

00000038 fun2:
  38:   e3a03000    mov r3,#0
  3c:   e1a02003    mov r2,r3
  40:   e790c003    ldr ip,[r0,r3]
  44:   e2833004    add r3,r3,#4
  48:   e7901003    ldr r1,r3]
  4c:   e2833004    add r3,#4
  50:   e082200c    add r2,ip
  54:   e3530efa    cmp r3,#4000   ; 0xfa0
  58:   e0822001    add r2,r1
  5c:   1afffff7    bne 40 
  60:   e1a00002    mov r0,r2
  64:   e12fff1e    bx  lr

代码是不同的,但我很惊讶于错过的优化机会.

Clang / llvm产生:


00000000 fun1:
   0:   e3a01000    mov r1,#0
   4:   e3a02ffa    mov r2,#1000   ; 0x3e8
   8:   e1a03001    mov r3,r1
   c:   e2522001    subs    r2,#1
  10:   e490c004    ldr ip,[r0],#4
  14:   e08c3003    add r3,ip,r3
  18:   e2c11000    sbc r1,r1,#0
  1c:   e182c001    orr ip,r1
  20:   e35c0000    cmp ip,#0
  24:   1afffff8    bne c 
  28:   e1a00003    mov r0,r3
  2c:   e12fff1e    bx  lr

00000030 fun2:
  30:   e3a01000    mov r1,#0
  34:   e3a02ffa    mov r2,#1000   ; 0x3e8
  38:   e1a03001    mov r3,r1
  3c:   e2522001    subs    r2,#1
  40:   e490c004    ldr ip,#4
  44:   e08c3003    add r3,r3
  48:   e2c11000    sbc r1,#0
  4c:   e182c001    orr ip,r1
  50:   e35c0000    cmp ip,#0
  54:   1afffff8    bne 3c
  58:   e1a00003    mov r0,r3
  5c:   e12fff1e    bx  lr

您可能会注意到编译器产生完全相同的代码,指针或偏移量.通过改变编译器,我比改变指针对数组索引更好.我认为llvm可以做得更好一些,我将需要更多的了解这些,以了解我的代码做了什么导致这一点.

编辑:

我希望使编译器至少使用有利于指针的ldr rd,[rs],#4指令,并希望编译器会看到它可能会破坏数组地址,从而将其视为指针而不是偏移量变成一个数组(并使用上面的指令,这基本上是clang / llvm做的).或者如果它使用数组的东西,它将使用ldr rd,[rm,rn]指令.基本上希望编译器之一能够产生以下解决方案之一:


funa:
    mov r1,#0
    mov r2,#1000
funa_loop:
    ldr r3,#4
    add r1,r3
    subs r2,#1
    bne funa_loop
    mov r0,r1
    bx lr

funb:
    mov r1,#0
funb_loop:
    ldr r3,r2]
    add r1,r3
    add r2,#4
    cmp r2,#0x4000
    bne funb_loop
    mov r0,r1
    bx lr

func:
    mov r1,#4000
    subs r2,#4
func_loop:
    beq func_done
    ldr r3,#4
    b func_loop
func_done:
    mov r0,r1
    bx lr

没有相当的到达那里,但相当接近.这是一个有趣的运动.注意以上是所有的ARM汇编器.

一般来说,(不是我的具体的C代码示例而不一定是ARM),一些流行的架构,您将从一个基于寄存器的地址(ldr r0,[r1])和一个寄存器索引/偏移量的负载(ldr r0,[r1,r2]),其中地址是两个寄存器的总和.一个寄存器理想地是阵列的基址,第二个是索引/偏移量.寄存器的前一个负载自身指向指针,后者指向数组.如果您的C程序不会更改或移动指针或索引,那么在这两种情况下,这意味着一个静态地址被计算,那么使用正常的负载,数组和指针都应该产生相同的指令.对于更有意思的改变指针/索引的情况.

Pointer

ldr r0,[r1]
...
add r1,some number

Array index

ldr r0,r2]
...
add r2,some number

(根据需要用商店替换负载添加子)

一些架构没有三个注册寄存器索引指令,所以你必须做一些事情

array index:
mov r2,r1
...
ldr r0,[r2]
...
add r2,some number

或者根据编译器可能会变得非常糟糕,esp如果编译用于调试或没有优化,并假设您没有三个寄存器添加

array index:
mov r2,#0
...
mov r3,r1
add r3,r2
ldr r4,[r3]
...
add r2,some number

所以这两种方法很可能是相等的.如在ARM上看到的,它可以将两个(即时)指针指针组合在一起,使其更快一点.阵列索引解决方案会烧写更多的寄存器,并且取决于架构的可用寄存器数量,这些寄存器可能会将寄存器交换到堆栈的时间越来越快(比使用指针)更慢.如果您不介意摧毁基地址,底线是指针解决方案可能会从性能角度给您一个优势.它与您的代码和编译器有很大关系.对我来说,它的可读性发挥作用,我觉得数组更容易阅读和跟随,第二个我需要保留该指针释放一个malloc或再次通过该内存等.如果是这样我可能会使用一个数组与一个索引,如果是一次性通过,我不关心摧毁基地址,我将使用一个指针.如上所述,使用编译器生成代码,如果性能至关重要,那么手工编译汇编器中的解决方案(根据建议的方法,让编译器先尝试一下).

猜你在找的C&C++相关文章