c++中的常量和编译器优化。

时间:2022-08-11 02:20:08

I've read all the advice on const-correctness in C++ and that it is important (in part) because it helps the compiler to optimize your code. What I've never seen is a good explanation on how the compiler uses this information to optimize the code, not even the good books go on explaining what happens behind the curtains.

我已经阅读了c++中关于const-correct的所有建议,它(部分)很重要,因为它可以帮助编译器优化代码。我从来没有见过编译器如何使用这些信息来优化代码的很好的解释,即使是好书也不会继续解释幕后发生的事情。

For example, how does the compiler optimize a method that is declared const vs one that isn't but should be. What happens when you introduce mutable variables? Do they affect these optimizations of const methods?

例如,编译器如何优化声明为const的方法,而不是应该声明为const的方法。当你引入可变变量时会发生什么?它们会影响const方法的这些优化吗?

12 个解决方案

#1


35  

Let's disregard methods and look only at const objects; the compiler has much more opportunity for optimization here. If an object is declared const, then (ISO/IEC 14882:2003 7.1.5.1(4)):

我们不考虑方法,只看const对象;编译器在这里有更多的优化机会。如果一个对象被声明为const,则(ISO/ iec14882:2003 7.1.5.1(4)):

Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.

除了声明可变(7.1.1)的任何类成员可以被修改,任何试图在其生命周期内修改const对象的尝试(3.8)都会导致未定义的行为。

Lets disregard objects that may have mutable members - the compiler is free to assume that the object will not be modified, therefore it can produce significant optimizations. These optimizations can include things like:

让我们忽略可能具有可变成员的对象——编译器可以*地假设对象不会被修改,因此它可以产生显著的优化。这些优化包括以下内容:

  • incorporating the object's value directly into the machines instruction opcodes
  • 将对象的值直接合并到机器指令操作码中
  • complete elimination of code that can never be reached because the const object is used in a conditional expression that is known at compile time
  • 由于const对象在编译时已知的条件表达式中使用,所以完全消除了无法达到的代码
  • loop unrolling if the const object is controlling the number of iterations of a loop
  • 如果const对象控制循环的迭代次数,则循环展开。

Note that this stuff applies only if the actual object is const - it does not apply to objects that are accessed through const pointers or references because those access paths can lead to objects that are not const (it's even well-defined to change objects though const pointers/references as long as the actual object is non-const and you cast away the constness of the access path to the object).

注意,这东西只适用于如果实际对象是常量,它不适用于通过常量指针或引用访问的对象,因为这些访问路径可能导致对象不是常量(甚至定义良好的改变对象虽然常量指针/引用只要实际对象是non-const你抛弃constness访问路径的对象)。

In practice, I don't think there are compilers out there that perform any significant optimizations for all kinds of const objects. but for objects that are primitive types (ints, chars, etc.) I think that compilers can be quite aggressive in optimizing the use of those items.

在实践中,我认为没有编译器对所有const对象执行任何重要的优化。但是对于原始类型(int, chars,等等)的对象,我认为编译器在优化这些项的使用方面是非常积极的。

#2


54  

I think that the const keyword was primarily introduced for compilation checking of the program semantic, not for optimization.

我认为const关键字主要用于程序语义的编译检查,而不是用于优化。

Herb Sutter, in the GotW #81 article, explains very well why the compiler can't optimize anything when passing parameters by const reference, or when declaring const return value. The reason is that the compiler has no way to be sure that the object referenced won't be changed, even if declared const : one could use a const_cast, or some other code can have a non-const reference on the same object.

在GotW #81文章中,Herb Sutter很好地解释了为什么编译器不能在通过const引用传递参数或声明const返回值时进行任何优化。原因是编译器没有办法确保引用的对象不会被更改,即使声明了const:一个可以使用const_cast,或者其他一些代码可以在同一个对象上有一个非常量引用。

However, quoting Herb Sutter's article :

然而,引用赫伯·萨特的文章:

There is [only] one case where saying "const" can really mean something, and that is when objects are made const at the point they are defined. In that case, the compiler can often successfully put such "really const" objects into read-only memory[...].

只有一种情况是说“const”可以真正意义上的意思,那就是当对象在被定义的时候成为常量。在这种情况下,编译器通常能够成功地将“真正的const”对象放入只读内存[…]。

There is a lot more in this article, so I encourage you reading it: you'll have a better understanding of constant optimization after that.

本文中还有很多内容,因此我鼓励您阅读:在此之后,您将对常量优化有更好的理解。

#3


6  

handwaving begins

挥臂开始

Essentially, the earlier the data is fixed, the more the compiler can move around the actual assignment of the data, ensuring that the pipeline doesn't stall out

本质上,数据越早被修复,编译器就越能绕过数据的实际分配,确保管道不会超时

end handwaving

结束挥臂

#4


5  

Meh. Const-correctness is more of a style / error-checking thing than an optimisation. A full-on optimising compiler will follow variable usage and can detect when a variable is effectively const or not.

咩。与优化相比,const -正确性更像是一种样式/错误检查。一个完整的优化编译器将遵循变量的用法,并且可以检测变量是否有效。

Added to that, the compiler cannot rely on you telling it the truth - you could be casting away the const inside a library function it doesn't know about.

此外,编译器不能依赖于您告诉它真相——您可能正在丢弃它不知道的库函数中的const。

So yes, const-correctness is a worthy thing to aim for, but it doesn't tell the compiler anything it won't figure out for itself, assuming a good optimising compiler.

所以,肯定的是,const-correct是值得追求的,但是它并没有告诉编译器任何它自己不知道的东西,假设一个好的优化编译器。

#5


3  

It does not optimize the function that is declared const.

它不优化被声明为常量的函数。

It can optimize functions that call the function that is declared const.

它可以优化调用声明为const的函数的函数。

void someType::somefunc();

void MyFunc()
{
    someType A(4);   // 
    Fling(A.m_val);
    A.someFunc();
    Flong(A.m_val);
}

Here to call Fling, the valud A.m_val had to be loaded into a CPU register. If someFunc() is not const, the value would have to be reloaded before calling Flong(). If someFunc is const, then we can call Flong with the value that's still in the register.

在这里,我们称它为“纵欲”。m_val必须被加载到CPU寄存器中。如果someFunc()不const,则必须在调用Flong()之前重新加载值。如果某个函数是const,那么我们可以调用Flong,它的值仍然在寄存器中。

#6


3  

The main reason for having methods as const is for const correctness, not for possible compilation optimization of the method itself.

将方法设置为const的主要原因是为了保证const的正确性,而不是对方法本身进行可能的编译优化。

If variables are const they can (in theory) be optimized away. But only is the scope can be seen by the compiler. After all the compiler must allow for them to be modified with a const_cast elsewhere.

如果变量是常量,它们(理论上)可以被优化掉。但是只有编译器才能看到范围。毕竟编译器必须允许在其他地方使用const_cast对它们进行修改。

#7


2  

Those are all true answers, but the answers and the question seem to presume one thing: that compiler optimization actually matters.

这些都是真实的答案,但答案和问题似乎是假定了一件事:编译器优化实际上很重要。

There is only one kind of code where compiler optimization matters, that is in code that is

只有一种代码对编译器优化很重要,那就是代码

  • a tight inner loop,
  • 严密的内部循环,
  • in code that you compile, as opposed to a 3rd-party library,
  • 在你编译的代码中,与第三方库不同,
  • not containing function or method calls (even hidden ones),
  • 不包含函数或方法调用(即使是隐藏的),
  • where the program counter spends a noticeable fraction of its time
  • 程序计数器花在什么地方的时间很明显

If the other 99% of the code is optimized to the Nth degree, it won't make a hoot of difference, because it only matters in code where the program counter actually spends time (which you can find by sampling).

如果其他99%的代码都被优化到第n个级别,那么它就不会产生很大的差异,因为它只在程序计数器实际花费时间(通过抽样可以找到)的代码中很重要。

#8


1  

I would be surprised if the optimizer actually puts much stock into a const declaration. There is a lot of code that will end up casting const-ness away, it would be a very reckless optimizer that relied on the programmer declaration to assume when the state may change.

如果优化器实际上在const声明中放入了大量股票,我会感到惊讶。有大量的代码最终会让您放弃使用它,这将是一个非常鲁莽的优化器,它依赖于程序员声明来假设状态可能发生变化。

#9


1  

const-correctness is also useful as documentation. If a function or parameter is listed as const, I don't need to worry about the value changing out from under my code (unless somebody else on the team is being very naughty). I'm not sure it would be actually worth it if it wasn't built into the library, though.

const-正确性也可以作为文档使用。如果一个函数或参数被列出为const,我不需要担心从我的代码中改变的值(除非团队中的其他人非常淘气)。不过,我不确定,如果它不是建在图书馆里,它是否真的值得。

#10


0  

The most obvious point where const is a direct optimization is in passing arguments to a function. It's often important to ensure that the function doesn't modify the data so the only real choices for the function signature are these:

const是直接优化的最明显的一点是将参数传递给函数。确保函数不修改数据通常是很重要的,因此函数签名的真正选择只有以下几点:

void f(Type dont_modify); // or
void f(Type const& dont_modify);

Of course, the real magic here is passing a reference rather than creating an (expensive) copy of the object. But if the reference weren't marked as const, this would weaken the semantics of this function and have negative effects (such as making error-tracking harder). Therefore, const enables an optimization here.

当然,真正的神奇之处在于传递引用,而不是创建对象的(昂贵的)副本。但是如果引用没有标记为const,这将削弱该函数的语义并产生负面影响(例如使错误跟踪变得更困难)。因此,const允许在这里进行优化。

/EDIT: actually, a good compiler can analyze the control flow of the function, determine that it doesn't modify the argument and make the optimization (passing a reference rather than a copy) itself. const here is merely a help for the compiler. However, since C++ has some pretty complicated semantics and such control flow analysis can be very expensive for big functions, we probably shouldn't rely on compilers for this. Does anybody have any data to back me up / prove me wrong?

/EDIT:实际上,一个好的编译器可以分析函数的控制流,确定它不修改参数并进行优化(传递引用而不是复制)。这里只是对编译器的帮助。但是,由于c++有一些非常复杂的语义,而且这种控制流分析对于大型函数来说非常昂贵,因此我们可能不应该为此依赖编译器。有人有任何数据支持我吗?

/EDIT2: and yes, as soon as custom copy constructors come into play, it gets even trickier because compilers unfortunately aren't allowed to omit calling them in this situation.

/EDIT2:是的,一旦自定义复制构造函数开始发挥作用,就会变得更加棘手,因为在这种情况下,编译器不允许忽略调用它们。

#11


0  

This code,

这段代码中,

class Test
{
public:
  Test (int value) : m_value (value)
  {
  }

  void SetValue (int value) const
  {
    const_cast <Test&>(*this).MySetValue (value);
  }

  int Value () const
  {
    return m_value;
  }

private:
  void MySetValue (int value)
  {
    m_value = value;
  }

  int
    m_value;
};

void modify (const Test &test, int value) 
{
  test.SetValue (value);
}

void main ()
{
  const Test
    test (100);

  cout << test.Value () << endl;
  modify (test, 50);
  cout << test.Value () << endl;
}

outputs:

输出:

100
50

which means the const declared object has been altered in a const member function. The presence of const_cast (and the mutable keyword) in the C++ language means that the const keyword can't aide the compiler in generating optimised code. And as I pointed out in my previous posts, it can even produce unexpected results.

这意味着const声明的对象在const成员函数中被修改。在c++语言中出现const_cast(和mutablec关键字)意味着const关键字不能帮助编译器生成优化的代码。正如我在之前的文章中指出的那样,它甚至可以产生意想不到的结果。

As a general rule:

作为一般规则:

const != optimisation

const ! =优化

In fact, this is a legal C++ modifier:

事实上,这是一个合法的c++修饰语:

volatile const

#12


-1  

const helps compilers optimize mainly because it makes you write optimizable code. Unless you throw in const_cast.

const帮助编译器优化,主要是因为它使您编写可优化的代码。除非你加入const_cast。

#1


35  

Let's disregard methods and look only at const objects; the compiler has much more opportunity for optimization here. If an object is declared const, then (ISO/IEC 14882:2003 7.1.5.1(4)):

我们不考虑方法,只看const对象;编译器在这里有更多的优化机会。如果一个对象被声明为const,则(ISO/ iec14882:2003 7.1.5.1(4)):

Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.

除了声明可变(7.1.1)的任何类成员可以被修改,任何试图在其生命周期内修改const对象的尝试(3.8)都会导致未定义的行为。

Lets disregard objects that may have mutable members - the compiler is free to assume that the object will not be modified, therefore it can produce significant optimizations. These optimizations can include things like:

让我们忽略可能具有可变成员的对象——编译器可以*地假设对象不会被修改,因此它可以产生显著的优化。这些优化包括以下内容:

  • incorporating the object's value directly into the machines instruction opcodes
  • 将对象的值直接合并到机器指令操作码中
  • complete elimination of code that can never be reached because the const object is used in a conditional expression that is known at compile time
  • 由于const对象在编译时已知的条件表达式中使用,所以完全消除了无法达到的代码
  • loop unrolling if the const object is controlling the number of iterations of a loop
  • 如果const对象控制循环的迭代次数,则循环展开。

Note that this stuff applies only if the actual object is const - it does not apply to objects that are accessed through const pointers or references because those access paths can lead to objects that are not const (it's even well-defined to change objects though const pointers/references as long as the actual object is non-const and you cast away the constness of the access path to the object).

注意,这东西只适用于如果实际对象是常量,它不适用于通过常量指针或引用访问的对象,因为这些访问路径可能导致对象不是常量(甚至定义良好的改变对象虽然常量指针/引用只要实际对象是non-const你抛弃constness访问路径的对象)。

In practice, I don't think there are compilers out there that perform any significant optimizations for all kinds of const objects. but for objects that are primitive types (ints, chars, etc.) I think that compilers can be quite aggressive in optimizing the use of those items.

在实践中,我认为没有编译器对所有const对象执行任何重要的优化。但是对于原始类型(int, chars,等等)的对象,我认为编译器在优化这些项的使用方面是非常积极的。

#2


54  

I think that the const keyword was primarily introduced for compilation checking of the program semantic, not for optimization.

我认为const关键字主要用于程序语义的编译检查,而不是用于优化。

Herb Sutter, in the GotW #81 article, explains very well why the compiler can't optimize anything when passing parameters by const reference, or when declaring const return value. The reason is that the compiler has no way to be sure that the object referenced won't be changed, even if declared const : one could use a const_cast, or some other code can have a non-const reference on the same object.

在GotW #81文章中,Herb Sutter很好地解释了为什么编译器不能在通过const引用传递参数或声明const返回值时进行任何优化。原因是编译器没有办法确保引用的对象不会被更改,即使声明了const:一个可以使用const_cast,或者其他一些代码可以在同一个对象上有一个非常量引用。

However, quoting Herb Sutter's article :

然而,引用赫伯·萨特的文章:

There is [only] one case where saying "const" can really mean something, and that is when objects are made const at the point they are defined. In that case, the compiler can often successfully put such "really const" objects into read-only memory[...].

只有一种情况是说“const”可以真正意义上的意思,那就是当对象在被定义的时候成为常量。在这种情况下,编译器通常能够成功地将“真正的const”对象放入只读内存[…]。

There is a lot more in this article, so I encourage you reading it: you'll have a better understanding of constant optimization after that.

本文中还有很多内容,因此我鼓励您阅读:在此之后,您将对常量优化有更好的理解。

#3


6  

handwaving begins

挥臂开始

Essentially, the earlier the data is fixed, the more the compiler can move around the actual assignment of the data, ensuring that the pipeline doesn't stall out

本质上,数据越早被修复,编译器就越能绕过数据的实际分配,确保管道不会超时

end handwaving

结束挥臂

#4


5  

Meh. Const-correctness is more of a style / error-checking thing than an optimisation. A full-on optimising compiler will follow variable usage and can detect when a variable is effectively const or not.

咩。与优化相比,const -正确性更像是一种样式/错误检查。一个完整的优化编译器将遵循变量的用法,并且可以检测变量是否有效。

Added to that, the compiler cannot rely on you telling it the truth - you could be casting away the const inside a library function it doesn't know about.

此外,编译器不能依赖于您告诉它真相——您可能正在丢弃它不知道的库函数中的const。

So yes, const-correctness is a worthy thing to aim for, but it doesn't tell the compiler anything it won't figure out for itself, assuming a good optimising compiler.

所以,肯定的是,const-correct是值得追求的,但是它并没有告诉编译器任何它自己不知道的东西,假设一个好的优化编译器。

#5


3  

It does not optimize the function that is declared const.

它不优化被声明为常量的函数。

It can optimize functions that call the function that is declared const.

它可以优化调用声明为const的函数的函数。

void someType::somefunc();

void MyFunc()
{
    someType A(4);   // 
    Fling(A.m_val);
    A.someFunc();
    Flong(A.m_val);
}

Here to call Fling, the valud A.m_val had to be loaded into a CPU register. If someFunc() is not const, the value would have to be reloaded before calling Flong(). If someFunc is const, then we can call Flong with the value that's still in the register.

在这里,我们称它为“纵欲”。m_val必须被加载到CPU寄存器中。如果someFunc()不const,则必须在调用Flong()之前重新加载值。如果某个函数是const,那么我们可以调用Flong,它的值仍然在寄存器中。

#6


3  

The main reason for having methods as const is for const correctness, not for possible compilation optimization of the method itself.

将方法设置为const的主要原因是为了保证const的正确性,而不是对方法本身进行可能的编译优化。

If variables are const they can (in theory) be optimized away. But only is the scope can be seen by the compiler. After all the compiler must allow for them to be modified with a const_cast elsewhere.

如果变量是常量,它们(理论上)可以被优化掉。但是只有编译器才能看到范围。毕竟编译器必须允许在其他地方使用const_cast对它们进行修改。

#7


2  

Those are all true answers, but the answers and the question seem to presume one thing: that compiler optimization actually matters.

这些都是真实的答案,但答案和问题似乎是假定了一件事:编译器优化实际上很重要。

There is only one kind of code where compiler optimization matters, that is in code that is

只有一种代码对编译器优化很重要,那就是代码

  • a tight inner loop,
  • 严密的内部循环,
  • in code that you compile, as opposed to a 3rd-party library,
  • 在你编译的代码中,与第三方库不同,
  • not containing function or method calls (even hidden ones),
  • 不包含函数或方法调用(即使是隐藏的),
  • where the program counter spends a noticeable fraction of its time
  • 程序计数器花在什么地方的时间很明显

If the other 99% of the code is optimized to the Nth degree, it won't make a hoot of difference, because it only matters in code where the program counter actually spends time (which you can find by sampling).

如果其他99%的代码都被优化到第n个级别,那么它就不会产生很大的差异,因为它只在程序计数器实际花费时间(通过抽样可以找到)的代码中很重要。

#8


1  

I would be surprised if the optimizer actually puts much stock into a const declaration. There is a lot of code that will end up casting const-ness away, it would be a very reckless optimizer that relied on the programmer declaration to assume when the state may change.

如果优化器实际上在const声明中放入了大量股票,我会感到惊讶。有大量的代码最终会让您放弃使用它,这将是一个非常鲁莽的优化器,它依赖于程序员声明来假设状态可能发生变化。

#9


1  

const-correctness is also useful as documentation. If a function or parameter is listed as const, I don't need to worry about the value changing out from under my code (unless somebody else on the team is being very naughty). I'm not sure it would be actually worth it if it wasn't built into the library, though.

const-正确性也可以作为文档使用。如果一个函数或参数被列出为const,我不需要担心从我的代码中改变的值(除非团队中的其他人非常淘气)。不过,我不确定,如果它不是建在图书馆里,它是否真的值得。

#10


0  

The most obvious point where const is a direct optimization is in passing arguments to a function. It's often important to ensure that the function doesn't modify the data so the only real choices for the function signature are these:

const是直接优化的最明显的一点是将参数传递给函数。确保函数不修改数据通常是很重要的,因此函数签名的真正选择只有以下几点:

void f(Type dont_modify); // or
void f(Type const& dont_modify);

Of course, the real magic here is passing a reference rather than creating an (expensive) copy of the object. But if the reference weren't marked as const, this would weaken the semantics of this function and have negative effects (such as making error-tracking harder). Therefore, const enables an optimization here.

当然,真正的神奇之处在于传递引用,而不是创建对象的(昂贵的)副本。但是如果引用没有标记为const,这将削弱该函数的语义并产生负面影响(例如使错误跟踪变得更困难)。因此,const允许在这里进行优化。

/EDIT: actually, a good compiler can analyze the control flow of the function, determine that it doesn't modify the argument and make the optimization (passing a reference rather than a copy) itself. const here is merely a help for the compiler. However, since C++ has some pretty complicated semantics and such control flow analysis can be very expensive for big functions, we probably shouldn't rely on compilers for this. Does anybody have any data to back me up / prove me wrong?

/EDIT:实际上,一个好的编译器可以分析函数的控制流,确定它不修改参数并进行优化(传递引用而不是复制)。这里只是对编译器的帮助。但是,由于c++有一些非常复杂的语义,而且这种控制流分析对于大型函数来说非常昂贵,因此我们可能不应该为此依赖编译器。有人有任何数据支持我吗?

/EDIT2: and yes, as soon as custom copy constructors come into play, it gets even trickier because compilers unfortunately aren't allowed to omit calling them in this situation.

/EDIT2:是的,一旦自定义复制构造函数开始发挥作用,就会变得更加棘手,因为在这种情况下,编译器不允许忽略调用它们。

#11


0  

This code,

这段代码中,

class Test
{
public:
  Test (int value) : m_value (value)
  {
  }

  void SetValue (int value) const
  {
    const_cast <Test&>(*this).MySetValue (value);
  }

  int Value () const
  {
    return m_value;
  }

private:
  void MySetValue (int value)
  {
    m_value = value;
  }

  int
    m_value;
};

void modify (const Test &test, int value) 
{
  test.SetValue (value);
}

void main ()
{
  const Test
    test (100);

  cout << test.Value () << endl;
  modify (test, 50);
  cout << test.Value () << endl;
}

outputs:

输出:

100
50

which means the const declared object has been altered in a const member function. The presence of const_cast (and the mutable keyword) in the C++ language means that the const keyword can't aide the compiler in generating optimised code. And as I pointed out in my previous posts, it can even produce unexpected results.

这意味着const声明的对象在const成员函数中被修改。在c++语言中出现const_cast(和mutablec关键字)意味着const关键字不能帮助编译器生成优化的代码。正如我在之前的文章中指出的那样,它甚至可以产生意想不到的结果。

As a general rule:

作为一般规则:

const != optimisation

const ! =优化

In fact, this is a legal C++ modifier:

事实上,这是一个合法的c++修饰语:

volatile const

#12


-1  

const helps compilers optimize mainly because it makes you write optimizable code. Unless you throw in const_cast.

const帮助编译器优化,主要是因为它使您编写可优化的代码。除非你加入const_cast。