在开发和调试阶段禁用优化真的是一个好习惯吗?

电器工程 微控制器 图片 图书 最佳实践 优化
2022-01-16 16:44:20

我读过Programming 16-Bit PIC Microcontrollers in C,书中有这样的肯定:

但是,在项目的开发和调试阶段,最好禁用所有优化,因为它们可能会修改正在分析的代码结构并导致单步执行和断点放置问题。

我承认我有点困惑。我不明白作者这么说是因为C30的评估期,还是真的是一个很好的做法。

我想知道你是否真的使用这种做法,为什么?

4个回答

这在整个软件工程中是相当标准的——当你优化代码时,编译器可以按照它想要的方式重新排列东西,只要你不能分辨出操作上的任何区别。因此,例如,如果您在循环的每次迭代中初始化一个变量,并且从不更改循环内的变量,则允许优化器将该初始化移出循环,这样您就不会浪费时间了。

它还可能意识到您计算了一个数字,然后在覆盖之前您不做任何事情。在这种情况下,它可能会消除无用的计算。

优化的问题在于,您需要在优化器已移动或消除的某段代码上放置断点。在这种情况下,调试器无法执行您想要的操作(通常,它会将断点放置在附近)。因此,为了使生成的代码更接近您编写的代码,您在调试期间关闭优化 - 这确保您想要中断的代码确实存在。

但是,您需要小心这一点,因为根据您的代码,优化可能会破坏事情!一般来说,被正常运行的优化器破坏的代码实际上只是有问题的代码,可以逃脱某些事情,所以你通常想弄清楚为什么优化器会破坏它。

我已将这个问题发送给 Jack Ganssle,他是这样回答我的:

丹尼尔,

我更喜欢使用已发布代码中的任何优化进行调试。NASA 说“测试你所飞的,飞你所测试的”。换句话说,不要进行测试然后更改代码!

但是,有时必须关闭优化才能使调试器正常工作。我尝试在我正在处理的模块中关闭它们。出于这个原因,我相信保持文件很小,比如几百行代码左右。

一切顺利,杰克

取决于,这通常适用于所有工具,而不仅仅是 C30。

优化通常以各种方式删除和/或重组代码。您的 switch 语句可能会使用 if/else 构造重新实现,或者在某些情况下可能会被一起删除。y = x * 16 可能会被一系列左移等替换。尽管最后一种优化通常仍然可以逐步完成,但主要是控制语句的重组得到了你。

这可能使调试器无法通过 C 代码单步调试,因为您在 C 中定义的结构不再存在,它们被编译器替换或重新排序为编译器认为更快或使用更少空间的东西。它还可能使断点无法从 C 列表中设置,因为您中断的指令可能不再存在。例如,您可能会尝试在 if 语句中设置断点,但编译器可能已经删除了该 if。您可以尝试在 while 或 for 循环内设置断点,但编译器决定展开该循环,使其不再存在。

因此,如果您可以在关闭优化的情况下进行调试,通常会更容易。您应该始终使用优化重新测试。这是您发现自己错过了一个重要volatile事件并导致间歇性故障(或其他一些奇怪现象)的唯一方法。

在嵌入式开发的情况下,无论如何您都必须小心优化。特别是在时序关键的代码部分中,例如一些中断。在这些情况下,您应该在汇编中对关键位进行编码或使用编译器指令来确保这些部分未优化,以便您知道它们具有固定的执行时间或固定的最坏情况运行时间。

另一个问题是将代码安装到 uC 中,您可能需要代码密度优化来简单地将代码安装到芯片中。这就是为什么它通常是一个好主意,从一个家庭中最大的 ROM 容量 uC 开始,并且在您的代码被锁定后只选择一个较小的用于制造。

通常,我会使用我计划发布的任何设置进行调试。如果我要发布优化代码,我会使用优化代码进行调试。如果我要发布未优化的代码,我会使用未优化的代码进行调试。我这样做有两个原因。首先,优化器可以产生足够大的时序差异,从而导致最终产品的行为与未优化的代码不同。其次,即使大多数都非常好,编译器供应商确实会犯错误,优化的代码可能会产生与未优化的代码不同的结果。因此,无论我打算发布什么设置,我都希望获得尽可能多的测试时间。

话虽如此,优化器会使调试变得困难,如前面的答案中所述。如果我发现难以调试的特定代码部分,我将暂时关闭优化器,进行调试以使代码正常工作,然后重新打开优化器并再次测试。