【问题标题】:C++0x performance improvementsC++0x 性能改进
【发布时间】:2010-11-01 19:13:20
【问题描述】:

允许编写更高效的 C++ 代码的 C++0x 改进之一是 unique_ptr 智能指针(太糟糕了,它不允许通过 memmove() 之类的操作移动:该提案未纳入草稿)。

即将发布的标准中还有哪些其他性能改进?以以下代码为例:

vector<char *> v(10,"astring");
string concat = accumulate(v.begin(),v.end(), string(""));

代码将连接向量 v 中包含的所有字符串。这段简洁的代码的问题在于,accumulate() 复制了周围的东西,并且不使用引用。并且 string() 每次调用 plus 运算符时都会重新分配。因此,与优化良好的类比 C 代码相比,该代码的性能较差。

C++0x 是否提供了解决问题的工具或其他工具?

【问题讨论】:

标签: c++ optimization stl c++11 unique-ptr


【解决方案1】:

是的,C++ 通过称为移动语义的东西解决了这个问题。

基本上,如果该对象是临时对象,则它允许一个对象采用另一个对象的内部表示。例如,您通常可以只允许目标字符串采用源字符串的内部表示,而不是通过复制构造函数复制字符串中的每个字节。仅当源是 r 值时才允许这样做。

这是通过引入一个移动构造函数来完成的。它是一个构造函数,您知道 src 对象是一个临时对象并且正在消失。因此,目的地接受 src 对象的内部表示是可以接受的。

移动赋值运算符也是如此。

为了区分复制构造函数和移动构造函数,该语言引入了右值引用。一个类定义了它的移动构造函数来获取一个右值引用,它只会绑定到右值(临时)。所以我的班级会按照以下方式定义一些东西:

 class CMyString
 {
 private:
     char* rawStr;
 public:

     // move constructor bound to rvalues
     CMyString(CMyString&& srcStr) 
     {
         rawStr = srcStr.rawStr
         srcStr.rawStr = NULL;             
     }

     // move assignment operator 
     CMyString& operator=(CMyString&& srcStr) 
     {
         if(rawStr != srcStr.rawStr) // protect against self assignment
         {
             delete[] rawStr;
             rawStr = srcStr.rawStr
             srcStr.rawStr = NULL;
         }
         return *this;
     }

     ~CMyString()
     {
         delete [] rawStr;
     }
 }

Here 是一篇关于移动语义和允许您执行此操作的语法的非常好的详细文章。

【讨论】:

  • 这使得排序和旋转集合等排列操作更快。我怀疑最有可能的例子是对字符串集合进行排序。
  • 这也是对右值引用的一个很好的解释:boostpro.com/trac/wiki/BoostCon09/RValue101
  • 扩展(或转换)所有容器和算法以移动语义看起来像是一项艰巨的任务,我想知道事情进展如何,是否会在没有交易效率的情况下保留与实际 STL 的兼容性。
  • @litb: 该链接要求输入用户名和密码 :(
【解决方案2】:

一个性能提升将是广义常量表达式,它由关键字 constexpr 引入。

constexpr int returnSomething() {return 40;}

int avalue[returnSomething() + 2]; 

这不是合法的 C++ 代码,因为 returnSomething()+2 不是常量表达式。

但是通过使用 constexpr 关键字,C++0x 可以告诉编译器该表达式是一个编译时常量。

【讨论】:

  • 更多关键字???!我们已经有了 volatile、unsigned、restrict、static、const、Register ......我还有什么遗漏吗?
  • @TrevorBoydSmith:C++ 有大约 60 个关键字,你错过了很多
【解决方案3】:

对不起 - 你不能说string concat = accumulate(v.begin(),v.end(), string("")); 必须重新分配。一个简单的实现当然会。但是编译器非常允许在这里做正确的事情。

在 C++98 中已经是这种情况,C++0x 继续允许智能和愚蠢的实现。也就是说,移动语义将使智能实现更简单。

【讨论】:

  • 这里是来自 gcc 4.1.1 的累积()主体: for (; __first != __last; ++__first) __init = __init + *__first;返回__init;你说得对,实现是允许优化的——通过提供专业化。对于部分可能的字符串,特化应该:1)将字符串作为引用或指针——这很难,因为函数签名是由标准指定的 2)使用 append() 或 +=——这是可能的。但毕竟我认为该语言中适当的新工具将允许在没有如此具体的专业化的情况下解决问题。
【解决方案4】:
vector<string> v(10, "foo");
string concat = accumulate(v.begin(), v.end(), string(""));

这个例子在任何 C++ 标准中都是糟糕的编程。相当于这样:

string tmp;
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp

C++11 移动语义只会处理等式的“将结果复制回 tmp”部分。 from tmp 的初始副本仍将是副本。这是一个经典的 Schlemiel the Painter's algorithm,但比在 C 中使用 strcat 的通常示例还要糟糕。

如果accumulate 只使用+= 而不是+=,那么它会避免所有这些副本。

但 C++11 确实为我们提供了一种更好的方法,同时保持简洁,使用范围 for

string concat;
for (const string &s : v) { concat += s; }

编辑:我想标准库供应商可以选择实现accumulate,并将操作数移动到+,所以tmp = tmp + "foo" 将变为tmp = move(tmp) + "foo",这几乎可以解决这个问题。我不确定这样的实现是否会严格遵守。 GCC、MSVC 和 LLVM 在 C++11 模式下都不会这样做。由于accumulate&lt;numeric&gt; 中定义,人们可能会认为它仅设计用于数字类型。

编辑 2:从 C++20 开始,accumulate 已重新定义为使用 move,就像我之前编辑的建议一样。我仍然认为这是对仅设计用于算术类型的算法的可疑滥用。

【讨论】:

    猜你喜欢
    • 2010-12-27
    • 2022-12-17
    • 1970-01-01
    • 1970-01-01
    • 2012-01-23
    • 1970-01-01
    • 2019-12-25
    • 2015-03-21
    • 2016-12-30
    相关资源
    最近更新 更多