【问题标题】:boost::transform vs std::transformboost::transform 与 std::transform
【发布时间】:2016-04-22 15:05:24
【问题描述】:

从下面的 sn-p 中,我是否应该得出结论,std::transformboost::transform 更有效,因为前者使用的初始化和析构函数比后者少?

#include <algorithm>
#include <boost/range/algorithm.hpp>
class Ftor {
public:
    Ftor(const Ftor& rhs) : t(rhs.t)
    { std::cout << " Ftor : copy\n"; }
    Ftor(float rate) : t(rate)
    { std::cout << " Ftor : init\n"; }
    ~Ftor()
    { std::cout << "~Ftor : ...\n"; }
    float operator() (float x) { return x < t ? 0.0 : 1.0; }
private:
    float t;
};

typedef std::vector<float> vec_t;

int main (void) 
{
    vec_t arg(/*...*/);
    vec_t val(arg.size());
    float x = 1.0;
    /* Standard transform test */
    std::cout << "Standard transform:\n";
    std::transform(arg.begin(), arg.end(), val.begin(), Ftor(x));
    std::cout << "Boost transform:\n";
    /* Boost transform test */
    boost::transform(boost::make_iterator_range(arg.begin(), arg.end()),
                     val.begin(), Ftor(x));
}

输出:

Standard transform:
 Ftor : init
~Ftor : ...
Boost transform:
 Ftor : init
 Ftor : copy
~Ftor : ...
~Ftor : ...

标准转换使用 2 次调用。 Boost 变换使用 4 次调用。标准变换获胜。还是……?

附录

正如@sehe 建议的那样,std::ref 每次调用transform 都会保存一个构造函数,而boost::transform 只使用一次调用。但是std::ref 不能将临时变量作为参数。但是,传递Ftor f(x) 很好,因为后者有一个明确的地址。

在循环内调用 transform 时计算构造函数/析构函数调用。我现在有两个 boost 选项:

std::cout << "with std::ref\n";
for (/*...*/) {
    x = ...;
    f = Ftor(x);
    boost::transform(arg, val.begin(), std::ref(f));
}

std::cout << "with temporary\n";
for (/*...*/) {
    x = ...;
    boost::transform(arg, val.begin(), Ftor(x));
}

输出:

with std::ref
 Ftor : init
 Ftor : init
 ...
~Ftor : ...
with temporary
 Ftor : init
 Ftor : copy
~Ftor : ...
~Ftor : ...
 Ftor : init
 Ftor : copy
~Ftor : ...
~Ftor : ...
 ...

我有一个 gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4 有或没有-O3 产生相同的结果。

构造函数/析构函数是否“昂贵”是相对于operator()。它是最终产品,它将执行不太苛刻的数学运算,与上面的示例没有什么不同。

Complete example on coliru

【问题讨论】:

  • 编辑几乎没有意义。为什么在仿函数中甚至没有使用成员变量t
  • return (x&lt;t?1.:0.);
  • 天哪。我真的需要检查我的眼睛。无论如何,等待基准代码(或者可能是代码试图实现的目标。可能有更好的算法方法)
  • 重新。编辑。 Complete example 再次计算了你不应该计算的东西。实际的基准在哪里?

标签: c++ algorithm boost transform std


【解决方案1】:

STL 算法可以复制它们的谓词/函子。这主要是因为它们是按值传递的。

你看到的是boost 做了一个更多的前转呼叫。

有问题吗?

通常不会。编译器非常擅长内联、复制省略和依赖分析。

很有可能,生成的代码最终完全相同。

当然,添加 cout 语句会完全破坏这一点。比较生成的代码没有构造函数/析构函数的副作用破坏它!

公平比较 using no-side-effects 为 STL 和 Boost 变体生成相同代码:http://paste.ubuntu.com/14544891/

修复

根据 STL(和 boost 算法)的设计方式,您可以根据需要显式地通过引用传递函子。您为此使用std::ref

Live On Coliru

#include <algorithm>
#include <vector>
#include <iostream>
#include <functional>
#include <boost/range/algorithm.hpp>

class Ftor {
  public:
    Ftor(const Ftor &rhs) : t(rhs.t) { std::cout << " Ftor : copy" << std::endl; } 
    Ftor(float rate) : t(rate)       { std::cout << " Ftor : init" << std::endl; } 
    ~Ftor()                          { std::cout << "~Ftor : ..."  << std::endl; } 
    float operator()(float x)        { return x; }  

  private:
    float t;
};

typedef std::vector<float> vec_t;

int main(void) {
    vec_t arg(190, 1), val(arg.size());

    {
        std::cout << "STL transform: " << std::endl;
        Ftor f(1.0);
        std::transform(arg.begin(), arg.end(), val.begin(), std::ref(f));
    }
    std::cout << "-----\n";

    {
        std::cout << "Boost transform: " << std::endl;
        Ftor f(1.0);
        boost::transform(arg, val.begin(), std::ref(f));
    }
    std::cout << "-----\n";
}

打印

STL transform: 
Ftor : init
~Ftor : ...
-----
Boost transform: 
Ftor : init
~Ftor : ...
-----

注意无论如何,在容器上使用范围算法并构造一个范围boost::make_iterator_range(arg.begin(), arg.end()) 而不仅仅是使用arg,这非常讽刺: p>

boost::transform(arg, val.begin(), Ftor(x));

【讨论】:

  • 公平比较 using no-side-effects 为 STL 和 Boost 变体生成 相同 代码:paste.ubuntu.com/14544891
  • 赞成公平比较...和讽刺的评论:D
  • 如果我用boost::transform(arg, val.begin(), std::ref(Ftor(1.0))); 替换接近末尾的行boost::transform(arg, val.begin(), std::ref(f)); 我得到一个错误:使用已删除的函数'void std::ref(const _Tp&&) [with _Tp = Ftor]'。什么给了?
  • 您不能安全地将引用传递给临时对象。这是 boosts 引用包装类中内置的安全预防措施
  • 因此,如果我在循环中有很多 transforms,开销可能会很重要...我更喜欢 A:for(/* ... */ ) { tmp=Ftor(x); boost::transform(arg,val.begin(),std::ref(tmp));} 还是 B:for(/* ... */ ) { boost::transform(arg,val.begin(),Ftor(x));}
猜你喜欢
  • 2011-09-14
  • 2020-08-03
  • 1970-01-01
  • 2012-08-15
  • 2022-11-22
  • 1970-01-01
  • 2021-09-22
  • 1970-01-01
  • 2014-04-02
相关资源
最近更新 更多