编译器优化2—指针,SIMD指令,循环,结构体

文章探讨了指针别名如何影响编译器优化,介绍了__restrict关键字消除别名以允许优化。同时,文章深入讲解了SIMD向量指令在提升性能方面的作用,以及在处理结构体和数组时,AOS(ArrayofStructs)与SOA(StructofArrays)布局的影响。还提到了STL中的优化策略和浮点运算的优化方法。
摘要由CSDN通过智能技术生成

指针别名

终于来到号称优化杀手的指针别名了!

先来看一个小例子:

可以看到c被重复赋值了两次,那为什么不优化掉呢?
因为可能存在

void func(int *a, int *b , int *c) {
    *c = *a;
    *c = *b;
}
int main(){
    int a , b;    
    func(&a, &b, &b);//在这里首先是将a->b,b->b;也就是最终b里存的是a

}

 如果你想让编译器放心优化,不要担心指针别名,可以这样写:

 可以看到加入了__restrict关键字之后,gcc就会开启优化。就不会重复两遍了。

__restrict只需要在非const里就可以了,因为const的指针都是只读的,必然就不会被写入,所以就不用担心别名的问题。

对比的是volatile,这个是让编译器不要优化

可以看到编译器不会自动优化了

而volatile可以针对更多的类型,而__restrict只针对指针。

合并写入 —矢量化指令

两个int32可以被合并写入为一个int64

四个int32可以被合并为一个_m128

 可以看到直接来到了128位的xmm寄存器。

而SIMD的加速填充:

 可以重点看一下画圆圈的部分,可以看到一次减了16,这是因为每次都存入四个数,一个数是4个字节。伪代码可以视为:

而如果不确定是不是4的倍数:

void func(int *a , int n) {
    for(int i =0;i<n;i++){
        a[i] = i;
    }
}

 那么编译器会进行特化,比如要是1023,就会对前1020的数据进行SIMD写入,然后对剩下的三个以标准的方式写入。

循环

如上图所示,可见编译版本很复杂,似乎生成了一个一次16个字节的SIMD版本和一个一次4个字节的普通版本。这是由于编译器害怕指针别名的现象出现,也就是它不清楚会不会修改a里的值,b也会跟着改变,所以会做一个判断,看a+1024和b的大小比较的结果来决定使用哪一个版本。 

也可以使用openmp语句将其优化。

 可以看到这两个的汇编语言中,是没有任何区别的,这就是编译器自动将不会变的量移到了循环的外边,也就是我们常说的,要将热的代码移到冷的里面去。

尽可能顺序访问也是优化的关键

循环展开:

 

相当于这样,但不太建议手动展开,会影响SIMD矢量化。

 

结构体

 

 可以看到,只是加了一个无关的z,编译器就不会采用SIMD并行了,这是因为在只有x,y的时候,是一次输入16个字节,两个结构体。而如果输入了一个z之后,一个结构体就变成了12个字节,所以无法达到并行的要求,所以编译器就自动变成了最基本的低效模式。

 

 使用关键字 alignas 也可以达到同样的效果:

 结构体的内存布局:

AOS和SOA

一直没弄懂这两个结构体布局,我们今天来好好看一下:

AOS(Array of Struct)单个对象的属性紧挨着存:xyzxyzxyzxyz

SOA(Struct of Array)属性分离存储在多个数组:xxxxyyyyzzzz

AOS 必须对齐到 2 的幂才高效,SOA 就不需要。

AOS 符合直觉,不一定要存储在数组这种线性结构,而 SOA 可能无法保证多个数组大小一致

SOA 不符合直觉,但通常是更高效的!

这就是AOS,是一个不能优化的代码;

这是SOA,优化后的结果。

但可以看到,这种SOA是不利于面向对象编程的,更多的是面向数据编程。

所以推出了一种中间方案:

AOSOA:

 这样也可以起到很好的优化效果。

STL:

stl中的vector也存在诸如此类的问题,可以通过pragma unroll 或者pradma omp simd等解决优化问题。

MATH

 正常来说,编译器是可以将a/2优化为a*0.5的,这是因为乘法要比除法运算快得多。

而在下面这个例子中,可以看到并没有进行优化,这是因为编译器不敢保证b是不是为0

优化方案1:手动优化:

 1024*乘法时间+1*除法时间<1024*除法时间

优化方案2 :指令调优:

 -ffast-math 选项让 GCC 更大胆地尝试浮点运算的优化,如果可以保证不会出现NAN和无穷大,那么就可以使用这个指令。

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值