在GoingNative活动中,在第2天的交互式面板中,在9分钟时,Chandler Carruth说:
指针会产生锯齿问题.他们放慢你的二进制文件速度而不加速它们.
这是什么意思?这可以用(简单)示例来说明吗?
bam*_*s53 25
别名通过阻止编译器进行某些优化来影响性能.例如:
void foo(int *array,int *size,int *value) {
for(int i=0;i<*size;++i) {
array[i] = 2 * *value;
}
}
Run Code Online (Sandbox Code Playgroud)
查看此代码,您可能希望编译器可以*value在循环外部加载一次,然后非常快速地将数组中的每个元素设置为该值.但由于混叠,情况并非如此.因为*value它可能是数组元素的别名,所以它可以在任何给定的迭代中改变.因此,代码必须每次迭代加载值,从而导致潜在的大幅减速.
如果变量不能别名,那么上面的代码将等同于以下内容:
void foo(int *array,int size,int value) {
for(int i=0;i<size;++i) {
array[i] = 2 * value;
}
}
Run Code Online (Sandbox Code Playgroud)
使用LLVM的在线演示来获取生成的代码,以下是不同的结果:
1)带别名
foo: # @foo
.cfi_startproc
# BB#0:
cmpl $0, (%rsi)
jle .LBB0_3
# BB#1:
xorl %eax, %eax
.align 16, 0x90
.LBB0_2: # %.lr.ph
# =>This Inner Loop Header: Depth=1
movl (%rdx), %ecx
addl %ecx, %ecx
movl %ecx, (%rdi,%rax,4)
incq %rax
cmpl (%rsi), %eax
jl .LBB0_2
.LBB0_3: # %._crit_edge
ret
.size foo, .Ltmp1-foo
.cfi_endproc
.Leh_func_end0:
Run Code Online (Sandbox Code Playgroud)
2)没有别名
foo: # @foo
.cfi_startproc
# BB#0:
testl %esi, %esi
jle .LBB0_3
# BB#1: # %.lr.ph
addl %edx, %edx
.align 16, 0x90
.LBB0_2: # =>This Inner Loop Header: Depth=1
movl %edx, (%rdi)
addq $4, %rdi
decl %esi
jne .LBB0_2
.LBB0_3: # %._crit_edge
ret
.size foo, .Ltmp1-foo
.cfi_endproc
.Leh_func_end0:
Run Code Online (Sandbox Code Playgroud)
您可以看到带有别名的版本必须在循环体(标签LBB0_2和之间LBB0_3)中执行更多工作.
je4*_*e4d 12
钱德勒谈论的问题类型可以通过简化来轻松说明strcpy:
char *stpcpy (char * dest, const char * src);
Run Code Online (Sandbox Code Playgroud)
在编写此实现时,您可能会认为指向的内存与指向的内存dest完全分开src.编译器可能希望通过从指向的字符串中读取一个字符块来优化它src,并立即将所有字符写入dest.但是如果dest指向前面的一个字节src,则其行为将与简单的逐字符副本不同.
这里的别名问题是src可以别名dest,并且生成的代码必须低于src不允许别名时的效率dest.
真实strcpy使用额外的关键字Restrict(技术上只是C的一部分,而不是C++,它告诉编译器假设src并且dest不重叠,这允许编译器生成更高效的代码.
这是一个更简单的例子,我们可以看到装配中的一个很大的不同:
void my_function_1(int* a, int* b, int* c) {
if (*a) *b = *a;
if (*a) *c = *a;
}
void my_function_2(int* __restrict a, int* __restrict b, int* __restrict c) {
if (*a) *b = *a;
if (*a) *c = *a;
}
Run Code Online (Sandbox Code Playgroud)
假设这是一个函数的简化,其中实际使用两个if语句而不仅仅是有意义if (*a) { *b=*a; *c=*a; },但意图是相同的.
我们可能会在写这篇文章时假设,a != b因为有一些理由为什么这样my_function使用是没有意义的.但是编译器不能假设,并且在执行第二行之前存储b和重新加载a内存,以覆盖以下情况b == a:
0000000000400550 <my_function_1>:
400550: 8b 07 mov (%rdi),%eax
400552: 85 c0 test %eax,%eax <= if (*a)
400554: 74 0a je 400560 <my_function_1+0x10>
400556: 89 06 mov %eax,(%rsi)
400558: 8b 07 mov (%rdi),%eax
40055a: 85 c0 test %eax,%eax <= if (*a)
40055c: 74 02 je 400560 <my_function_1+0x10>
40055e: 89 02 mov %eax,(%rdx)
400560: f3 c3 repz retq
Run Code Online (Sandbox Code Playgroud)
如果我们通过添加来消除混叠的可能性__restrict,编译器会生成更短更快的代码:
0000000000400570 <my_function_2>:
400570: 8b 07 mov (%rdi),%eax
400572: 85 c0 test %eax,%eax
400574: 74 04 je 40057a <_Z9my_function_2PiS_S_+0xa>
400576: 89 06 mov %eax,(%rsi)
400578: 89 02 mov %eax,(%rdx)
40057a: f3 c3 repz retq
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
10182 次 |
| 最近记录: |