【问题标题】:C++ operator's optimization via templates通过模板优化 C++ 运算符
【发布时间】:2017-03-29 13:02:00
【问题描述】:

所以,我有一个 C++ Matrix 类,并且有一些重载的运算符(如 +、*、%、-atc...),它们看起来像这样:

Matrix operator*(const Matrix& b) const;

他们正在返回 Matrix 类型的 TMP 对象,然后它被复制,这会降低性能。

如果我做类似的事情

Matrix some = a*b + c*d - (i+j)*m*b; //[1]

大约有 6 个复制构造函数被调用。

所以,我看到 ppl 做了一些模板元编程,将 [1] 扩展为类似:

Matrix some = ((a*b)+=(c*d))-=(((i+j)*=m)*=b); //[2]

[2] 处的代码将调用大约 3 个复制构造函数。(*=、+= 将结果写入左操作数并返回其链接)

你们能否解释一下,我如何通过模板来做到这一点,或者我应该阅读什么来理解如何做到这一点。

【问题讨论】:

  • "...然后它被复制了,这会降低性能。" 阅读 NRVO/RVO en.wikipedia.org/wiki/Return_value_optimization 。大多数现代编译器现在都在实现这一点。
  • 除了编译器的返回值优化和复制省略之外,您还可以实现移动运算符和std::move返回的对象。
  • "about 6" 测量优化构建时实际调用了多少复制构造函数。你可能会感到惊讶。
  • 调用了 zero 个复制构造函数,而不是 6。将一些输出放入复制构造函数中,使用 /O2 或任何调用编译器的标志进行编译,然后自己看看.

标签: c++ templates optimization operator-overloading


【解决方案1】:

他们要搜索的关键字是“表达式模板”。

不过,很多事情都可以通过使用右值引用来实现。

顺便说一句,如果你真的很认真,你会想要比你的例子建议的更多的重写;例如乘法和加法步骤通常希望合并为一个操作,而不是两个单独的操作。

【讨论】:

    【解决方案2】:

    所以,正如我在 cmets 中看到的,编译器自己优化它,但是,也有一些时刻存在,可以优化。

    开始吧。

    1. 首先,通常我们通过operator=保存重载运算符的结果,如果我们在其中添加几个字段{bool tmp,bool复制}我们可以跳过内存处理,它应该是这样的.

    .

    ...freeing memory, if needed...
    if(!(tmp && !copied)
    {
        ...memcpy actions...
    }else
    {
        ...assigning a pointers to allocated data right to our objects pointers...
        this->arr = a.arr;
        a.copied = true;
    }
    ...other post actions...
    

    我们还需要改变一个解构器

    ~SomeClass()
    {
        if( !(tmp && copied) )//If its not a tmp and not copied then freeing
        {
            ...freeing a memory...
        }//otherwise just skeep
    }
    
    1. 在运算符中,我们通常为结果对象调用构造函数,但是,如果我们的对象之一是 tmp,如果可能的话,为什么不在其中保存数据?

    .

    SomeClass operator+(SomeClass a)
    {
        ...some code...
        if(!(tmp || a.tmp))//if there is no tmp object, doing the same, as usual.
        {
            SomeClass res;
            ...some usual code
            res.tmp = true;
            res.copied = false;
            return res;
        }else
        {
            if(tmp)
            {
                ...doing operations and storing them in this...
                return *this;
            }else //"a" is tmp
            {
                ...doing operations and storing them in "a"...
                return a;
            }
        }
    }
    

    所以...通过这些操作,我们减少了被调用的操作数量,我们节省了本来会分配的内存,而且我们也没有从运算符结果中复制内存。

    现在,代码如下:

    res = a * n + b * t - k;
    

    将只为“a * n”和“b * t”分配内存,然后所有内容都将存储在这些运算符结果的 tmp 对象中。最后,我们不会将内存(值)从 tmp 对象处理到“res”,我们只是简单地将指针分配给“tmp”内存(不会释放)。

    【讨论】:

      猜你喜欢
      • 2021-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多