ref是在复制编译器优化,我能避免它吗

Is ref-copying a compiler optimization, and can I avoid it?

本文关键字:能避免 优化 复制 编译器 ref      更新时间:2023-10-16

我不喜欢指针,通常会尽量使用refs编写代码。

我已经为一个小型Win32应用程序编写了一个非常初级的"垂直布局"系统。大多数布局方法如下所示:

void Control::DoLayout(int availableWidth, int &consumedYAmt)
{
  textYPosition = consumedYAmt;
  consumedYAmt += measureText(font, availableWidth);
}

它们像这样循环通过:

int innerYValue = 0;
foreach(control in controls) {
  control->DoLayout(availableWidth, innerYValue);
}
int heightOfControl = innerYValue;

它并没有在这里绘制内容,只是精确计算这个控件需要多少空间(通常还会添加填充等)。这对我来说非常有效……在调试模式下。

我发现,在Release模式下,我可以突然看到明显的、可记录的问题,当我在控件中循环并调用DoLayout()时,consumedYAmt变量在外部循环中实际上停留在0。最烦人的部分是,如果我插入断点并逐行遍历代码,这种情况就会停止,并且部分代码会由内部的"add"方法正确更新。

我在想这是否是一些编译器优化,他们认为我只是在int中添加ref标志,作为优化内存的一种方式;或者,如果有任何可能性的话,这实际上是以一种不同于看起来的方式工作的。

我会给出一个可重复性最低的例子,但我无法用一个小小的命令行应用程序做到这一点。我感觉到,如果这是一个优化,它只适用于较大的代码块和间接寻址。

编辑:再次抱歉信息普遍不足,但我现在得到提示,这可能是某种链接器问题。我在伪代码中跳过了继承模型的一部分:调用类实际上调用"Layout()",这是该类根定义上的一个非虚拟函数。此函数执行一些与实现无关的逻辑,然后使用相同的参数调用DoLayout()。然而,我现在注意到,如果我尝试向Layout()添加断点,Visual Studio声称"断点不会被命中。调试器的目标代码类型的可执行代码都与此行关联。"我可以向某些其他行添加断点,但我开始注意到奇怪的步进逻辑,它拒绝进入某些函数,如Layout。已尝试完全清除生成文件夹并重新生成。我将不得不继续寻找,因为我必须承认这并不是什么好事情

此外,随机添加:"控件"列表是一个包含shared_ptr对象的向量。我以前没有怀疑过循环机制,但现在我更仔细地观察了。

"consumedYAmt变量实际上保持在0"

您描述的行为是特定优化的典型行为,这更多地是由于CPU而不是编译器。我怀疑您正在从另一个线程记录consumedYAmt。对consumedYAmt的更新根本无法到达其他线程。

这对CPU来说是合法的,因为C++编译器没有设置内存围栏。CPU编译器没有设置围栏,因为变量不是原子的。

在一个没有线程的小程序中,它根本不会显示出来,也不会在调试模式中显示出来。

由OP编写


好吧,最终解决了这个问题。尽管这个问题很简单,但由于发布模式的调试器似乎以不一致的方式运行,因此很难确定它。当我改变策略,在很多地方添加Logging语句时,我发现我的Control类有一个"mShowing"变量,它的构造函数中没有初始化。在调试模式下,它显然保留了未初始化的内存,我想这使它成为"true"——但在发布模式下,我最好的分析是内存保护使它默认为"false",事实证明,它在大多数时候都跳过了DoLayout方法的主体。

由于在整个过程中,响应者缺乏可处理的信息(如果我发布一个更长的例子,肯定会更容易),我只是简单地对每个提到未初始化变量的评论投了赞成票。