首页 / 软件开发 / C++ / 使用IBM XL CC++和XL Fortran编译器调试经优化的代码
使用IBM XL CC++和XL Fortran编译器调试经优化的代码2013-11-04 IBM ethan.mao软件开发者们在开发产品级代码时常会面对一个艰难的选择,你总是希望你的代码性能优越,这意味着你 需要在高优化级别上编译它;同时,你可能希望调试你加入产品中的这份二进制代码,而不是编译时没有经过 优化的源文件。如果你尝试过调试优化过的代码,你可能已经知道这其中的难处了:源代码语句不按顺序执行,或者在你希望它们执行的时候它们没有;变量没有按预期地进行更新;变量没有定义的值,甚至没有一个定义的标识;在调试器内对变量的更新对程序执行不起作用 。这不是因为编译器出了什么差错,它设计的初衷就是为了保留你程序的结果和外部行为,而不是它 在调试器中的瞬态和内部行为。新一代编译器在2012年年中IBM发布了IBMPower系统上C/C++和 Fortran编译器的最新版本:XLC/C++编译器V12.1和XLFortran编译器V14.1,均对应AIX和PowerLinux平台。这 些新的编译器提供了一系列数值的选择以供调试优化过的代码,你可以自由选择在完全优化代码和完全可调试 代码之间的权衡级别。在大多数其它编译器中,开发者有两个可用的选择:编译一个没有优化的调试版本 (例如使用-g),或一个优化过的版本,但是可调试性较差(例如使用-O2)。在新的XLC++ V12.1和XL FortranV14.1编译器中,你可以使用一系列-g的值,从-g0一直到-g9,调试级别越低,在调试期间 观测的错误可能性越大,这意味着你可以自由权衡可调试性和性能。在程序优化时编译器会保障各级别的可调 试性,并在调试过程中在保证预期行为的情况下有效地将应用的性能最大化。执行顺序编译器 在优化过程中经常会在不改变程序结果的前提下重新编排逻辑,编译器这么做可能是为了直接提高程序的性能 ,或者是为了让接下来的优化能提高程序的性能。此外,为了提高处理器吞吐量或是其它的原因,当主优化阶 段完成、指令生成时,源代码行相关的指令序列可能也会被重新排序,这些编译器的优化有两个主要 的影响:第一,如果你按源代码的行数单步跟踪程序,那么程序可能不会按照正确的顺序执行。第二,如果你 在一个过程的开始设了个断点,没有人能保证这过程的参数在这时会含有正确的值,程序甚至根本不会进入这 个过程。这是因为编译器在优化过程中将被调用的过程代码内联至调用处。在XLC/C++和 XLFortran的早先版 本中,旧的-g行为会生成全部调试信息,但它对于一个优化过的程序是否有用就不得而知了,因此,在用-g和 -O编译的程序中,当你试图去调试并观测某个特定过程被调用时的参数是什么时,你甚至不能确定过程入口处 设立的断点是否可以让你看到这些参数的实际值。然而,-g3或更高的调试级别可以保证在过程的入口处参数 是有效且可见的。优化代码中的某些语句可能并没有被执行到,而另一些可能比在它们之前的语句更先被 执行。如果你在一个给定的源代码行中设立一个断点,你不能确定逻辑上在这之前的行中变量被赋的值是否正 确。例如,考虑一下程序段:10 x=x+1;11fl=fl1/fl2;12 y=y+1;如果我们在第11行设立一个断点并运行程序,调试器会在与第11行关联的第一条指令处 停下,但此时第10行可能还没有被执行过,因为第10行与第11行是无关的,编译器可以自由将它们调换顺序。 如果你想要单步执行这段代码,你可能会发现我们在停在第10行前就已经执行到了11行,这是一个很强的信号 (但不是一定的),说明与第10行相关的指令都还没有被执行到。 在这个例子中编译器会将除法尽早做好, 因为除法指令有很长的延迟,而此时一个先进的流水线式处理器可以在除法进行时继续工作,这样的话没有关 联上的行为,例如将x的值读入寄存器并将它加1,可能会同时进行。假设我们对在第11行处的除法结果有兴趣 。在没有优化过的程序中,我们只需要单步到第12行,再查看fl的值。但在优化过的程序中,单步到第12步不 能保证所有与第11行相关的操作都已经完成,我们可能已经完成了除法但还没有将结果存回内存,因此,确定 变量已经可以显示正确结果的唯一方法是单步执行机器指令直到你看到除法运算的结果被写回了内存空间。如 果你只关心结果的值(而不是验证是否更新了变量),取而代之你可以在除法运算更新了其目标寄存器的同时 打印它的值。当你用 XLC/C++ V12.1或 XL FortranV14.1的-g8或更高的选项编译程序时,调试器可以 得到每个可执行语句开始时程序的状态。在先前的例子上使用-g8可以确保当你到达第11行的断点时,第10行 的加法已经完成,而且你可以通过调试器来得到其结果。