为了账号安全,请及时绑定邮箱和手机立即绑定

在C+中,I+和+i之间是否存在性能差异?

在C+中,I+和+i之间是否存在性能差异?

C++ C
ITMISS 2019-06-06 15:42:12
在C+中,I+和+i之间是否存在性能差异?我们有个问题..i++和++i 在C中?C+的答案是什么?
查看完整描述

3 回答

?
慕丝7291255

TA贡献1859条经验 获得超6个赞

[执行摘要:使用++i如果你没有明确的理由使用i++.]

对于C+来说,答案要复杂一些。

如果i是一个简单类型(不是C+类的实例),然后给出C的答案(“不,没有性能差异”)保留,因为编译器正在生成代码。

但是,如果i是C+类的实例,那么i++++i打电话给其中一个operator++职能。下面是这些函数的标准对:

Foo& Foo::operator++()   // called for ++i{
    this->data += 1;
    return *this;}Foo Foo::operator++(int ignored_dummy_value)   // called for i++{
    Foo tmp(*this);   // variable "tmp" cannot be optimized away by the compiler
    ++(*this);
    return tmp;}

因为编译器没有生成代码,而只是调用operator++函数,则无法优化tmp变量及其关联的复制构造函数。如果复制构造函数很昂贵,那么这会对性能产生重大影响。


查看完整回答
反对 回复 2019-06-06
?
大话西游666

TA贡献1817条经验 获得超14个赞

是。的确有。

+运算符可以定义为函数,也可以不定义为函数。对于原语类型(int,Double,.)操作符是内置的,所以编译器可能能够优化您的代码。但是,对于定义+操作符的对象,情况就不同了。

运算符+(Int)函数必须创建副本。这是因为预期后缀+将返回与其持有的值不同的值:它必须在临时变量中保存其值,增加其值并返回临时值。在操作符+()、前缀+的情况下,不需要创建副本:对象可以自己递增,然后返回自己。

这里有一个例子说明了这一点:

struct C{
    C& operator++();      // prefix
    C  operator++(int);   // postfixprivate:

    int i_;};C& C::operator++(){
    ++i_;
    return *this;   // self, no copy created}C C::operator++(int ignored_dummy_value){
    C t(*this);
    ++(*this);
    return t;   // return a copy}

每次调用运算符+(Int)时,您都必须创建一个副本,而编译器不能对它做任何事情。给出选择时,使用运算符+();这样就不会保存副本。在许多增量(大循环?)的情况下,它可能是重要的。和/或大型物体。


查看完整回答
反对 回复 2019-06-06
?
烙印99

TA贡献1829条经验 获得超13个赞

以下是增量操作符位于不同转换单元时的基准。编译器与g+4.5。

暂时忽略样式问题

// a.cc#include <ctime>#include <array>class Something {public:
    Something& operator++();
    Something operator++(int);private:
    std::array<int,PACKET_SIZE> data;};int main () {
    Something s;

    for (int i=0; i<1024*1024*30; ++i) ++s; // warm up
    std::clock_t a = clock();
    for (int i=0; i<1024*1024*30; ++i) ++s;
    a = clock() - a;

    for (int i=0; i<1024*1024*30; ++i) s++; // warm up
    std::clock_t b = clock();
    for (int i=0; i<1024*1024*30; ++i) s++;
    b = clock() - b;

    std::cout << "a=" << (a/double(CLOCKS_PER_SEC))
              << ", b=" << (b/double(CLOCKS_PER_SEC)) << '\n';
    return 0;}

O(N)增量

试验

// b.cc#include <array>class Something {public:
    Something& operator++();
    Something operator++(int);private:
    std::array<int,PACKET_SIZE> data;};Something& Something::operator++(){
    for (auto it=data.begin(), end=data.end(); it!=end; ++it)
        ++*it;
    return *this;}Something Something::operator++(int){
    Something ret = *this;
    ++*this;
    return ret;}

结果

虚拟机上g+4.5的结果(以秒为单位):

Flags (--std=c++0x)       ++i   i++-DPACKET_SIZE=50 -O1      1.70  2.39-DPACKET_SIZE=50 -O3      0.59  1.00-DPACKET_SIZE=500 -O1 
   10.51 13.28-DPACKET_SIZE=500 -O3     4.28  6.82

O(1)增量

试验

现在让我们来看以下文件:

// c.cc#include <array>class Something {public:
    Something& operator++();
    Something operator++(int);private:
    std::array<int,PACKET_SIZE> data;};Something& Something::operator++(){
    return *this;}Something Something::operator++(int){
    Something ret = *this;
    ++*this;
    return ret;}

它在增量中什么也不做。这模拟了增量具有恒定复杂性的情况。

结果

现在的结果差别很大:

Flags (--std=c++0x)       ++i   i++-DPACKET_SIZE=50 -O1      0.05   0.74-DPACKET_SIZE=50 -O3      0.08   0.97-DPACKET_SIZE=500 -O1
     0.05   2.79-DPACKET_SIZE=500 -O3     0.08   2.18-DPACKET_SIZE=5000 -O3    0.07  21.90

结语

性能方面

如果您不需要前面的值,请养成使用预增量的习惯。即使使用内置类型,也要保持一致,如果您用自定义类型替换内置类型,您将不会有遭受不必要的性能损失的风险。

语义层面

  • i++

    increment i, I am interested in the previous value, though.

  • ++i

    increment i, I am interested in the current value

    increment i, no interest in the previous value

    ..再说一遍,你会习惯的,即使你现在还不习惯。

克努斯。

过早的优化是万恶之源。这是过早的悲观。


查看完整回答
反对 回复 2019-06-06
  • 3 回答
  • 0 关注
  • 718 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信