UnT*_*aDe 58 c c++ const compiler-optimization
我知道在可能的情况下,在通过引用或指针传递参数时,应尽可能使用const关键字.如果我指定参数是常量,编译器可以做的任何优化吗?
可能有几种情况:
功能参数:
常量参考:
void foo(const SomeClass& obj)
Run Code Online (Sandbox Code Playgroud)
Constant SomeClass对象:
void foo(const SomeClass* pObj)
Run Code Online (Sandbox Code Playgroud)
并且指向SomeClass的常量指针:
void foo(SomeClass* const pObj)
Run Code Online (Sandbox Code Playgroud)
变量声明:
const int i = 1234
Run Code Online (Sandbox Code Playgroud)
函数声明:
const char* foo()
Run Code Online (Sandbox Code Playgroud)
每个提供什么样的编译器优化(如果有的话)?
rav*_*avi 30
[读者请注意,这篇文章的大部分内容都来自Herb Sutter的一篇文章 - http://www.gotw.ca/gotw/081.htm - 没有OP的归属.]
情况1:-
在程序中声明const时
int const x = 2;
Run Code Online (Sandbox Code Playgroud)
编译器可以通过不为此变量提供存储来优化远离此const,而是将其添加到符号表中.因此,后续读取只需要间接到符号表而不是从内存中获取值的指令.
注意: - 如果您执行以下操作: -
const int x = 1;
const int* y = &x;
Run Code Online (Sandbox Code Playgroud)
然后这将迫使编译器为其分配空间'x'.因此,对于这种情况,这种优化程度是不可能的.
在函数参数方面const意味着该函数中未修改参数.据我所知,使用const它并没有显着的性能提升,而是确保正确性的手段.
CASE_2: -
"将参数和/或返回值声明为const有助于编译器生成更优的代码吗?"
const Y& f( const X& x )
{
// ... do something with x and find a Y object ...
return someY;
}
Run Code Online (Sandbox Code Playgroud)
问题=>编译器可以做得更好吗?
=>它可以避免参数或返回值的副本吗?
不,因为参数已经通过引用传递.
=>它可以将x或someY的副本放入只读内存吗?
不,因为x和y都生活在其范围之外,来自和/或被赋予外部世界.即使someY在f()本身内动态分配,它和它的所有权也会被放弃给调用者.
Ques =>在f()体内出现的代码可能优化怎么样?由于const,编译器能否以某种方式改进它为f()体生成的代码?
即使调用const成员函数,编译器也不能假设对象x或对象someY的位不会被更改.此外,还有其他问题(除非编译器执行全局优化):编译器也可能不确定没有其他代码可能有非const引用,该引用将同一对象别名为x和/或someY,以及是否有任何此类在执行f()期间,可能会偶然使用对同一对象的非const引用; 并且编译器甚至可能不知道x和someY仅仅是引用的真实对象实际上是否首先被声明为const.
CASE_3: -
void f( const Z z )
{
// ...
}
Run Code Online (Sandbox Code Playgroud)
问题=>这会有优化吗?
是的,因为编译器知道z确实是一个const对象,即使没有全局分析,它也可以执行一些有用的优化.例如,如果f()的主体包含类似g(&z)的调用,则编译器可以确保z的非可变部分在调用g()期间不会更改
Mic*_*yan 12
在给出任何答案之前,我想强调的是,使用或不使用的原因const实际上应该是程序的正确性以及其他开发人员的清晰度,而不是编译器优化; 也就是说,创建一个参数const文档,该方法不会修改该参数,并使成员函数const文档表明该成员不会修改它所属的对象(至少不能以逻辑方式更改任何输出的对象)其他const成员函数).例如,这样做允许开发人员避免制作不必要的对象副本(因为他们不必担心原件将被销毁或修改)或避免不必要的线程同步(例如,通过知道所有线程只读取和执行不要改变有问题的对象).
至少在理论上,编译器可以进行优化,尽管在优化模式下允许它做出某些可能破坏标准C++代码的非标准假设,请考虑:
for (int i = 0; i < obj.length(); ++i) {
f(obj);
}
Run Code Online (Sandbox Code Playgroud)
假设length函数被标记为const但实际上是一个昂贵的操作(假设它实际上在O(n)时间而不是O(1)时间运行).如果函数f通过const引用获取其参数,则编译器可能会优化此循环以:
int cached_length = obj.length();
for (int i = 0; i < cached_length; ++i) {
f(obj);
}
Run Code Online (Sandbox Code Playgroud)
...因为函数f不修改参数的事实保证了length每次给定对象没有改变时函数应该返回相同的值.但是,如果f声明通过可变引用获取参数,则length需要在循环的每次迭代中重新计算,因为f可能以某种方式修改对象以产生值的更改.
正如评论中所指出的,这是假设一些额外的警告,并且只有在非标准模式下调用编译器时才有可能允许它做出额外的假设(例如,这些const方法严格地取决于它们的输入和优化可以假设代码永远不会用于const_cast将const引用参数转换为可变引用.
功能参数:
const对于引用的内存并不重要.这就像在优化器后面绑一只手.
假设您调用另一个没有可见定义的函数(例如void bar())foo.优化器将具有限制,因为它无法知道是否bar已修改传递给的函数参数foo(例如,通过访问全局内存).外部修改内存的可能性和别名会给该领域的优化器带来重大限制.
虽然您没有问,但函数参数的const 值确实允许优化,因为优化器可以保证是一个const对象.当然,复制该参数的成本可能远高于优化器的好处.
见:http://www.gotw.ca/gotw/081.htm
变量声明:
const int i = 1234
这取决于它的声明位置,创建时间和类型.此类别主要是const优化存在的地方.修改const对象或已知常量是未定义的,因此允许编译器进行一些优化; 它假定您不调用未定义的行为并引入一些保证.
const int A(10);
foo(A);
// compiler can assume A's not been modified by foo
Run Code Online (Sandbox Code Playgroud)
显然,优化器还可以识别不变的变量:
for (int i(0), n(10); i < n; ++i) { // << n is not const
std::cout << i << ' ';
}
Run Code Online (Sandbox Code Playgroud)
函数声明:
const char* foo()
不重要.可以在外部修改引用的存储器.如果返回的引用变量foo是可见的,那么优化器可以进行优化,但这与const函数返回类型的存在/不存在无关.
同样,const值或对象是不同的:
extern const char foo[];
const 的确切效果因使用它的每个上下文而异。如果在声明变量时使用 const,则该变量在物理上是 const 并且有效地驻留在只读内存中。
const int x = 123;
Run Code Online (Sandbox Code Playgroud)
试图摆脱常量是未定义的行为:
尽管 const_cast 可以从任何指针或引用中删除常量性或易失性,但使用生成的指针或引用写入声明为 const 的对象或访问声明为 volatile 的对象会调用未定义的行为。cppreference/const_cast
因此在这种情况下,编译器可能会假设 的值x始终为123。这开启了一些优化潜力(恒定传播)
对于函数来说则是另一回事。认为:
void doFancyStuff(const MyObject& o);
Run Code Online (Sandbox Code Playgroud)
我们的函数doFancyStuff可以使用 执行以下任何操作o。
mutableMyObject 的数据成员请注意,如果您使用声明为 const 的 MyObject 实例调用我们的函数,您将使用 #2 调用未定义的行为。
大师问题:以下内容会调用未定义的行为吗?
const int x = 1;
auto lam = [x]() mutable {const_cast<int&>(x) = 2;};
lam();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
17228 次 |
| 最近记录: |