【问题标题】:The relevance of scopes in conjunction with compiler optimization作用域与编译器优化的相关性
【发布时间】:2019-04-05 15:46:25
【问题描述】:

我有一个library,它在编译时执行大量循环展开。我还使用了一个基准测试工具,它通过在范围内实例化一个结构来工作,并在销毁时测量创建和销毁之间的时间并返回收集的值。现在,通过在其中插入一些函数调用,它可以测量函数调用的时间。我想知道的是,编译器可以跳转范围吗?(请参阅下面的代码)

PerfEvent ev;
{
    PerfEventBlock bl(ev, countPE);
    ev.setParam("name","FTensor3D");
    res(l,m,n,o) = t1(l,m,k)*t2(k,n,o);
}

或者换句话说:编译器可以“外包”到范围之外(我得到了奇怪的结果,我想知道)

【问题讨论】:

  • 您能否澄清您的意思,并告诉我们minimal reproducible example到底出了什么问题。
  • 如果您得到奇怪的结果,您应该发布minimal reproducible example,我们可以告诉您原因。以这种形式向他们提问过于宽泛,您会得到如下一般性的答案。
  • 为什么不直接看生成的代码?
  • @CleboSevic 你太个人化了。除了您确实让 cmets 如何改进您的问题这一事实之外,downvote 按钮本身表明它适用于 unclear 问题。我不能否认,虽然我有点明白你想要什么的要点(这就是为什么我没有被否决投票),但这只是模糊的措辞。 “外包”和“跳转范围”没有特别的含义,而您的意思可能是“在范围之外运行构造函数/析构函数”。

标签: c++ scope compiler-optimization


【解决方案1】:

只要代码的可观察结果(由标准定义)是相同的 - 在优化之前和之后,编译器就可以进行它想要的任何转换

注意:例如,执行速度被视为可观察的结果。此外,如果您的程序包含未定义行为,那么 任何 结果都是可以接受的。

【讨论】:

  • 值得澄清的是,执行速度被视为可观察的结果。
  • 但同样值得注意的是,对任何类型的计时工具的调用通常对编译器都是不透明的,因此不能证明它是无副作用的。因此,编译器无法根据需要重新排序计时调用。
  • @MaxLanghof 你是对的,但如果我要列出不被视为可观察到的副作用的所有内容,那么我不妨引用标准。并且列出对编译器不透明的所有内容也将是一个冗长的列表。我相信我已经在当前问题中提到了与 OP 相关的所有内容。所以我会留在那里。 ?
【解决方案2】:

如果您的PerfEventBlock 构造函数和析构函数执行系统调用,那么编译器应该无法证明它没有可观察到的副作用。但是,它仍然可以(理论上)在这些代码之前或之后重新排序您的间歇性代码,只要它可以证明这样做不会从抽象机器的角度改变可观察到的行为。另见例如here.

例子:

void inc(int& a) { ++a; }

void foo();

int bar()
{
    int a = 1;
    foo();
    inc(a);
    foo();
    return a;
}

https://godbolt.org/z/gOr7aU

编译器不知道foo() 做了什么,所以它必须假设存在可观察到的副作用。尽管如此,您仍然可以在程序集中看到 inc 是如此微不足道,编译器将其内联(并不断折叠)它。没有义务在两个副作用之间执行inc(a) 所涉及的任何事情,因为inc 本身没有外部可观察到的影响。

因此,编译器可能会将您要分析的代码从PerfEventBlock 的构造函数和析构函数之间移出。事实上,通过链接时优化,这个选项可以比你想象的更进一步(对另一个编译单元的函数调用 - 例如这里的foo() - 可能对编译器不透明,但对链接器不透明)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-19
    • 1970-01-01
    • 2010-09-21
    相关资源
    最近更新 更多