是Intel的编译器出问题了吗?

在使用 Intel Fortran 编译器进行程序移植的过程中程序员有时候会碰到一些奇怪的现象:比如同一段程序多次运行,输出的数值结果是随机变化的不确定值;或者同一段程序在没有打开编译器的优化选项和在打开某些优化选项的时候却给出不同的结果,有时甚至会出现程序崩溃的现象。

 

由于这些程序在原有的系统上往往都是经过验证的,这种情况下大多数人的第一反应就是: Intel 的编译器出了问题!但是事实真的是这样吗?我看未必,事情往往没有我们想象的那么简单。大量已有的程序调试经验告诉我们:如果一个程序移植过程中出现了上述的情况,那么极有可能是程序的代码中存在 bug ,而不是编译器生成了错误的代码。能引起上述现象的常见程序错误有这么两类:

 

程序中存在未经过初始化就引用的变量

 

使用 Intel 编译器时,未经初始化的自动变量在运行时会具有随机的初始值。由于语言标准和具体编译器实现的原因,某些平台上的编译器具有自动把未初始化的变量设为 0 的选项。那么在这类平台上开发进行 Fortran 程序开发的人员很可能会养成变量不初始化就加以引用的习惯。当这些程序被移植到新平台上后,由于 Intel 的编译器不支持这一功能,那么程序编译虽然可以通过,但运行时就会输出随机变化的不确定值。

 

- 程序中存在数组越界访问

 

对某个数组的越界访问往往会破坏其它相邻数据结构的值,有时甚至会出现内存访问错误导致程序崩溃。这时有人会问,既然越界访问存在,那么在先前的系统上为什么一点问题都没有呢?这是因为不同平台上的内存分配机制和策略是不同的,同一个平台上使用不同的优化选项也会导致运行时采用不同的内存的分配。所以有时就会出现同一个程序打开优化和不打开优化时有完全不同的行为或输出结果

 

针对上面的程序问题, Intel 编译器提供了丰富的运行时检查选项来帮助我们找出相关的错误。下面列出几个最常用的供大家参考,这样在出现上述情况时可以自己先测试一下。

 

-CU (Linux), /CU (Windows)

打开针对未初始化变量进行引用的运行时检查。如果一个非 SAVE 属性且类型为整型,浮点型,复数型或逻辑型的局部变量在写入之前就进行了读操作,那么程序将发生一个运行错误。这里给出一个简单的例子。

 

$ cat test.f90

real ra

write (*,*) "ra = ", ra

end

 

$ ifort -CU test.f90

$./a.out

forrtl: severe (193): Run-Time Check Failure. The variable 'main$test_$RA' is being used without being defined

Image              PC                Routine            Line        Source

a.out              0000000000470F0D  Unknown               Unknown  Unknown

a.out               000000000046FA15  Unknown               Unknown  Unknown

a.out              00000000004202D9  Unknown               Unknown  Unknown

a.out              0000000000403CEF  Unknown               Unknown  Unknown

a.out              0000000000404708  Unknown               Unknown  Unknown

a.out              0000000000402B76  Unknown               Unknown  Unknown

a.out              0000000000402ADC  Unknown               Unknown  Unknown

libc.so.6          0000003D48A1D974  Unknown               Unknown  Unknown

a.out              00000000004029D9  Unknown               Unknown  Unknown

 

可以看到对未初始化变量“ ra ”的引用在打开“ -CU ”选项的情况下引发了一个运行时错误,而且错误信息指出了是变量“ ra ”未经初始化就被引用。

 

-CB (Linux) /CB (Windows)

打开对数组越界访问的编译时和运行时检查。当程序存在超过数组下标界限的访问时给出一个错误。例如:

$ cat test.f90

real ra(10)

integer i

ra = 0.0

i = 11

write (*,*) "ra = ", ra(i)

end

 

$ ifort -CB test.f90

$ ./a.out

forrtl: severe (408): fort: (2): Subscript #1 of the array RA has value 11 which is greater than the upper bound of 10

 

Image              PC                Routine            Line        Source

a.out              0000000000470F5D  Unknown                Unknown  Unknown

a.out              000000000046FA65  Unknown               Unknown  Unknown

a.out              0000000000420329  Unknown               Unknown  Unknown

a.out              0000000000403D3F  Unknown               Unknown  Unknown

a.out               0000000000404142  Unknown               Unknown  Unknown

a.out              0000000000402BB8  Unknown               Unknown  Unknown

a.out              0000000000402ADC  Unknown               Unknown  Unknown

libc.so.6          0000003D48A1D974   Unknown               Unknown  Unknown

a.out              00000000004029D9  Unknown               Unknown  Unknown

 

可以看到,打开 -CB 选项后对数组“ ra ”进行的越界访问出发了运行时错误,这样程序员很快就可以发现并改正错误代码。

 

-zero (Linux) /Qzero (Windows)

把所有未初始化,具有 save 属性 且类型为整型,浮点型,复数型或逻辑型的局部变量自动初始化为 0

-save (Linux) /Qsave (Windows)

把所有非递归过程中且没有 AUTOMATIC 属性的局部变量分配到静态存储空间,也就是相当于把他们加上了 SAVE 属性。

 

把上面两个选项同时使用可以起到把未初始化的变量自动设置为零的功能,可以用来判断出现问题的程序中是否存在引用未初始化变量的问题。如果加上这两个选项后有问题的程序能够始终给出稳定的正确输出结果,那么代码中就很有可能存在变量未初始化的问题。这里要注意的是由于 save 选项改变了变量内存分配策略,很有可能带来其它的副作用,所以即使程序在加上这两个选项后可以解决问题,得到输出正确结果,也不要用它们作为最终解决方案,而是要找出代码中的错误并加以改正。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值