Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/157.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 创建一个始终返回零的函数,但优化器不会';我不知道_C++_Performance_Optimization_Compiler Optimization - Fatal编程技术网

C++ 创建一个始终返回零的函数,但优化器不会';我不知道

C++ 创建一个始终返回零的函数,但优化器不会';我不知道,c++,performance,optimization,compiler-optimization,C++,Performance,Optimization,Compiler Optimization,我想创建一个总是返回零的函数,但这一事实对于优化器来说并不明显,因此使用该值的后续计算不会因为“已知零”状态而折叠 在没有链接时间优化的情况下,这通常与将其放入自己的编译单元一样简单: int zero() { return 0; } 优化器无法跨单元查看,因此不会发现此函数的始终为零的特性 然而,我需要一些能与LTO一起工作的东西,以及未来尽可能多的智能优化。我考虑从一个全球性的角度阅读: int x; int zero() { return x; } 。。。但在我看来,一个足够

我想创建一个总是返回零的函数,但这一事实对于优化器来说并不明显,因此使用该值的后续计算不会因为“已知零”状态而折叠

在没有链接时间优化的情况下,这通常与将其放入自己的编译单元一样简单:

int zero() {
  return 0;
}
优化器无法跨单元查看,因此不会发现此函数的始终为零的特性

然而,我需要一些能与LTO一起工作的东西,以及未来尽可能多的智能优化。我考虑从一个全球性的角度阅读:

int x;

int zero() {
  return x;
}
。。。但在我看来,一个足够聪明的编译器可能会注意到,
x
从未写入,并且仍然决定
zero()
始终为零

我考虑过使用
volatile
,比如:

int zero() {
  volatile int x = 0;
  return x;
}
。。。但是volatile读取所需的副作用的实际语义并不完全清楚,并且似乎不排除函数仍然返回零的可能性

这种始终为零但不在编译时的值在几种情况下非常有用,例如强制两个值之间存在无操作依赖关系。类似于:
a+=b&zero()
会导致
a
依赖于最后一个二进制中的
b
,但不会更改
a
的值


不要告诉我“标准不保证任何方法可以做到这一点”——我很清楚,我正在寻找一个实用的答案,而不是标准中的语言。

如果编译器能够解决这个问题,我会感到惊讶:

int not_a_zero_honest_guv()
{
    // static makes sure the initialization code only gets called once
    static int const i = std::ifstream("") ? 1:0;
    return i;
}

int main()
{
    std::cout << not_a_zero_honest_guv();
}
int not_a_zero_money_guv()
{
//static确保只调用一次初始化代码
静态int const i=std::ifstream(“”)1:0;
返回i;
}
int main()
{

std::cout您会发现每个编译器都有一个扩展来实现这一点

通用条款:

MSVC:


在clang和gcc上,重击变量是可行的,但会带来一些开销

int zero()
{
    int i = 0;
    asm volatile(""::"g"(&i):"memory");
    return i;
}
在gcc上的O3下编译为

    mov     DWORD PTR [rsp-4], 0
    lea     rax, [rsp-4]
    mov     eax, DWORD PTR [rsp-4]
    ret
叮当作响

    mov     dword ptr [rsp - 12], 0
    lea     rax, [rsp - 12]
    mov     qword ptr [rsp - 8], rax
    mov     eax, dword ptr [rsp - 12]
    ret

.

首先,我认为OP的第三个建议:

int zero() {
  volatile int x = 0;
  return x;
}
事实上会起作用(但这不是我的答案;请参见下文)。两周前,这个完全相同的功能曾是讨论的主题,有很多讨论和不同的观点,我将不在这里重复。但对于最近的测试,请参见


我的答案是在上面添加一个
static
,即:

int zero() {
  static volatile int x;
  return x;
}
请参阅此处的一些测试:

现在,随着
静态的加入,“可观察行为”的抽象概念变得更加可信。只需一点工作,我就可以找出
x
的地址,特别是如果我禁用了。这可能在
.bss
段中。然后,再多做一点工作,我就可以将调试器/黑客工具附加到正在运行的进程中,然后更改
x
的值,并使用
volatile
>,我已经告诉编译器我可能会这样做,所以不允许通过优化
x
来改变这种“可观察的行为”(它可能通过内联优化
zero
的调用,但我不在乎)

的标题有点误导,因为讨论集中在堆栈上的
x
而不是局部变量。因此不适用于此处。但我们可以将
x
从局部范围更改为文件范围,甚至全局范围,如:

volatile int x;
int zero() {
  return x;
}
这不会改变我的论点


进一步讨论:

是的,
volatile
有时会出现问题:例如,请参阅此处和中显示的指向volatile问题的指针

是的,有时(总是?)编译器会有bug

但我想说的是,这个解决方案并不是一个边缘案例,编译器编写人员之间存在共识,我将通过查看现有的分析来做到这一点

John Regehr在2010年的blogpost中报告了一个bug,其中在gcc和Clang中对易失性访问进行了优化(在三个小时内修复)。一位评论员引用了该标准(重点补充):

“6.7.3…构成对具有易失性限定类型的对象的访问的实现定义的。

Regehr对此表示同意,但补充道,对于如何处理非边缘案件,各方已达成共识:

是的,对易失性变量的访问是由定义实现的。但是,您错过了一个事实,即所有合理的C实现都将从易失性变量读取为读访问,并将写入易失性变量写入写访问。 有关更多参考资料,请参阅:

  • 另一个Regehr 2010博客帖子

  • 温特穆特的


这些都是关于编译器错误和程序员错误的报告。但它们表明了
volatile
应该/确实起作用,并且这个答案符合这些规范。

这并不能保证,而且实际上在
clang
今天已经不起作用了(尽管clang通常尊重
noinline
),而gcc已经进行了可能会破坏这一点的过程间分析,尽管它还没有这样做。@BeeOnRope您是对的。这可能应该被报告为错误。此属性是否也具有函数结果不能用于优化的语义?在gcc手册中,它只描述了它不能执行的行为例如,
如果(!zero())foo();
,编译器可以(据我所知)将其翻译为
zero();foo();
attribute@M.M有趣。gcc和msvc都不这样做。
rand()!=0
也可能是相同的……几乎总是零,但编译器肯定不能认为这是一个好的,尽管依赖文件系统行为有点丢脸。只是好奇,为什么要包装
ifstream
调用
int zero() {
  static volatile int x;
  return x;
}
volatile int x;
int zero() {
  return x;
}