Nig*_*ht0 5 javascript c c++ java optimization
我读了一些关于提升和重新排序的内容,因此似乎Java VM可能会选择提升某些表达式.我还读到了在Javascript中提升函数声明的问题.
第一个问题: 有人可以确认在C,C++和Java中是否通常存在提升?或者它们都依赖于编译器/优化?
我读了很多示例C代码,它们总是在任何断言或边界条件之前将变量声明置于顶部.我认为在变量声明之前执行所有断言和边界情况会更快一些,因为函数可能只是终止.
主要问题: 变量声明必须始终在上下文中排在最前面吗?(这里有提升工作吗?)或者编译器是否通过首先检查这些独立的断言和边界情况(在无关变量声明之前)来自动优化代码?
这是一个相关的例子:
void MergeSort(struct node** headRef) {
struct node* a;
struct node* b;
if ((*headRef == NULL) || ((*headRef)->next == NULL)) {
return;
}
FrontBackSplit(*headRef, &a, &b);
MergeSort(&a);
MergeSort(&b);
*headRef = SortedMerge(a, b);
}
Run Code Online (Sandbox Code Playgroud)
如上所示,边界情况不依赖于变量"a"和"b".因此,将边界情况放在变量声明之上会使它稍快一些吗?
更新:
上面的例子并不像我希望的那样好,因为变量"a"和"b"只是声明,而不是在那里初始化.在我们确实需要使用它们之前,编译器会忽略声明.
我通过初始化检查了GNU GCC程序集的变量声明,程序集具有不同的执行顺序.编译器没有改变我对独立断言和边界情况的排序. 因此,重新排序这些断言和边界情况会改变程序集,从而改变机器运行它们的方式.
我认为差异是微不足道的,大多数人从不关心这一点.
编译器可以根据需要重新排序/修改您的代码,只要修改后的代码在顺序执行时与原始代码等效。所以吊装是允许的,但不是必需的。这是一种优化,它完全是特定于编译器的。
C++ 中的变量声明可以随心所欲。在 C 中,它们曾经必须在上下文中处于首位,但是当引入 c99 标准时,规则放宽了,现在它们可以随心所欲,类似于 C++。尽管如此,许多 c 程序员仍然坚持将它们放在上下文中。
在您的示例中,编译器可以自由地将 if 语句移到顶部,但我认为不会。这些变量只是在堆栈上声明并且未初始化的指针,声明它们的成本很小,而且在函数的开头而不是在断言之后创建它们可能更有效。
如果您的声明会涉及任何副作用,例如
struct node *a = some_function();
Run Code Online (Sandbox Code Playgroud)
那么编译器将限制它可以重新排序的内容。
编辑:
我用这个简短的程序在实践中检查了 GCC 的循环提升:
#include <stdio.h>
int main(int argc, char **argv) {
int dummy = 2 * argc;
int i = 1;
while (i<=10 && dummy != 4)
printf("%d\n", i++);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我用这个命令编译了它:
gcc -std=c99 -pedantic test.c -S -o test.asm
Run Code Online (Sandbox Code Playgroud)
这是输出:
.file "test.c"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "%d\12\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB7:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
call ___main
movl 8(%ebp), %eax
addl %eax, %eax
movl %eax, 24(%esp)
movl $1, 28(%esp)
jmp L2
L4:
movl 28(%esp), %eax
leal 1(%eax), %edx
movl %edx, 28(%esp)
movl %eax, 4(%esp)
movl $LC0, (%esp)
call _printf
L2:
cmpl $10, 28(%esp)
jg L3
cmpl $4, 24(%esp)
jne L4
L3:
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE7:
.ident "GCC: (GNU) 4.8.2"
.def _printf; .scl 2; .type 32; .endef
Run Code Online (Sandbox Code Playgroud)
然后我用这个命令编译它:
gcc -std=c99 -pedantic test.c -O3 -S -o test.asm
Run Code Online (Sandbox Code Playgroud)
这是输出:
.file "test.c"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "%d\12\0"
.section .text.startup,"x"
.p2align 4,,15
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB7:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %ebx
andl $-16, %esp
subl $16, %esp
.cfi_offset 3, -12
call ___main
movl 8(%ebp), %eax
leal (%eax,%eax), %edx
movl $1, %eax
cmpl $4, %edx
jne L8
jmp L6
.p2align 4,,7
L12:
movl %ebx, %eax
L8:
leal 1(%eax), %ebx
movl %eax, 4(%esp)
movl $LC0, (%esp)
call _printf
cmpl $11, %ebx
jne L12
L6:
xorl %eax, %eax
movl -4(%ebp), %ebx
leave
.cfi_restore 5
.cfi_restore 3
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE7:
.ident "GCC: (GNU) 4.8.2"
.def _printf; .scl 2; .type 32; .endef
Run Code Online (Sandbox Code Playgroud)
所以基本上,随着优化的开启,原始代码被转换成这样:
#include <stdio.h>
int main(int argc, char **argv) {
int dummy = 2 * argc;
int i = 1;
if (dummy != 4)
while (i<=10)
printf("%d\n", i++);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
因此,如您所见,C 中确实存在提升。
| 归档时间: |
|
| 查看次数: |
2529 次 |
| 最近记录: |