一个const引入的优化问题
今天定位了一个const引入的优化问题, 问题本身挺简单的, 但是自己太菜, 写代码时竟然没注意, 所以记录一下, 引起警戒.
问题背景与定位
测试团队反馈修改newlib系统调用以后跑coremark报错bad alloc, 于是就看了了下sbrk()的修改.
因为之前在修改newlib时需要通过系统调用初始化一些数据, 这些数据对newlib而言是只读的, 于是我写了这样一段代码.
typedef struct mem_s {
int i1;
int i2;
} mem_t;
static const mem_t meminfo;
extern int __syscall(void *p);
int __attribute__((constructor(0))) wrapper() {
return __syscall(&meminfo);
}
void get_info(int *p1, int *p2) {
*p1 = meminfo.i1;
*p2 = meminfo.i2;
}
meminfo保存了一些全局的数据信息, 它在constructor函数wrapper()中通过系统调用__syscall()被初始化. 外部程序通过调用get_info()获取这些数据.
由于meminfo在运行期间本身不会被修改, 为了模块化和扩展性就将meminfo定义为static const, 保证变量仅本文件可见并且不会误修改.
而我修改后的sbrk()正是通过get_info()获取信息并做对应处理的, 那么第一怀疑点是get_info()返回值有误. 尝试打印一下返回值, 发现都是0.
于是迷之自信的怀疑是系统调用返回出错, 把问题推给仿真器团队. 但是仿真器团队不肯接锅, 于是写个测试接口证明给它们看.
void dump_info() {
int fd = __open("1.log", O_RDWR | O_CREAT, 0644);
__write(fd, &meminfo, sizeof(meminfo));
}
这里直接把数据写入文件是因为printf()会调用sbrk()导致递归调用. 加了打印接口以后发现仿真器返回的系统调用值是正确的. 那问题出在哪里呢?
重新review一遍代码, 突然想到是编译优化导致的! 因为是const变量所以默认值是恒定的, compiler直接将其localize成初始值.
而编译器本身无法感知constructor函数中修改内存的行为, 所以get_info()中p1与p2直接被赋值为0.
为阻止优化给meminfo加上volatile修饰符即可, 添加volatile之后coremark正确执行.
小结
const/volatile的作用随便找个人都能说上来, 但是自己写代码时候还是考虑不周.
说到底是写代码时候没有考虑清楚每个变量的设置与使用导致的, 所以古话说谋定而后动, 一定自己想明白了再写代码.
最重要的是想清楚了再甩锅, 否则小心怼在铁板上.