【问题标题】:for loop being ignored (optimized?) outfor 循环被忽略(优化?)
【发布时间】:2015-05-13 07:16:14
【问题描述】:

我正在使用 for/while 循环在我的代码中实现延迟。延迟的持续时间在这里并不重要,尽管它足够大以至于可以注意到。这是代码sn-p。

uint32_t i;

// Do something useful

for (i = 0; i < 50000000U; ++i)
{}

// Do something useful

我观察到的问题是这个 for 循环不会被执行。它可能会被编译器忽略/优化。但是,如果我将循环计数器 i 限定为 volatile,则 for 循环似乎正在执行,并且我确实注意到了所需的执行延迟。

这种行为似乎有点违反我对带有/不带有 volatile 关键字的编译器优化的理解。

即使循环计数器得到优化并存储在处理器寄存器中,计数器不应该仍然工作,也许延迟更小吗? (由于消除了内存获取开销。)

我正在构建的平台是 Xtensa 处理器(由 Tensilica 提供),而 C 编译器是 Tensilica 提供的,Xtensa C/C++ 编译器以最高级别的优化运行。

我对@9​​87654326@ 和-o3 和ofast 优化级别进行了同样的尝试。在这种情况下,延迟似乎有效。

【问题讨论】:

  • 老实说,如果我是编译器,我会将 i 设置为 50000000U 并完成,尤其是在高优化级别上。使用 volatile,它可能会在外部进行更改,因此我无法优化它并在任何迭代中从寄存器/缓存/任何地方读取它。
  • 编译器不关心保留变量或循环;它关心保留输入和输出等内容。撕掉一个无所事事的循环是完全允许的。
  • 另外,因为还没有人提到它:不要这样做。这不是制造延迟的方法。 Use one of the functions designed for the job.
  • “我正在使用 for/while 循环在我的代码中实现延迟。”一开始是个非常糟糕的主意...

标签: c delay compiler-optimization timedelay xtensa


【解决方案1】:

这都是关于可观察行为的。循环的唯一可观察行为是循环之后 i50000000U。允许编译器对其进行优化并将其替换为i = 50000000U;。这个i 赋值也将被优化掉,因为i 的值没有可观察到的后果。

volatile 关键字告诉编译器写入和读取 i 具有可观察到的行为,从而阻止优化。

编译器也不会优化对无法访问代码的函数的调用。从理论上讲,如果编译器可以访问整个操作系统代码,它可以优化除 volatile 变量之外的所有内容,这些变量通常用于硬件 IO 操作。

这些优化规则都符合 C 标准中所写的内容(cf. cmets for references)。

此外,如果您想要延迟,请使用专用函数(例如:OS API),它们可靠且不消耗 CPU,不像您的自旋延迟。

【讨论】:

  • 我认为这在 N1570 §5.1.2.3 中有所涉及,尤其是在第 4 段中: 在抽象机器中,所有表达式都按照语义的规定进行评估。如果一个实际的实现可以推断出它的值没有被使用并且没有产生所需的副作用(包括调用函数或访问易失性对象引起的任何副作用),则它不需要评估表达式的一部分。
  • 感谢 ElderBug 的见解。这就是我真正想要的; C标准中对此提出了一些警告。至于使用 OS API 进行延迟,我正在使用单处理器、裸机固件,延迟的准确性/精确度在这里无关紧要。我只需要确保它超过某个阈值即可。
  • @user694733 感谢您指出。它确实减少了麻烦。 :)
  • @LoneWolf 对于裸机固件,您通常可以访问硬件计时器。一种很好的延迟技术是存储定时器值,然后循环直到值超过某个点(用定时器频率计算)。您只需要一个运行计时器(或为此运行一个计时器),它提供了可靠的最小延迟。
  • @LoneWolf 在裸机固件上编写延迟循环比在某些托管桌面应用程序上更糟糕,因为在裸机系统上您可以直接访问高精度硬件计时器。因此,请使用 MCU 的片上定时器,它们的存在是有原因的。
猜你喜欢
  • 1970-01-01
  • 2017-04-29
  • 1970-01-01
  • 2021-07-05
  • 2011-08-30
  • 1970-01-01
  • 2019-05-11
  • 2015-04-15
  • 1970-01-01
相关资源
最近更新 更多