指向同一地址的两个相同类型的指针
aliasing
本身是一个信号处理方面的概念。是指在信号采样过程中,不同的信号不再能相互区分的现象。
如下图所示的波纹现象,相对于拍摄的采样频率(横纵像素分辨率),墙砖缝隙变化的频率要大于采样频率。或者换句话说,多条墙砖缝隙需要挤在一个像素里面。
同样的现象也会出现在程序员穿着“高密度”的格子衬衫接受电视采访时。
墙砖缝隙出现aliasing
后无法再行区分,从字面意义来说,Pointer Aliasing
就是不同的指针也无法区分。
指针无法区分,只有一种情况,就是指针的类型和指向的地址都是相同的,这就是Pointer Aliasing
。
为什么会有性能影响
1 | void foo(int *array, int *size, int *value) { |
如果让我们自己“优化”一下这段代码,我们可能会首先将value
指向的值存入一个临时变量里,然后将临时变量在循环中直接赋值给array
。
我们假设个array
的初始状态:[0, 1, 2, 3, 4]
如果value
指向的值等于3,那么按我们优化的方式,array
最终的状态是:[6, 6, 6, 6, 6]
但这里存在一个问题,如果value
指向array[3]
,那么array
最终的状态就是:[6, 6, 6, 12, 24]
value
和array[3]
就是指向相同地址类型相同的指针。
编译器为了得到最终正确的结果,就不得不取消我们之前提到的”优化”方式。
预防方法
使用__restrict
关键字:
1 | void foo(int * __restrict array, int *__restrict size, int *__restrict value) { |
当然,前提是自己可以确定代码逻辑中不会引入aliasing
。
怎么使用
首先明确一点,不是加上了__restrict
性能就会提升。
Pointer aliasing
对性能根本的伤害不是需要每次重新去某个地址取值,而是因为引入了潜在的数据依赖关系,从而关闭了很多编译器优化代码的能力。
上面两段代码,在-O0
优化时生成的汇编代码(gcc 4.8.5
)完全相同。不同的地方在于,第一段代码在-O2
和-O3
时生成的汇编代码仍然相同;而第二段做了__restrict
处理的代码则会在-O3
时加入大量循环展开等优化方式。
在线查看汇编代码:链接
所以__restrict
需要在打开较高等级的编译器优化的情况下使用才会有效果。