0

按照这个线程...

对于这段代码:

#include <stdio.h>

int main(void)
{
    int i;
    size_t u;

    for (i = 0; i < 10; i++) {
        u = (size_t)i;
        printf("i = %d, u = %zu\n", i, u);
    }
    return 0;
}

汇编中的输出是:

编辑:用 -O2 编译

    .file   "demo.c"
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "i = %d, u = %zu\n"
    .section    .text.startup,"ax",@progbits
    .p2align 4,,15
    .globl  main
    .type   main, @function
main:
.LFB3:
    .cfi_startproc
    pushq   %rbx
    .cfi_def_cfa_offset 16
    .cfi_offset 3, -16
    xorl    %ebx, %ebx
    .p2align 4,,10
    .p2align 3
.L2:
    movq    %rbx, %rdx
    movl    %ebx, %esi
    xorl    %eax, %eax
    movl    $.LC0, %edi
    addq    $1, %rbx
    call    printf
    cmpq    $10, %rbx
    jne .L2
    xorl    %eax, %eax
    popq    %rbx
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc
.LFE3:
    .size   main, .-main
    .ident  "GCC: (Debian 4.7.2-5) 4.7.2"
    .section    .note.GNU-stack,"",@progbits

转换是否u = (size_t)i;消耗额外的周期?

4

3 回答 3

5

是的,当然,随着代码的发布。您的转换在这里:

movl    -4(%rbp), %eax
cltq
movq    %rax, -16(%rbp)

当然,这段代码是未经优化的,所以这不是一个很公平的比较。如果您使用优化对其进行编译,编译器可能会意识到这些值始终是正数,并且只需从任何寄存器保存i%rdx保存第三个参数的单个移动。

编辑:

正如怀疑的那样,优化后的代码基本上没有开销。在这种情况下,编译器已将循环转换为 count up u,并派生iu而不是相反,因此%rbx用于循环,而 的值i只是 using %ebx,即 - 的低 32 位,%rbx所以有在这个例子中没有开销。我强调这一点,因为很可能在其他情况下从intto转换size_t会受到惩罚。这完全取决于情况。

于 2013-05-14T11:38:40.553 回答
4

是的,确实如此,因为它将内部表示从 32 位更改为 64 位。具体来说,

.L3:
    movl    -4(%rbp), %eax
    cltq
    movq    %rax, -16(%rbp)
    movq    -16(%rbp), %rdx

读取i、执行符号扩展并复制到%rdx. 我不确定为什么这个值必须通过堆栈 - 正如 mats 指出的那样,这看起来像是来自非优化编译器运行的代码。

编辑

在优化的汇编代码中,循环计数器被维护为更广泛的数据类型。公平地说,mov寄存器之间的 s 在运行时周期 wrt quad 或 dword 上没有区别(实际上它们没有区别:请参阅intels 相关文档中的表 C-16,此 SO post引用。

于 2013-05-14T11:46:09.700 回答
2

不确定这是否是为您消耗周期的实际分配我相信这是消耗周期的分配

例如在这个 t1.c

#include <stdio.h>

int main(void)
{
    int i;
    size_t u;

    for (i = 0; i < 10; i++) {
        printf("i = %d, u = %zu\n", i, u);
    }
    return 0;
}

以及 t1.c 的组装

        .file   "t1.c"
        .section        .rodata
.LC0:
        .string "i = %d, u = %zu\n"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        movl    $0, 24(%esp)
        jmp     .L2
.L3:
        movl    $.LC0, %eax
        movl    28(%esp), %edx
        movl    %edx, 8(%esp)
        movl    24(%esp), %edx
        movl    %edx, 4(%esp)
        movl    %eax, (%esp)
        call    printf
        addl    $1, 24(%esp)
.L2:
        cmpl    $9, 24(%esp)
        jle     .L3
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.6 20110731 (Red Hat 4.4.6-3)"
        .section        .note.GNU-stack,"",@progbits

在上述情况下,暂时没有任何任务

第二种情况 t2.c

#include <stdio.h>

int main(void)
{
    int i;
    size_t u;

    for (i = 0; i < 10; i++) {
        i = (size_t) u;
        printf("i = %d, u = %zu\n", i, u);
    }
    return 0;
}

以及随后的组装

        .file   "t2.c"
        .section        .rodata
.LC0:
        .string "i = %d, u = %zu\n"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        movl    $0, 24(%esp)
        jmp     .L2
.L3:
        movl    28(%esp), %eax
        movl    %eax, 24(%esp)
        movl    $.LC0, %eax
        movl    28(%esp), %edx
        movl    %edx, 8(%esp)
        movl    24(%esp), %edx
        movl    %edx, 4(%esp)
        movl    %eax, (%esp)
        call    printf
        addl    $1, 24(%esp)
.L2:
        cmpl    $9, 24(%esp)
        jle     .L3
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.6 20110731 (Red Hat 4.4.6-3)"
        .section        .note.GNU-stack,"",@progbits

检查上面的陈述

movl    28(%esp), %eax
movl    %eax, 24(%esp)

现在是最后一个例子 t3.c

#include <stdio.h>

int main(void)
{
    int i;
    int u;

    for (i = 0; i < 10; i++) {
        i = u;
        printf("i = %d, u = %zu\n", i, u);
    }
    return 0;
}

以及随后的组装

        .file   "t3.c"
        .section        .rodata
.LC0:
        .string "i = %d, u = %zu\n"
        .text
.globl main
        .type   main, @function
main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        subl    $32, %esp
        movl    $0, 24(%esp)
        jmp     .L2
.L3:
        movl    28(%esp), %eax
        movl    %eax, 24(%esp)
        movl    $.LC0, %eax
        movl    28(%esp), %edx
        movl    %edx, 8(%esp)
        movl    24(%esp), %edx
        movl    %edx, 4(%esp)
        movl    %eax, (%esp)
        call    printf
        addl    $1, 24(%esp)
.L2:
        cmpl    $9, 24(%esp)
        jle     .L3
        movl    $0, %eax
        leave
        ret
        .size   main, .-main
        .ident  "GCC: (GNU) 4.4.6 20110731 (Red Hat 4.4.6-3)"
        .section        .note.GNU-stack,"",@progbits

现在您可以观察 t2 和 t3 并在此处看到差异,但确实因拱门而异

于 2013-05-14T11:51:54.340 回答