【问题标题】:Is it possible to disable gcc/g++ optimisations for specific sections of code?是否可以禁用特定代码部分的 gcc/g++ 优化?
【发布时间】:2017-10-27 23:55:05
【问题描述】:

我正在编译一些没有优化但在启用优化时中断的代码。我怀疑代码的某些关键部分被优化了,导致逻辑中断。

我想做这样的事情:

code...

#disable opt

more code...

#enable opt

如果我可以为该部分设置优化级别(如 O0、O1...),那就更好了

对于那些建议它是代码的人:

被删除的代码部分是(通过反汇编目标文件检查):

void wait(uint32_t time)
{
  while (time > 0) {
    time--;
  }
}

我严重怀疑该代码有问题

【问题讨论】:

  • 这是为了调试目的,还是为了“修复”错误?我希望你不要试图用这个想法来修复你的程序。
  • 嗯,它是针对嵌入式系统的,所以硬件操作被认为毫无意义的可能性很高,甚至使用 volatile 也无济于事,所以我打算用它来修复它:p
  • 请注意,现代编译器会积极优化调用未定义行为的代码——它们是被允许的,因为行为未定义。如果您确实发现优化时出现问题,请仔细查看代码;检查未定义的行为。
  • 消失的代码是:while(var > 0) var-- 甚至不是无限循环
  • 使用像 while 循环这样的旋转是在嵌入式系统上自找麻烦。你不应该相信嵌入式硬件会给你周期准确的延迟。您应该使用用于小延迟的标准库函数。我不知道您需要什么时间,但usleep() 会延迟到微秒级。如果延迟很长(例如毫秒),您应该使用中断或 RTOS 睡眠功能。

标签: c gcc optimization


【解决方案1】:

您可以做的最好的事情是将您不想优化的代码移到单独的源文件中。编译它而不进行优化,并将其链接到您的其余代码。

使用 GCC,您还可以使用 __attribute__((optimize("O0")) 声明一个函数来禁止优化。

【讨论】:

    【解决方案2】:

    您可以浏览 GCC 文档。例如:

    • Function specific option pragmas

      #pragma GCC optimize ("string"...)

      此编译指示允许您为源文件中稍后定义的函数设置全局优化选项。可以指定一个或多个字符串。在此之后定义的每个函数都如同为该函数指定了attribute((optimize("STRING")))。选项周围的括号是可选的。有关优化属性和属性语法的详细信息,请参阅函数属性。

      另请参阅 push_options、pop_options 和 reset_options 的编译指示。

    • Common function attributes

      optimize optimize 属性用于指定使用与命令行中指定的不同的优化选项来编译函数。参数可以是数字或字符串。数字被假定为优化级别。以O 开头的字符串被假定为优化选项,而其他选项被假定与-f 前缀一起使用。您还可以使用 '#pragma GCC optimize' pragma 设置影响多个函数的优化选项。有关 '#pragma GCC optimize' pragma 的详细信息,请参阅 Function Specific Option Pragma。

      此属性应仅用于调试目的。它不适用于生产代码。

    • Optimize options

      此页面列出了许多优化选项,可与命令行上的-f 选项一起使用,因此与optimize 属性和/或编译指示一起使用。

    【讨论】:

      【解决方案3】:

      如果优化导致你的程序崩溃,那么你有一个错误,应该修复它。通过不优化这部分代码来隐藏问题是不好的做法,这会使您的代码变得脆弱,就像为下一个支持您的代码的开发人员留下一个地雷一样。更糟糕的是,忽略它,您将无法学习如何调试这些问题。

      一些可能的根本原因:

      硬件访问被优化:使用Volatile 关键代码不太可能被优化掉,尽管如果您正在接触硬件寄存器,那么您应该添加 volatile 属性以强制编译器访问这些寄存器,而不管优化设置如何。

      竞争条件:使用Mutex or Semaphore 控制对共享数据的访问 您更有可能遇到特定于时间的竞争条件,并且优化会导致此时间条件显示出来。这是一件好事,因为这意味着你可以修复它。您是否有多个线程或进程访问相同的硬件或共享数据?您可能需要添加互斥体或信号量来控制访问以避免时间问题。

      Heisenbug: 这是当代码的行为根据是否添加调试语句或代码是否优化而改变时。有一个很好的例子here,其中优化的代码在寄存器中以高精度进行浮点比较,但是当添加 printf 时,这些值被存储为双精度值并以较低的精度进行比较。这导致代码以一种方式失败,但不是另一种。也许这会给你一些想法。

      时序循环得到优化:创建一个等待函数,该函数通过创建一个递增局部变量以增加延迟的时序循环来工作,这不是一种好的编程风格。可以根据编译器和优化设置完全优化此类循环。此外,如果您移动到不同的处理器,延迟量也会发生变化。延迟函数应该基于 CPU 滴答声或实时工作,这不会得到优化。让延迟函数使用 CPU 时钟或实时时钟,或调用标准函数,例如 nanosleep() 或 use a select with a timeout。请注意,如果您使用的是 CPU 滴答,请务必对函数进行注释,并强调实现需要针对特定​​目标。

      底线:正如其他人所建议的,将可疑代码放在单独的文件中,并编译该单一源文件而不进行优化。测试它以确保它工作,然后将一半代码迁移回原始代码,并重新测试一半优化和一半未优化的代码,以确定你的错误在哪里。一旦你知道哪一半有 Heisenbug,使用分而治之来重复这个过程,直到你找出优化时失败的代码的最小部分。

      如果您能在那时找到错误,那就太好了。否则在此处发布该片段,以便我们帮助调试它。提供用于在优化时导致其失败的编译器优化标志。

      【讨论】:

      • 没有关键代码正在被优化。我有一个小的等待函数(一个只增加一个变量而没有别的循环),它正在二进制文件中消失
      • 换句话说我确切地知道错误是什么,编译器正在省略整个函数
      • @Makogan:由于代码不使用递增变量,因此编译器完全可以省略代码。您必须更好地掩饰您正在做的事情,以避免编译器优化一个根本没有任何可检测到的副作用的函数。例如,您可能需要将递增的变量分配给全局变量——或者以其他方式阻止编译器消除代码。
      • @Makogan:创建一个等待函数,通过增加局部变量来增加延迟,这不是好的编程风格。正如乔纳森所指出的,它可以完全优化出来,如果你移动到不同的处理器,延迟也可能会改变。您的延迟功能应该基于时间工作,这不会得到优化。让它使用 CPU 时钟或实时时钟,或调用标准函数,如 nanosleep() 或使用带有超时的选择。见stackoverflow.com/a/30969653/6693299
      • @Makogan 您误解了语言的定义以及与编译器的合同,并且您忽略/忽略了您被告知的内容以及问题的答案。您的代码要求该函数什么都不做。时期。您有一些错误的观念,即您的程序要求在某种不合理的特殊执行模型下执行某些步骤。定义/合同中使用了一个执行模型,它是“C++ 抽象机”,该抽象并不是说执行该循环需要任何时间。另请参见“假设规则”。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-02-17
      • 2018-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-15
      相关资源
      最近更新 更多