objective-c - 通过指针运算与 C 中的下标访问数组值

我一直在读到,在 C 中,使用指针算术通常比数组访问的下标更快。即使使用现代(据称是优化的)编译器也是如此吗?

如果是这样,当我开始从学习 C 转向 Objective-C 和 Cocoa 时,情况是否仍然如此?在 Mac 上?

在 C 和 Objective-C 中,哪种数组访问的首选编码风格是?哪个(由各自语言的专业人士)认为更清晰、更“正确”(因为缺乏更好的术语)?

最佳答案

您需要了解这种说法背后的原因。你有没有问过自己为什么它更快?让我们比较一些代码:

int i;
int a[20];

// Init all values to zero
memset(a, 0, sizeof(a));
for (i = 0; i < 20; i++) {
    printf("Value of %d is %d\n", i, a[i]);
}
它们都是零,真是令人惊讶:-P 问题是,a[i] 意味着什么?实际上是低级机器代码?它的意思是
  • 取地址a在内存中。
  • 添加 i乘以单个项目的大小 a到那个地址(int 通常是四个字节)。
  • 从该地址获取值。

  • 所以每次你从 a 获取一个值时,a的基地址与 i 的乘法结果相加由四。如果只是解引用一个指针,则不需要执行步骤1.和2.,只需执行步骤3。
    考虑下面的代码。
    int i;
    int a[20];
    int * b;
    
    memset(a, 0, sizeof(a));
    b = a;
    for (i = 0; i < 20; i++) {
        printf("Value of %d is %d\n", i, *b);
        b++;
    }
    
    此代码可能更快......但即使是这样,差异也很小。为什么会更快? “*b”与上述第 3 步相同。但是,"b++"与步骤 1. 和步骤 2 不同。 "b++"将指针增加 4。

    (important for newbies: running ++ on a pointer will not increase the pointer one byte in memory! It will increase the pointer by as many bytes in memory as the data it points to is in size. It points to an int and the int is four bytes on my machine, so b++ increases b by four!)


    好的,但为什么它会更快?因为给一个指针加四比乘 i 快按四并将其添加到指针。在任何一种情况下,您都有一个加法,但在第二个中,您没有乘法(您避免了一次乘法所需的 CPU 时间)。考虑到现代 CPU 的速度,即使阵列是 1 个 mio 元素,我想知道您是否真的可以对差异进行基准测试。
    您可以通过查看它生成的程序集输出来检查现代编译器可以优化任何一个以同样快的速度。您可以通过将“-S”选项(大写 S)传递给 GCC 来实现。
    这是第一个C代码的代码(使用了优化级别-Os,这意味着优化代码大小和速度,但不要做会显着增加代码大小的速度优化,不像-O2,更不像-O3 ):
    _main:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %edi
        pushl   %esi
        pushl   %ebx
        subl    $108, %esp
        call    ___i686.get_pc_thunk.bx
    "L00000000001$pb":
        leal    -104(%ebp), %eax
        movl    $80, 8(%esp)
        movl    $0, 4(%esp)
        movl    %eax, (%esp)
        call    L_memset$stub
        xorl    %esi, %esi
        leal    LC0-"L00000000001$pb"(%ebx), %edi
    L2:
        movl    -104(%ebp,%esi,4), %eax
        movl    %eax, 8(%esp)
        movl    %esi, 4(%esp)
        movl    %edi, (%esp)
        call    L_printf$stub
        addl    $1, %esi
        cmpl    $20, %esi
        jne L2
        addl    $108, %esp
        popl    %ebx
        popl    %esi
        popl    %edi
        popl    %ebp
        ret
    
    与第二个代码相同:
    _main:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %edi
        pushl   %esi
        pushl   %ebx
        subl    $124, %esp
        call    ___i686.get_pc_thunk.bx
    "L00000000001$pb":
        leal    -104(%ebp), %eax
        movl    %eax, -108(%ebp)
        movl    $80, 8(%esp)
        movl    $0, 4(%esp)
        movl    %eax, (%esp)
        call    L_memset$stub
        xorl    %esi, %esi
        leal    LC0-"L00000000001$pb"(%ebx), %edi
    L2:
        movl    -108(%ebp), %edx
        movl    (%edx,%esi,4), %eax
        movl    %eax, 8(%esp)
        movl    %esi, 4(%esp)
        movl    %edi, (%esp)
        call    L_printf$stub
        addl    $1, %esi
        cmpl    $20, %esi
        jne L2
        addl    $124, %esp
        popl    %ebx
        popl    %esi
        popl    %edi
        popl    %ebp
        ret
    
    嗯,这是不同的,这是肯定的。 104 和 108 的数字差异来自变量 b (在第一个代码中,堆栈上少了一个变量,现在我们多了一个,改变了堆栈地址)。 for中的真实代码差异循环是
    movl    -104(%ebp,%esi,4), %eax
    
    相比
    movl    -108(%ebp), %edx
    movl    (%edx,%esi,4), %eax
    
    实际上在我看来,第一种方法似乎更快(!),因为它发出一个 CPU 机器代码来执行所有工作(CPU 为我们完成所有工作),而不是有两个机器代码。另一方面,下面的两个汇编命令的运行时间可能比上面的要短。
    作为结束语,我想说取决于您的编译器和 CPU 功能(CPU 提供什么命令以何种方式访问​​内存),结果可能是两种方式。任何一个都可能更快/更慢。除非您将自己完全限制为一个编译器(也意味着一个版本)和一个特定的 CPU,否则您无法确定。由于 CPU 可以在单个汇编命令中执行越来越多的操作(很久以前,编译器确实必须手动获取地址,将 i 乘以 4,然后在获取值之前将两者相加),这些语句曾经是绝对真理古往今来越来越值得怀疑。还有谁知道 CPU 内部是如何工作的?上面我将一个组装指令与另外两个指令进行了比较。
    我可以看到指令的数量不同,这样的指令需要的时间也可能不同。此外,这些指令在其机器演示中需要多少内存(毕竟它们需要从内存传输到 CPU 缓存)是不同的。然而,现代 CPU 不会按照您提供指令的方式执行指令。它们将大指令(通常称为 CISC)拆分为小的子指令(通常称为 RISC),这也使它们能够在内部更好地优化程序流程以提高速度。事实上,第一条指令和下面的两条其他指令可能会产生相同的子指令集,在这种情况下,没有任何可测量的速度差异。
    关于Objective-C,它只是带有扩展的C。因此,在 C 语言中适用的所有内容都将适用于 Objective-C,也适用于指针和数组。另一方面,如果您使用对象(例如, NSArrayNSMutableArray ),这是完全不同的野兽。但是,在这种情况下,无论如何您都必须使用方法访问这些数组,没有可供选择的指针/数组访问。

    https://stackoverflow.com/questions/233148/

    相关文章:

    ios - 将 NSInteger 转换为 NSIndexpath

    ios - 在objective-c中导入swift类,-Swift.h文件未找

    objective-c - 'pod install' 不会更新现有的 pod

    objective-c - 适合 UILabel 中的文本

    iphone - 什么是-[NSString sizeWithFont :forWidth:line

    objective-c - 使用自动布局自定义纵横比

    objective-c - 恢复 iOS7 之前的 UINavigationController p

    iphone - 如何使用第一个字符作为部分名称

    iphone - animateWithDuration :animations: block ma

    iphone - UITableView/UITableViewCell 点击事件响应?