G++:移动到另一个翻译单元会中断 "const optimization"?
G++: Moving to another translation unit breaks "const optimization"?
我正在制作演示各种 C++ 优化的演示文稿,并坚持使用 const
允许它们的示例。
考虑以下代码:
object.h
class Object {
int i1;
int i2;
public:
Object(int i1_, int i2_) : i1(i1_), i2(i2_) {}
int getI1() const { return i1; }
int getI2() const { return i2; }
std::pair<int, int> calculate() const {
return std::pair<int, int>(i1 + i2, i1 * i2);
}
};
constopt.cpp
#include <iostream>
#include "object.h"
int main() {
Object o(10, 20);
std::cout << o.getI1() << " + " << o.getI2() << " = "
<< o.calculate().first << std::endl
<< o.getI1() << " * " << o.getI2() << " = "
<< o.calculate().second << std::endl;
return 0;
}
内联calculate()
时,一切正常,G++直接将常量(10和20)传递给operator <<
缓存getI1()
和getI2()
调用:
mov [=13=]xa,%esi
mov [=13=]x601080,%edi
callq 0x400740 <_ZNSolsEi@plt>
但是当我将 calculate()
移动到一个单独的翻译单元时,它会强制提取 i1
和 i2
两次(在 o.calculate().first
之前和之后):
mov (%rsp),%esi
mov 0x4(%rsp),%r14d
mov [=14=]x601080,%edi
callq 0x400740 <_ZNSolsEi@plt>
我看不出有什么区别,因为 getI1()
不依赖于 calculate()
可以产生的任何副作用,并且 Object
即使在 [=16] 时也是常量=] 在单独的翻译单元中。
是不是G++不够聪明,或者说它没有资格在这种情况下进行优化?我的猜测是它可以缓存 getI1()
和 getI2()
调用来自那个答案:How does const after a function optimize the program?
我使用 gcc 4.8.1 版。 -Os 和 -O2.
我都试过了
在这种情况下,GCC 优化器似乎没有使用 const
。相反,它会自己挖掘(不能在不同的翻译单元中执行),并搜索 pure and const functions。函数可以用 __attribute__((const))
手动标记。消除额外 getI*()
调用的阶段称为 FRE(完全冗余消除)。
好吧,要准确回答您的问题,必须了解编译器的实现细节。我不知道,所以以下内容纯属推测。
Object::i1
和 Object::i2
未声明为常量。因此,编译器在没有看到 Object::calculate
的定义的情况下,不得不假定它们可能会发生变化。
Object::calculate
本身是 const 本身并不排除它对 Object::i1
和 Object::i2
进行更改。考虑以下稍作修改的示例:
class Object {
int i1;
int i2;
int* p1;
public:
Object(int i1_, int i2_) : i1(i1_), i2(i2_), p1(&i1) {}
int getI1() const { return i1; }
int getI2() const { return i2; }
std::pair<int, int> calculate() const;
};
std::pair<int, int> Object::calculate() const {
(*p1)++;
return std::pair<int, int>(i1 + i2, i1 * i2);
}
此外,o
一开始就没有被声明为 const,因此 Object::calculate
有权做出像 const_cast
那样粗鲁的事情并逍遥法外!
because getI1() doesn't rely on any side effects that can be created by calculate()
可能会,但 C++ 标准未指定是否会。
即使您在调用 calculate()
之前将 getI1()
和 getI2()
写入流,在 C++ 中,函数参数的计算顺序是未指定的。这意味着在对 std::cout
进行任何实际写入之前,允许编译器以任何顺序对 calculate()
和 getI1()
以及 getI2()
进行所有调用。如果它决定在调用 getI1()
或 getI2()
之前对 calculate()
进行一次或两次调用,则它不能假定 i1
和 [=22= 的值]没变。
如果将表达式拆分,那么编译器应该能够看到 i1
和 i2
在调用 calculate()
:
之前不能更改
// this statement uses i1 and i2 before they can possibly be changed
std::cout << o.getI1() << " + " << o.getI2() << " = ";
// this statement might mutate i1 and i2
std::cout << o.calculate().first << std::endl
<< o.getI1() << " * " << o.getI2() << " = "
<< o.calculate().second << std::endl;
and Object is intended to be const even when calculate() is in separate translation unit.
但你实际上并没有声明 o
为 const
。
调用 const
成员函数并不能保证成员 不能 被改变,它更像是调用者和函数之间的非正式契约,而不是保证.
我正在制作演示各种 C++ 优化的演示文稿,并坚持使用 const
允许它们的示例。
考虑以下代码:
object.h
class Object {
int i1;
int i2;
public:
Object(int i1_, int i2_) : i1(i1_), i2(i2_) {}
int getI1() const { return i1; }
int getI2() const { return i2; }
std::pair<int, int> calculate() const {
return std::pair<int, int>(i1 + i2, i1 * i2);
}
};
constopt.cpp
#include <iostream>
#include "object.h"
int main() {
Object o(10, 20);
std::cout << o.getI1() << " + " << o.getI2() << " = "
<< o.calculate().first << std::endl
<< o.getI1() << " * " << o.getI2() << " = "
<< o.calculate().second << std::endl;
return 0;
}
内联calculate()
时,一切正常,G++直接将常量(10和20)传递给operator <<
缓存getI1()
和getI2()
调用:
mov [=13=]xa,%esi
mov [=13=]x601080,%edi
callq 0x400740 <_ZNSolsEi@plt>
但是当我将 calculate()
移动到一个单独的翻译单元时,它会强制提取 i1
和 i2
两次(在 o.calculate().first
之前和之后):
mov (%rsp),%esi
mov 0x4(%rsp),%r14d
mov [=14=]x601080,%edi
callq 0x400740 <_ZNSolsEi@plt>
我看不出有什么区别,因为 getI1()
不依赖于 calculate()
可以产生的任何副作用,并且 Object
即使在 [=16] 时也是常量=] 在单独的翻译单元中。
是不是G++不够聪明,或者说它没有资格在这种情况下进行优化?我的猜测是它可以缓存 getI1()
和 getI2()
调用来自那个答案:How does const after a function optimize the program?
我使用 gcc 4.8.1 版。 -Os 和 -O2.
我都试过了在这种情况下,GCC 优化器似乎没有使用 const
。相反,它会自己挖掘(不能在不同的翻译单元中执行),并搜索 pure and const functions。函数可以用 __attribute__((const))
手动标记。消除额外 getI*()
调用的阶段称为 FRE(完全冗余消除)。
好吧,要准确回答您的问题,必须了解编译器的实现细节。我不知道,所以以下内容纯属推测。
Object::i1
和 Object::i2
未声明为常量。因此,编译器在没有看到 Object::calculate
的定义的情况下,不得不假定它们可能会发生变化。
Object::calculate
本身是 const 本身并不排除它对 Object::i1
和 Object::i2
进行更改。考虑以下稍作修改的示例:
class Object {
int i1;
int i2;
int* p1;
public:
Object(int i1_, int i2_) : i1(i1_), i2(i2_), p1(&i1) {}
int getI1() const { return i1; }
int getI2() const { return i2; }
std::pair<int, int> calculate() const;
};
std::pair<int, int> Object::calculate() const {
(*p1)++;
return std::pair<int, int>(i1 + i2, i1 * i2);
}
此外,o
一开始就没有被声明为 const,因此 Object::calculate
有权做出像 const_cast
那样粗鲁的事情并逍遥法外!
because getI1() doesn't rely on any side effects that can be created by calculate()
可能会,但 C++ 标准未指定是否会。
即使您在调用 calculate()
之前将 getI1()
和 getI2()
写入流,在 C++ 中,函数参数的计算顺序是未指定的。这意味着在对 std::cout
进行任何实际写入之前,允许编译器以任何顺序对 calculate()
和 getI1()
以及 getI2()
进行所有调用。如果它决定在调用 getI1()
或 getI2()
之前对 calculate()
进行一次或两次调用,则它不能假定 i1
和 [=22= 的值]没变。
如果将表达式拆分,那么编译器应该能够看到 i1
和 i2
在调用 calculate()
:
// this statement uses i1 and i2 before they can possibly be changed
std::cout << o.getI1() << " + " << o.getI2() << " = ";
// this statement might mutate i1 and i2
std::cout << o.calculate().first << std::endl
<< o.getI1() << " * " << o.getI2() << " = "
<< o.calculate().second << std::endl;
and Object is intended to be const even when calculate() is in separate translation unit.
但你实际上并没有声明 o
为 const
。
调用 const
成员函数并不能保证成员 不能 被改变,它更像是调用者和函数之间的非正式契约,而不是保证.