关注thread ...
对于这段代码:
#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;
是否正在消耗额外的周期?
答案 0 :(得分:5)
是的,因为代码已发布,当然。您的转换位于:
movl -4(%rbp), %eax
cltq
movq %rax, -16(%rbp)
当然,这段代码没有经过优化,所以这不是一个非常公平的比较。如果使用优化对其进行编译,编译器可能会意识到值始终为正,只需从保存第三个参数的任何寄存器i
到%rdx
进行一次移动。
编辑:
如所怀疑的,优化代码中基本上没有开销。在这种情况下,编译器已将循环转换为向上计数u
,并从i
派生u
而不是相反,因此%rbx
用于循环,i
的值仅使用%ebx
,这是%rbx
的低32位 - 因此在此示例中没有开销。我强调这一点,因为可能存在其他情况,从int
转换为size_t
将会受到惩罚。这完全取决于具体情况。
答案 1 :(得分:4)
是的,确实如此,因为它将内部表示从32位更改为64位。 具体来说,
.L3:
movl -4(%rbp), %eax
cltq
movq %rax, -16(%rbp)
movq -16(%rbp), %rdx
读取i
,执行符号扩展并复制到%rdx
。我不确定为什么这个值必须通过堆栈 - 正如垫子所指出的,这看起来像是来自非优化编译器运行的代码。
修改强>
在优化的汇编代码中,循环计数器保持为更宽的数据类型。 afair,寄存器之间的mov
在运行时周期中没有区别于四字或双字(实际上它们没有:见表{C-16 in intels pertinent doc,由this SO post引用。< / p>
答案 2 :(得分: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,看看这里的区别,但是从拱到拱的确不同