0%

strict alias理解

个人理解

别名:多个变量指向同一块内存区域时,这些变量互为别名

目前CPU都是多核,现代的指令体系都是并行、乱序执行。编译器为了充分利用硬件资源提升程序性能,编译时要分析指令、数据间的依赖关系,没有依赖关系的指令、数据访问可以并发乱序进行。
在分析依赖关系时别名就是一个绕不开的问题,究竟谁是谁的别名,如果编译器不清楚,那为了保证编译出的程序的正确性,只能做最坏打算,不确定的地方假定都是别名。
后来编译器定义了一些规则,符合什么条件的变量才可能是别名,不符合的绝对不会是别名。通过编译选项 strict-aliasing可以通知编译器,我的代码严格遵守这些规则,放心大胆的优化。
通过编译选项no-strict-aliasing可以通知编译器,我代码中有各种风骚的类型转换,你优化时悠着点,做好最坏的打算。

验证

代码如下

main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <stdint.h>

static int anint;

static void bar(int data)
{
printf("data is %u\n", data);
}
static void foo(double *dblptr)
{
anint=1;
*dblptr=3.14159;
/*
strict-alias下:编译器可以认为*dblptr和anint是两个独立的变量,互不影响,那bar(anint)等同于bar(1)。
*dblptr=3.14159和bar(anint)两条指令无关,可以乱序执行。比如先执行bar(anint),后执行*dblptr=3.14159。
亦或者依然是顺序执行,但bar(anint)直接优化为bar(1)。总之编译器可以放心的做优化
no-strict-alias下:编译器不能认为*dblptr和anint是两个独立的变量,所以要保证指令的执行顺序。
必须先执行*dblptr=3.14159,后执行bar(anint)
*/
bar(anint);
}
int main(int argc, char *argv[])
{
foo((double *) &anint);

return 0;
}

命令脚本如下

cmd1.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#!/bin/bash

#删除同名文件
rm -f test-*

#以两个不同的参数编译出两个目标程序
gcc -g -O2 -fstrict-aliasing main.c -o test-strict-alias
gcc -g -O2 -fno-strict-aliasing main.c -o test-no-strict-alias

#运行目标程序
echo "-----strict-alias---"
./test-strict-alias

echo "-----no-strict-alias---"
./test-no-strict-alias

执行结果

output1
1
2
3
4
5
-----strict-alias---
data is 1
-----no-strict-alias---
data is 4028335726

补充说明

实测时,O2优化级别以下的编译,-fstrict-aliasing 和 -fno-strict-aliasing运行结果一样。推测是编译器编译时没有打乱指令顺序,所以二者一致。

参考资料