C99的restrict关键字
很多文章都大致说出restrict的意思:它向编译器表达一种用于优化的意愿,即由restrict所修饰的指针所指的对象只会通过这个指针访问。然而好像极少会深入剖析这个概念的含义。
写两个相似的函数,唯一的区别在于restrict的修饰。
void
f1(int *restrict p1, int *restrict p2)
{
for (int i = 0; i < 100; i++)
*p1 += *p2;
}
void
f2(int *p1, int *p2)
{
for (int i = 0; i < 100; i++)
*p1 += *p2;
}
使用gcc的-O3(-O2也是一样的)选项来优化编译,可以得到:
f1(int *restrict p1, int *restrict p2):
movl 8(%esp), %edx
movl 4(%esp), %eax
imull $100, (%edx), %edx
addl %edx, (%eax)
ret
f2(int *p1, int *p2):
pushl %ebx
movl 8(%esp), %ecx
movl $100, %eax
.L3:
movl 12(%esp), %ebx
movl (%ecx), %edx
addl (%ebx), %edx
subl $1, %eax
movl %edx, (%ecx)
jne .L3
popl %ebx
ret
我高亮了两个函数进行*p1 += *p2的部分。f1里把*p2的值乘以100,再把它加到*p1上。而在f2里,*p2被按部就班地通过每次迭代里加到*p1上。可以看到,通过对restrict的优化,f1省略了函数的压栈和循环的代码。很明显,f1比f2高效地多。f1之所以会得到这样的优化,是因为它认为p1和p2所指的值是不同的,所以p1值的改变不会影响到p2。然而f2不敢作这样的假设。
但是,如果我们破坏restrict的规则,程序可能会出错。上面的程序不好测试,因为最后的和会很大,导致整型溢出,所以我举另一个例子:
void
f1(int *restrict ap1, int *restrict ap2)
{
for (int i = 0; i < 100; i++)
*ap1 = *ap2 + 1;
}
void
f2(int *bp1, int *bp2)
{
for (int i = 0; i < 100; i++)
*bp1 = *bp2 + 1;
}
产生的汇编代码为:
f1(int *restrict ap1, int *restrict ap2)
movl 8(%esp), %eax
movl (%eax), %edx
movl 4(%esp), %eax
addl $1, %edx
movl %edx, (%eax)
ret
f2(int *bp1, int *bp2)
pushl %ebx
movl 8(%esp), %ebx
movl $100, %eax
movl 12(%esp), %ecx
.LVL3:
movl (%ecx), %edx
addl $1, %edx
subl $1, %eax
movl %edx, (%ebx)
jne .L3
popl %ebx
ret
测试代码:
int
main()
{
int a = 1;
int b = 1;
f1(&a, &a);
f2(&b, &b);
printf("a: %d, b: %d\n", a, b);
return 0;
}
输出为:a: 2, b: 101
至此,我想restrict的用法和含义已经非常清晰了。
摘自 技博控*淘米挣
补充:软件开发 , 其他 ,