我有一个library,它在编译时进行了大量的循环展开。我还使用了基准标记工具,该工具通过实例化作用域内的结构来工作,在销毁时它会测量从创建到销毁之间的时间并返回收集的值。现在,通过在其中插入一些函数调用,它可以测量函数调用的时间。我想知道的是,编译器可以跳转范围吗?(请参见下面的代码)
PerfEvent ev;
{
PerfEventBlock bl(ev, countPE);
ev.setParam("name","FTensor3D");
res(l,m,n,o) = t1(l,m,k)*t2(k,n,o);
}
或者换句话说:编译器可以将“外包”工作扩展到范围之外(我得到了奇怪的结果并且很纳闷)
答案 0 :(得分:4)
只要代码的可观察结果(由标准定义)相同-在优化之前和之后,编译器就可以进行所需的任何转换。
注意:例如,执行速度不是 视为可观察的结果。另外,如果您的程序包含未定义行为,则任何结果是可以接受的。
答案 1 :(得分:2)
如果您的PerfEventBlock
构造函数和析构函数执行系统调用,则编译器将无法证明它没有可观察到的副作用。但是,只要能够证明这样做不会从抽象机器的角度改变可观察到的行为,它仍然可以(理论上)在这些代码之前或之后对间歇代码进行重新排序。另请参见here。
示例:
void inc(int& a) { ++a; }
void foo();
int bar()
{
int a = 1;
foo();
inc(a);
foo();
return a;
}
编译器不知道foo()
所做的事情,因此必须假定存在明显的副作用。尽管如此,您仍可以在程序集中看到inc
如此琐碎,编译器将其内联(并固定折叠)。在这两种副作用之间,没有义务执行inc(a)
中涉及的任何内容,因为inc
本身本身没有外部可观察到的效果。
因此,编译器可能会将您要分析的代码从PerfEventBlock
的构造函数和析构函数之间移出。实际上,通过链接时优化,此选项可以在表上的保留位置超出您的想象(对另一个编译单元的函数调用-如此处的foo()
-对于编译器而言可能是不透明的,但对链接器而言是不透明的)。