如何使用C/C++;优化会影响可变变量吗?

如何使用C/C++;优化会影响可变变量吗?,c,gcc,optimization,C,Gcc,Optimization,我对一些代码有一些问题(见下文)。当所有优化都关闭(-O0)时,代码的行为与我预期的一样,但是当我打开优化时,代码的性能与我预期的不一样。因此,我在这里发帖是想看看是否有人看到了在进行优化时代码会以不同方式(错误地)运行的原因。让我解释一下代码在做什么 TPM0->CNT是一个计时器寄存器,属于易失性uint32\t类型。当TPM0->CNT达到其最大值并滚动时,将生成一个中断。在该中断中,sg_CpuCount将增加自上次中断以来经过的计时器周期数 GetCpuCount函数的任务是返回瞬时计

我对一些代码有一些问题(见下文)。当所有优化都关闭(-O0)时,代码的行为与我预期的一样,但是当我打开优化时,代码的性能与我预期的不一样。因此,我在这里发帖是想看看是否有人看到了在进行优化时代码会以不同方式(错误地)运行的原因。让我解释一下代码在做什么

TPM0->CNT是一个计时器寄存器,属于易失性uint32\t类型。当TPM0->CNT达到其最大值并滚动时,将生成一个中断。在该中断中,sg_CpuCount将增加自上次中断以来经过的计时器周期数

GetCpuCount函数的任务是返回瞬时计数(即sg_CpuCount+TPM0->CNT),但是,因为中断可能随时发生,并且计时器不会停止,因此在读取/计算时必须小心。读取两个组件,然后再次测试第一个组件,以查看其是否发生变化。如果它改变了已知的中断,则在第一次和第二次读取之间触发一个中断,并重复该过程,但是如果它们相同,则已知没有中断发生,并且返回添加的组件

逻辑合理吗?优化是否会生成错误代码?有什么见解吗?提前谢谢

编辑:我应该提到在进行优化时我注意到的行为。基本上,对GetCpuCount的后续调用并不总是像关闭优化时那样单调递增。也就是说,以后对GetCpuCount的调用可能会返回比以前调用更小的数字——这应该是不可能的

edit2:我应该提到的是,这段代码运行在一个32位的微体系结构上(我使用的是64位的值)。我仍然认为应该可以,但这是一个值得思考的问题

static volatile int64_t sg_CpuCount;

void TPM0_IRQHandler(void)
{
   TPM0->SC |= TPM_SC_TOF(1); //clear interrupt flag
   sg_CpuCount += CPU_CLOCK / TICKRATE_HZ;
}

int64_t GetCpuCount(void)
{
   for (;;) {
      int64_t tmpCpuCount = sg_CpuCount;
      uint32_t tmpCnt = TPM0->CNT;
      if (tmpCpuCount == sg_CpuCount) {
         return tmpCpuCount + tmpCnt;
      }
   }
}
编辑3:添加asm

优化(-0s)

非优化(-00)


如果您在32位机器上,则无法保证64位操作是原子操作。由于优化,时间可能会改变。永远不要在中断被禁用的情况下调用函数

static volatile uint32_t sg_CpuCount;

void TPM0_IRQHandler(void)
{
   TPM0->SC |= TPM_SC_TOF(1); //clear interrupt flag
   sg_CpuCount++; //count interrupts
}

int64_t GetCpuCount(void)
{
   for (;;) {
      uint32_t tmpCpuCount = sg_CpuCount;
      uint32_t tmpCnt = TPM0->CNT;
      if (tmpCpuCount == sg_CpuCount) {
         return (int64_t)tmpCpuCount * CPU_CLOCK / TICKRATE_HZ + tmpCnt;
      }
   }
} 

在分析ASM的优化和非优化代码后,我注意到一个主要区别。一个先加载64位整数高位字,另一个先加载低位字。所以我相信user5329483是正确的,我将解释可能导致问题的确切顺序

read sg_CpuCount low
read sg_CpuCount high
read TPM0->CNT
read sg_CpuCount low
***interrupt***
read sg_CpuCount high
compare first sg_CpuCount with second sg_CpuCount (equal!!!!!)
由于中断中sg_CpuCount的增量可能只会改变较低的字,因此上述序列无法检测到发生的中断

非优化代码如下所示:

read sg_CpuCount high
read sg_CpuCount low
read TPM0->CNT
read sg_CpuCount high
***interrupt***
read sg_CpuCount low
compare first sg_CpuCount with second sg_CpuCount (**NOT** equal!!!!!)
此代码正确地检测到发生的中断

因此,优化实际上改变了代码——但是从编译器的角度来看,这两个代码族都是同样有效的。因此,答案是通过将64位int拆分为2个32位变量来明确定义操作顺序,或者更好地使用user5329483建议的代码


编辑:不幸的是,32位不足以运行足够长的时间。无论如何,现在问题已经很好理解了。< /P>请添加<代码>主< /代码>函数。完整性。你真的想要挥发而不是<代码> STD::原子< /代码>。这实际上是C代码而不是C++代码。另外,我不确定hot std::atomic是否与中断交互。您是否需要
sg\u CpuCount
成为
int64\u t
?我会尝试将它改为一个
易失性的sig\u atomic\t
。主函数对此并没有真正的帮助。我想我编辑了这篇文章,添加了您可能需要的其他信息。我意识到64位操作不会是原子操作,但这就是为什么GetCpuCount函数中用于验证sg_CpuCount的if语句在第一次读取时没有更改的原因。对此我不是很确定。在第一个示例中,您没有检测到中断,但该函数仍然返回一个有效值:
sg_CpuCount+TPM0->CNT
。TPM0是什么类型的?TPM0是许多CPU寄存器的结构,CNT是易失性uint32\t。是的,您是正确的,第一个示例可以返回一个有效值,即使它无法检测到中断-但并不总是。。。我相信还有一个因素在起作用——中断延迟。我发现在中断执行之前,可以将TPM0->CNT读取为0或1(即滚动)。我的问题只发生在这种情况下。因此,在第一个示例中,CNT被读取(作为0或1),sg_CpuCount low在中断之前被读取,但sg_CpuCount不正确,因为它尚未更新。您是否看到GetCpuCount()中设置的计时器溢出标志?它应该立即触发中断,并且应该在ISR中清除,这样您就看不到它了。如果是,为什么?全局禁用中断?我没有调查该标志是否在GetCpuCount函数中可见。但在标志断言之后,cpu可能会在中断过程开始之前再执行一条指令。通过在if语句之前放置一条asm(“nop”)指令,修复了所有问题。我正在寻找关于中断延迟计时的文档,以确定一个nop是否能保证正确的操作。
read sg_CpuCount low
read sg_CpuCount high
read TPM0->CNT
read sg_CpuCount low
***interrupt***
read sg_CpuCount high
compare first sg_CpuCount with second sg_CpuCount (equal!!!!!)
read sg_CpuCount high
read sg_CpuCount low
read TPM0->CNT
read sg_CpuCount high
***interrupt***
read sg_CpuCount low
compare first sg_CpuCount with second sg_CpuCount (**NOT** equal!!!!!)