从功能返回容器:优化速度和现代风格

时间:2013-01-04 18:27:28

标签: c++ optimization c++11 rvalue-reference

不完全是一个问题,虽然只是我一直在思考如何通过风格更优雅地编写这样的代码,同时充分利用新的c ++标准等。这是一个例子

将Fibonacci序列返回到一个容器,最多N个值(对于那些在数学上不倾斜的序列,这只是添加前两个值,前两个值等于1.即1,1,2,3,5,8,13 ,...)

示例从main运行:

std::vector<double> vec;
running_fibonacci_seq(vec,30000000);

1)

template <typename T, typename INT_TYPE>
    void running_fibonacci_seq(T& coll, const INT_TYPE& N)
    {
        coll.resize(N);
        coll[0] = 1;
        if (N>1) {
        coll[1] = 1;
        for (auto pos = coll.begin()+2;
            pos != coll.end();
            ++pos)
        {
            *pos = *(pos-1) + *(pos-2);
        }
        }
    }

2)相同但使用 rvalue &amp;&amp;而不是&amp; 1.E。

void running_fibonacci_seq(T&& coll, const INT_TYPE& N)

编辑:正如下面评论过的用户所注意到的那样,右值和左值在时间上没有任何作用 - 由于评论中讨论的原因,速度实际上是相同的

结果N = 30,000,000

Time taken for &:919.053ms

Time taken for &&: 800.046ms

首先我知道这不是一个问题,但这些或哪个是最好的现代c ++代码?使用右值参考(&amp;&amp;),似乎移动语义就位,并且没有不必要的副本,这使得时间有了很小的改进(对于我来说,由于未来的实时应用程序开发很重要)。一些特定的“问题”是

a)将一个容器(在我的例子中是向量)传递给函数作为参数并不是关于如何真正使用rvalue的优雅解决方案。这个事实是真的吗?如果是这样,在上面的例子中rvalue如何显示它的亮点?

b) coll.resize(N); 调用和 N = 1 的情况,有没有办法避免这些调用,因此给用户一个简单的界面只使用该函数而不动态创建向量的大小。模板元编程是否可以在这里使用,因此向量在编译时分配了特定的大小? (即running_fibonacci_seq&lt; 30000000&gt;)因为数字可能很大,是否需要使用模板元编程,如果可以,我们也可以使用this (link)

c)有更优雅的方法吗?我有一种感觉std :: transform功能可以通过使用 lambdas 来使用,例如。

    void running_fibonacci_seq(T&& coll, const INT_TYPE& N)
    {
        coll.resize(N);
        coll[0] = 1;
        coll[1] = 1;
        std::transform (coll.begin()+2,
                coll.end(),         // source
                coll.begin(),       // destination
                [????](????) {      // lambda as function object
                    return ????????;
                });
    }

[1] http://cpptruths.blogspot.co.uk/2011/07/want-speed-use-constexpr-meta.html

3 个答案:

答案 0 :(得分:2)

明显的答案:

std::vector<double> running_fibonacci_seq(uint32_t N);

为什么?

因为常数:

std::vector<double> const result = running_fibonacci_seq(....);

因为不变量更容易:

void running_fibonacci_seq(std::vector<double>& t, uint32_t N) {
    // Oh, forgot to clear "t"!
    t.push_back(1);
    ...
}

但速度是多少?

有一种称为返回值优化的优化,允许编译器在许多情况下省略副本(并直接在调用者的变量中构建结果)。即使复制/移动构造函数具有副作用,C ++标准也明确允许它。

那么,为什么要传递“out”参数?

  • 您只能有一个返回值(叹息
  • 您可能希望重用已分配的资源(此处为t的内存缓冲区)

答案 1 :(得分:2)

由于“引用折叠”,此代码不使用右值引用,也不会移动任何内容:

template <typename T, typename INT_TYPE>
void running_fibonacci_seq(T&& coll, const INT_TYPE& N);

running_fibonacci_seq(vec,30000000);

当你认识到这一点时,你的所有问题(以及现有的评论)都变得毫无意义。

答案 2 :(得分:1)

简介:

#include <vector>
#include <cstddef>
#include <type_traits>

template <typename Container>
Container generate_fibbonacci_sequence(std::size_t N)
{
    Container coll;
    coll.resize(N);
    coll[0] = 1;
    if (N>1) {
      coll[1] = 1;
      for (auto pos = coll.begin()+2;
        pos != coll.end();
        ++pos)
      {
        *pos = *(pos-1) + *(pos-2);
      }
    }
    return coll;
}

struct fibbo_maker {
  std::size_t N;
  fibbo_maker(std::size_t n):N(n) {}
  template<typename Container>
  operator Container() const {
    typedef typename std::remove_reference<Container>::type NRContainer;
    typedef typename std::decay<NRContainer>::type VContainer;
    return generate_fibbonacci_sequence<VContainer>(N);
  }
};

fibbo_maker make_fibbonacci_sequence( std::size_t N ) {
  return fibbo_maker(N);
}

int main() {
  std::vector<double> tmp = make_fibbonacci_sequence(30000000);
}

fibbo_maker的东西只是我聪明。但它让我可以推断出你想要的纤维序列的类型,而不必重复它。