有时声称,C + + 11/14 可以获得您的性能提升即使只编译 C + + 98 代码。原因通常是沿行移动语义,如在某些 rvalue 构造函数自动生成的情况下或现在的 STL 的一部分。现在我想知道是否这些情况以前实际上已由处理 RVO 或类似的编译器优化。

然后我的问题是如果可以给我一种 C + + 的实际示例 98 代码,而无需修改,运行更快地使用编译器支持的新的语言功能。我知道,符合标准的编译器不需要执行复制 elision 和只是通过该原因移动语义可能会使有关的速度,但我想要查看的低异常的情况下,如果您将。

编辑︰ 只是清楚的我想不问一下是否新的编译器,这比旧的编译器,速度更快但相反如果没有代码,添加-std = c + + 14 向编译器标志它将运行得更快 (避免副本,但您可以拿出任何其他移动语义之外,如果我很感兴趣,太)

2014-12-22 01:03:48
问题评论:

请记住,复制 elision 并返回值优化构建使用复制构造函数的新对象时执行。但是,在复制赋值运算符,没有复制 elision (方式可以是,因为编译器知道如何处理已构造的对象是临时)。因此,在这种情况下,C + + 11/14 赢得巨大,通过让您使用移动赋值运算符的可能性。有关您的问题,我认为 C + + 98 代码应该更快如果由 C + + 11/14 编译器编译,也许是更快因为编译器较新。

使用标准库的代码也可能更快,即使您做它完全兼容与 C + + 98,因为在 C + + 11/14 基础库使用内部移动语义,在可能的情况。所以看起来相同,在 C + + 98 和 C + + 11/14 中的代码都会 (可能的) 更快的后一种情况下,使用标准库对象,如矢量、 列表等和移动语义进行区别。

@vsoftco,是这样的情况我 alluding,但不是可能都用一个示例︰ 来自我记得是否我需要定义复制构造函数,移动构造函数不自动生成的它为我们留下非常简单的类,RVO,我认为,都能够正常工作。异常可能是跟与 STL 容器,rvalue 构造函数都由库实现者 (即没有可供它使用移动代码中更改任何内容) 中的某些内容。

类不需要非常简单的以不具有复制构造函数。C + + 兴旺离不开值语义,以及拷贝构造函数,赋值运算符,析构函数等异常。

链接 @Eric 谢谢,很有趣。但是,具有快速查阅了通过速度优势在其中似乎大多来自添加std::move ,移动构造函数 (它们将需要对现有代码进行修改)。唯一真正相关的我的问题是句子"您获得即时速度优势只是通过重新编译",其中未备份的任何示例 (它不会说了 STL 在同一幻灯片上,像我在我的问题,但没有任何具体)。我被要求提供一些示例。如果我正在读错误的幻灯片,请不要让我知道。

回答:

我知道的 5 个常规类别在进行 C + + 03 编译器重新编译,因为 C + + 11 可能会导致无限的性能提升几乎无关的实施质量。这些是所有变体移动语义。

std::vector重新分配

struct bar{
  std::vector<int> data;
};
std::vector<bar> foo(1);
foo.back().data.push_back(3);
foo.reserve(10); // two allocations and a delete occur in C++03

每次在 C + + 03 foo的缓冲区重新分配它复制每个vectorbar.

在 C + + 11 相反移动bar::datas,这是基本上是免费。

在这种情况下,这依赖于内部std容器vector的优化。在每种情况下,std容器的使用是只是因为它们具有高效move语义在 C + + 11"自动"时升级您的编译器的 c + + 对象。不要阻止它包含std容器的对象还会继承自动改进move构造函数。

NRVO 失败

NRVO (命名返回值优化) 中出现故障时,它会回到副本,C + + 03 11 C + + 上它将回来在移动。NRVO 失败很容易︰

std::vector<int> foo(int count){
  std::vector<int> v; // oops
  if (count<=0) return std::vector<int>();
  v.reserve(count);
  for(int i=0;i<count;++i)
    v.push_back(i);
  return v;
}

或者甚至︰

std::vector<int> foo(bool which) {
  std::vector<int> a, b;
  // do work, filling a and b, using the other for calculations
  if (which)
    return a;
  else
    return b;
}

我们有三个值,则返回的值,并在函数内的两个不同的值。Elision 允许在合并的返回值,但不是与其他函数中的值。他们两个不能合并使用返回值而无需相互合并。

基本问题是 NRVO elision 是脆弱的并不接近return站点的更改的代码可以突然有巨大性能降低在该点发出任何诊断。在大多数 NRVO 故障情况下 C + + 11 得到与move,而 C + + 03 得到与副本。

返回的函数参数

Elision 此处是也不可能︰

std::set<int> func(std::set<int> in){
  return in;
}

在 C + + 11 这是便宜︰ 在 C + + 03 没有方法来避免复制。函数的参数不能省略了的返回值,因为由调用代码管理的生命周期和位置参数和返回值。

但是,C + + 11 可以移动到另一个。(一个小玩具例子可能可以执行某些操作set).

push_backinsert

最后 elision 到容器不会发生︰ 但 C + + 11 重载 rvalue 移动插入运算符,它保存副本。

struct whatever {
  std::string data;
  int count;
  whatever( std::string d, int c ):data(d), count(c) {}
};
std::vector<whatever> v;
v.push_back( whatever("some long string goes here", 3) );

在 C + + 03 创建是临时whatever然后它将被复制到向量v2 std::string缓冲区分配,每个都有相同的数据,其中一个将被丢弃。

在 C + + 11 临时whatever创建。whatever&& push_back重载,然后moves,临时到向量v一个std::string缓冲区是分配,并移动到向量。std::string将被丢弃。

工作分配

下面的 @Jarod42 的回答中被盗。

Elision 无法进行分配,但可以从移动。

std::set<int> some_function();

std::set<int> some_value;

// code

some_value = some_function();

some_function在此处返回,从 elide 的候选对象,但不是用于直接构造的对象,因为它不能被省略了。在 C + + 03,临时被复制到some_value内容的上述结果。在 C + + 11 中,移动到some_value,它基本上是免费。


对于上述的完整效果,您需要合成移动构造函数和赋值为您的编译器。

MSVC 2013std容器中实现移动构造函数,但无法合成移动您的类型的构造函数。

这样的类型包含std::vectors 和类似不进入 MSVC2013,这种改进,但将开始在 MSVC2015 中获取它们。

clang 和 gcc 早已实施了隐式移动构造函数。英特尔的 2013年编译器将支持移动构造函数的隐式生成,如果传递-Qoption,cpp,--gen_move_operations (他们不它默认情况下,为了进行跨兼容与 MSVC2013)。

@alarge 是。但很多时候更有效比复制构造函数的移动构造函数,通常不得不移动资源而不是复制它们。无需编写自己的移动构造函数 (并仅重新编译一个 C + + 03 程序),std库容器都将move构造函数中的"免费",更新,并且 (如果没有阻止) 使用的构造对象 (和上述的对象) 将开始在许多情况下得到自由的移动构造。很多的情况下覆盖了在 C + + 03 elision︰ 并不是所有。

这就是一种坏的优化实现,然后,返回的不同命名对象没有重叠的生存期,因为 RVO 是从理论上讲仍有可能。

@ildjarn 我喜欢注释,但是问题中已经提到,我不寻找收益之间最小限度地一致 (即无 RVO 等等),而有关在真实生活中的代码可能实际上加快这要归功于新的语言功能的示例的编译器实现︰ 即在新出现的以前不可用优化可能会免费给开发人员。

@alarge 有放置在 elision 出现故障,如当两个对象包含相互重叠的寿命也可以到第三,但不是相互省略了。然后移动 11 C + + 和 C + + 03 中的副本的需要 (为忽略-如果)。Elision 通常是脆弱的实践。上述std容器的使用时主要是因为它们是廉价移动 exoensive 复制类型免费获得 C + + 11 中重新编译 C + + 03。vector::resize是一个例外︰move使用 C + + 11 所示。

我只看到 1 个常规类别即移动语义和 5 特殊情况。

如果有类似于︰

std::vector<int> foo(); // function declaration.
std::vector<int> v;

// some code

v = foo();

而你在 C + + 11 中移动赋值,在 C + + 03,有副本。因此,在这种情况下有免费的优化。

@Yakk︰ 复制 elision 如何分配中发生?

我也相信副本 elision 不可能在分配,因为已经构造的左下方,并且编译器将了解如何与"旧"数据窃取从右手边的资源后没有合理方法 @Jarod42。但或许我是错误的我真的希望找出一次和永远的答案。当您复制构造,因为对象是"全新"并没有决定如何处理旧数据的问题时,副本 elision 意义。据我所知,唯一的例外是这样:"分配可以仅将省略了根据-如果规则"

好 C + + 的 03 代码已经没有移动在这种情况下,通过foo().swap(v);

@BenVoigt 当然可以,但不是所有代码进行了优化,并在发生这种情况并不是所有点可以都很容易地达到。

内容来源于Stack Overflow Can modern C++ get you performance for free?
请输入您的翻译

Can modern C++ get you performance for free?

确认取消