int fact1(int limit)
{
for (i=1;i<limit;i++) fact *= i;
}
int fact2(int limit)
{
for (i=limit; i!=0; i--) fact *= i;
}
这个是当时上学老师出的一道题,我也觉得应该效率一样,不知道各位有什么不同看法
根据10楼兄弟的回答,我用objdump的反汇编结果如下:
00000000004004c2 <fact1>:
4004c2: 55 push %rbp
4004c3: 48 89 e5 mov %rsp,%rbp
4004c6: 89 7d ec mov %edi,-0x14(%rbp)
4004c9: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)
4004d0: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
4004d7: c7 45 f8 01 00 00 00 movl $0x1,-0x8(%rbp)
4004de: eb 0e jmp 4004ee <fact1+0x2c>
4004e0: 8b 45 fc mov -0x4(%rbp),%eax
4004e3: 0f af 45 f8 imul -0x8(%rbp),%eax
4004e7: 89 45 fc mov %eax,-0x4(%rbp)
4004ea: 83 45 f8 01 addl $0x1,-0x8(%rbp)
4004ee: 8b 45 f8 mov -0x8(%rbp),%eax
4004f1: 3b 45 ec cmp -0x14(%rbp),%eax
4004f4: 7c ea jl 4004e0 <fact1+0x1e>
4004f6: 5d pop %rbp
4004f7: c3 retq
00000000004004f8 <fact2>:
4004f8: 55 push %rbp
4004f9: 48 89 e5 mov %rsp,%rbp
4004fc: 89 7d ec mov %edi,-0x14(%rbp)
4004ff: c7 45 f8 00 00 00 00 movl $0x0,-0x8(%rbp)
400506: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%rbp)
40050d: 8b 45 ec mov -0x14(%rbp),%eax
400510: 89 45 f8 mov %eax,-0x8(%rbp)
400513: eb 0e jmp 400523 <fact2+0x2b>
400515: 8b 45 fc mov -0x4(%rbp),%eax
400518: 0f af 45 f8 imul -0x8(%rbp),%eax
40051c: 89 45 fc mov %eax,-0x4(%rbp)
40051f: 83 6d f8 01 subl $0x1,-0x8(%rbp)
400523: 83 7d f8 00 cmpl $0x0,-0x8(%rbp)
400527: 75 ec jne 400515 <fact2+0x1d>
400529: 8b 45 fc mov -0x4(%rbp),%eax
40052c: 5d pop %rbp
40052d: c3 retq
应该和你的觉过一样,减法可以直接判断,加法多了一个mov,但是我这边怎么这么繁琐,没你的简易
反汇编一下就知道区别了。
循环条件的判断,一个是迭代量跟某个固定值的比较(cmp %eax, XXX),一个是迭代量是否为0的比较(test %eax, %eax)。
cmp和test的效率有区别吗?
-O3之后,就会发现,循环体是4条指令跟3条指令的区别。
也就是说,可以直接利用减法的结果来进行判断调整,加法则不行,还需要额外的比较。
.L4:
imull %eax, %edx
addl $1, %eax
cmpl %edi, %eax
jne .L4
.L14:
imull %eax, %edx
subl $1, %eax
jne .L14
效率一样,没有任何区别。
没区别。
你又没有把n变成log(n),这样的效率比较有什么意义?
我知道++i的效率是高于i++的,
你这两个函数,从遍历角度来看,都是o(n)的
如果真的要讨论效率的话,我只能说,我觉得前面那个要快一点,因为,一开始乘的数字会小一点,大概也许会快一点吧
除非你的fact是一个非常奇怪的数据类型,它和不同大小的i相乘所耗费的时间还不同。
你分别大括号里的for之前一个记录时间,for之后一个记录时间,然后相减,分别看一下它们执行的时间毫秒差就可以看出来了。
这应该效率一样的把, 不觉得会有什么区别
我当时也觉得应该效率一样,不过现在的看法是:
i=1中,只要读i的地址,然后把1传递即可
i=limit中,既要读i的地址,也要读limit的地址,然后再传值,所以我感觉是第二个效率高,大家觉得呢
int fact1(int limit)
{
for (i=1;i<limit;i++) fact *= i;
}
int fact2(int limit)
{
for (i=limit; i!=0; i--) fact *= i;
}
你老师当时没给你答案吗,哎,看limit的取值范围,上面下面进入循环里面的情况不一样
感觉一样都是O(n)。
一样! 编译器都应该会优化循环limit次
我怎么感觉会报未定义的i、fact错误呢
[XXXXX ~]$ cat x.c
int fact1(int limit)
{
int i, n = 1;
for (i = 0; i < limit; i++)
n *= i;
return n;
}
int fact2(int limit)
{
int i, n = 1;
for (i = limit; i != 0; i--)
n *= i;
return n;
}
[XXXXX ~]$ gcc -S x.c -o -
.file "x.c"
.text
.globl fact1
.type fact1, @function
fact1:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -20(%rbp)
movl $1, -4(%rbp)
movl $0, -8(%rbp)
jmp .L2
.L3:
movl -4(%rbp), %eax
imull -8(%rbp), %eax
movl %eax, -4(%rbp)
addl $1, -8(%rbp)
.L2:
movl -8(%rbp), %eax
cmpl -20(%rbp), %eax
jl .L3
movl -4(%rbp), %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size fact1, .-fact1
.globl fact2
.type fact2, @function
fact2:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -20(%rbp)
movl $1, -4(%rbp)
movl -20(%rbp), %eax
movl %eax, -8(%rbp)
jmp .L6
.L7:
movl -4(%rbp), %eax
imull -8(%rbp), %eax
movl %eax, -4(%rbp)
subl $1, -8(%rbp)
.L6:
cmpl $0, -8(%rbp)
jne .L7
movl -4(%rbp), %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size fact2, .-fact2
.ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-4)"
.section .note.GNU-stack,"",@progbits
[XXXXX ~]$ gcc -S x.c -o - -O3
.file "x.c"
.text
.p2align 4,,15
.globl fact1
.type fact1, @function
fact1:
.LFB0:
.cfi_startproc
xorl %edx, %edx
testl %edi, %edi
movl $1, %eax
jle .L3
.p2align 4,,10
.p2align 3
.L6:
imull %edx, %eax
addl $1, %edx
cmpl %edi, %edx
jne .L6
.L3:
rep
ret
.cfi_endproc
.LFE0:
.size fact1, .-fact1
.p2align 4,,15
.globl fact2
.type fact2, @function
fact2:
.LFB1:
.cfi_startproc
testl %edi, %edi
movl $1, %eax
je .L11
.p2align 4,,10
.p2align 3
.L14:
imull %edi, %eax
subl $1, %edi
jne .L14
.L11:
rep
ret
.cfi_endproc
.LFE1:
.size fact2, .-fact2
.ident "GCC: (GNU) 4.4.7 20120313 (Red Hat 4.4.7-4)"
.section .note.GNU-stack,"",@progbits
我的汇编如下
ubuntu:~/data$ gcc -S x.c -o - -O3
.file "x.c"
.text
.p2align 4,,15
.globl fact1
.type fact1, @function
fact1:
.LFB24:
.cfi_startproc
rep
ret
.cfi_endproc
.LFE24:
.size fact1, .-fact1
.p2align 4,,15
.globl fact2
.type fact2, @function
fact2:
.LFB25:
.cfi_startproc
rep
ret
.cfi_endproc
.LFE25:
.size fact2, .-fact2
.globl fact
.bss
.align 4
.type fact, @object
.size fact, 4
fact:
.zero 4
.comm limit,4,4
.comm i,4,4
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",@progbits
ubuntu:~/data$ gcc -S x.c -o - -O1
.file "x.c"
.text
.globl fact1
.type fact1, @function
fact1:
.LFB24:
.cfi_startproc
cmpl $1, %edi
jle .L4
movl $1, %eax
.L3:
addl $1, %eax
cmpl %edi, %eax
jne .L3
.L4:
rep
ret
.cfi_endproc
.LFE24:
.size fact1, .-fact1
.globl fact2
.type fact2, @function
fact2:
.LFB25:
.cfi_startproc
testl %edi, %edi
je .L9
.L8:
subl $1, %edi
jne .L8
.L9:
rep
ret
.cfi_endproc
.LFE25:
.size fact2, .-fact2
.globl fact
.bss
.align 4
.type fact, @object
.size fact, 4
fact:
.zero 4
.comm limit,4,4
.comm i,4,4
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",@progbits