使用C ++ 11的“自动”可以提高性能吗?


230

我可以看到为什么autoC ++ 11中的类型可以提高正确性和可维护性。我已经读到它也可以提高性能(Herb Sutter的Always Always Auto),但是我错过了一个很好的解释。

  • 如何auto提高性能?
  • 谁能举一个例子?

5
请参阅herbutterutter.com/2013/06/13/…,其中谈到了避免意外的隐式转换,例如从小工具到小部件的转换。这不是一个普遍的问题。
Jonathan Wakely 2015年

42
您是否接受“减少不必要的悲观贬低”作为性能改进?
5gon12eder 2015年

1
仅在将来清除代码的性能,也许
Croll

我们需要一个简短的答案:如果您还不错,那就不要。它可以防止“笨拙”的错误。C ++的学习曲线会杀死那些根本没有做到的人。
亚历克·蒂尔

Answers:


309

auto可以避免隐式隐式转换,从而提高性能。我发现一个令人信服的例子如下。

std::map<Key, Val> m;
// ...

for (std::pair<Key, Val> const& item : m) {
    // do stuff
}

看到错误了吗?在这里,我们想通过const引用优雅地获取地图中的每个项目,并使用新的range-for表达式使我们的意图清晰明了,但实际上我们正在复制每个元素。这是因为std::map<Key, Val>::value_typestd::pair<const Key, Val>,没有std::pair<Key, Val>。因此,当我们(隐式)拥有:

std::pair<Key, Val> const& item = *iter;

不必引用现有对象并将其留在那儿,我们必须进行类型转换。只要存在隐式转换,就可以对其他类型的对象(或临时对象)进行const引用,例如:

int const& i = 2.0; // perfectly OK

类型转换是允许的隐式转换,其原因与您可以转换 const KeyKey,但我们必须建立一个临时的新型的,以允许这一点。因此,有效地,我们的循环可以做到:

std::pair<Key, Val> __tmp = *iter;       // construct a temporary of the correct type
std::pair<Key, Val> const& item = __tmp; // then, take a reference to it

(当然,实际上没有__tmp对象,只是为了说明而已,实际上,未命名的临时对象在item其整个生命周期中都受到约束)。

只是更改为:

for (auto const& item : m) {
    // do stuff
}

刚刚为我们节省了很多副本-现在引用的类型与初始值设定项类型匹配,因此不需要临时或转换,我们可以直接进行引用。


19
@Barry您能解释一下为什么编译器会愉快地制作副本,而不是抱怨尝试将an std::pair<const Key, Val> const &视为as std::pair<Key, Val> const &吗?C ++ 11的新手,不确定范围如何auto发挥作用。
Agop

@Barry感谢您的解释。那是我所缺少的那部分-由于某种原因,我认为您不能一直引用一个临时对象。但是,您当然可以-它的作用域将不再存在。
Agop

@barry我明白了,但问题是没有答案可以说明使用所有auto可提高性能的原因。因此,我将在下面用自己的话写下来。
Yakk-Adam Nevraumont

38
我仍然不认为这是“ auto提高性能”的证明。这只是“ auto帮助防止程序员犯下破坏性能的错误”的示例。我认为两者之间有一个微妙而重要的区别。不过,+ 1。
Lightness Races in Orbit

70

因为 auto推导了初始化表达式的类型,所以不涉及类型转换。与模板化算法结合使用,这意味着与自己编写类型相比,您可以获得更直接的计算-特别是在处理无法命名的表达式时!

一个典型的例子来自(ab)使用std::function

std::function<bool(T, T)> cmp1 = std::bind(f, _2, 10, _1);  // bad
auto cmp2 = std::bind(f, _2, 10, _1);                       // good
auto cmp3 = [](T a, T b){ return f(b, 10, a); };            // also good

std::stable_partition(begin(x), end(x), cmp?);

使用cmp2cmp3,整个算法可以内联比较调用,而如果构造一个std::function对象,不仅不能内联该调用,而且还必须在函数包装器的类型擦除内部进行多态查找。

关于此主题的另一个变体是您可以说:

auto && f = MakeAThing();

这始终是一个引用,绑定到函数调用表达式的值,并且从不构造任何其他对象。如果您不知道返回值的类型,则可能会被迫通过构造一个新对象(也许是临时对象)T && f = MakeAThing()。(此外,auto &&甚至在返回类型不可移动且返回值为prvalue时也可以使用。)


因此这是“避免类型擦除”的原因auto。您的另一个变体是“避免意外复制”,但需要修饰;为什么auto只在其中键入类型可以让您更快呢?(我认为答案是“您输入的类型错误,并且它会默默地进行转换”)这使它成为Barry答案的解释不够充分的示例,不是吗?即,有两种基本情况:自动避免类型擦除和自动避免无意转换的无提示类型错误,这两种情况都需要花费运行时间。
Yakk-Adam Nevraumont

2
“不仅不能内联电话”-那为什么呢?你的意思是,在原则上的东西防止被devirtualized呼叫如果相关专长后的数据流分析std::bindstd::function并且std::stable_partition都被内联?或者仅仅是在实践中,没有C ++编译器会积极地内联以解决混乱?
史蒂夫·杰索普

@SteveJessop:大多数情况下是后者-经过std::function构造函数后,查看实际的调用将非常复杂,尤其是在进行小函数优化时(因此,您实际上不希望进行虚拟化)。当然,原则上一切都像,如果...
Kerrek SB

41

有两类。

auto可以避免类型擦除。有不可命名的类型(如lambdas)和几乎不可命名的类型(如Result std::bind或其他表达式模板之类的东西)。

不这样做的话auto,最终您将不得不键入将数据擦除的内容,例如std::function。类型擦除具有成本。

std::function<void()> task1 = []{std::cout << "hello";};
auto task2 = []{std::cout << " world\n";};

task1具有类型擦除开销-可能的堆分配,内联它的困难以及虚拟函数表调用的开销。 task2没有。Lambda 需要自动或其他形式的类型推导来存储而不进行类型擦除;其他类型可能是如此复杂,以至于他们实际上只需要它。

其次,您可能会弄错类型。在某些情况下,错误的类型看似完美,但会导致复制。

Foo const& f = expression();

如果expression()返回Bar const&Bar什至可以从Bar&哪里Foo构造,它将编译BarFoo将创建一个临时对象,然后将其绑定到f,并且其生存期将延长直至f消失。

程序员可能Bar const& f打算但不打算在那里复制,但无论如何都要复制。

最常见的示例是的类型*std::map<A,B>::const_iteratorstd::pair<A const, B> const&不是std::pair<A,B> const&,但是错误是一类错误,无提示地降低了性能。您可以std::pair<A, B>从构造一个std::pair<const A, B>。(地图上的键是const,因为编辑它是一个坏主意)

@Barry和@KerrekSB都在回答中首先说明了这两个原则。这仅仅是在一个答案中突出显示两个问题的尝试,其措辞针对的是问题,而不是以示例为中心。


9

现有的三个答案给出了一些示例,其中使用auto帮助“可以减少无意中的悲观化”,从而有效地“改善性能”。

硬币有反面。如果auto对象的运算符不返回基本对象,则可能会导致代码错误(仍可编译且可运行)。例如,此问题询问如何使用autoEigen库使用给出不同(不正确)的结果,以下几行

const auto    resAuto    = Ha + Vector3(0.,0.,j * 2.567);
const Vector3 resVector3 = Ha + Vector3(0.,0.,j * 2.567);

std::cout << "resAuto = " << resAuto <<std::endl;
std::cout << "resVector3 = " << resVector3 <<std::endl;

导致不同的输出。诚然,这主要是由于Eigens的懒惰评估,但是该代码对(库)用户是透明的。

虽然此处的性能没有受到很大的影响,但auto用于避免意外的悲观化可能被归类为过早的优化,或者至少是错误的;)。


1
增加了相反的问题:stackoverflow.com/questions/38415831/...
莱昂
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.