Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/128.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么这个函数的递归版本更快?_C++_C++11_Compiler Optimization - Fatal编程技术网

C++ 为什么这个函数的递归版本更快?

C++ 为什么这个函数的递归版本更快?,c++,c++11,compiler-optimization,C++,C++11,Compiler Optimization,下面是一个用于在多维数值范围内迭代的简单类: #include <array> #include <limits> template <int N> class NumericRange { public: // typedef std::vector<double>::const_iterator const_iterator; NumericRange() { _lower.fill(std::numeric_limits&

下面是一个用于在多维数值范围内迭代的简单类:

#include <array>
#include <limits>

template <int N>
class NumericRange
{
public:
  //  typedef std::vector<double>::const_iterator const_iterator;
  NumericRange() {
    _lower.fill(std::numeric_limits<double>::quiet_NaN());
    _upper.fill(std::numeric_limits<double>::quiet_NaN());
    _delta.fill(std::numeric_limits<double>::quiet_NaN());
  }
  NumericRange(const std::array<double, N> & lower, const std::array<double, N> & upper, const std::array<double, N> & delta):
    _lower(lower), _upper(upper), _delta(delta) {
    _state.fill(std::numeric_limits<double>::quiet_NaN());
    _next_index_to_advance = 0;
  }

  const std::array<double, N> & get_state() const {
    return _state;
  }

  void start() {
    _state = _lower;
  }

  bool in_range(int index_to_advance = N-1) const {
    return ( _state[ index_to_advance ] - _upper[ index_to_advance ] ) < _delta[ index_to_advance ];
  }

  void advance(int index_to_advance = 0) {
    _state[ index_to_advance ] += _delta[ index_to_advance ];
    if ( ! in_range(index_to_advance) ) {
      if (index_to_advance < N-1) {
    // restart index_to_advance
    _state[index_to_advance] = _lower[index_to_advance];

    // carry
    index_to_advance;
    advance(index_to_advance+1);
      }
    }
  }

private:
  std::array<double, N> _lower, _upper, _delta, _state;
  int _next_index_to_advance;
};

int main() {
  std::array<double, 7> lower{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
  std::array<double, 7> upper{1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0};
  std::array<double, 7> delta{0.03, 0.06, 0.03, 0.06, 0.03, 0.06, 0.03};

  NumericRange<7> nr(lower, upper, delta);
  int c = 0;
  for (nr.start(); nr.in_range(); nr.advance()) {
    const std::array<double, 7> & st = nr.get_state();
    ++c;
  }
  std::cout << "took " << c << " steps" << std::endl;

  return 0;
}
运行时是通过命令
time
使用unix用户时间获取的。代码是使用gcc-4.7编译的,带有选项
-std=c++11-O3
(但我认为它应该在gcc-4.6上使用
c++0x
)。递归版本耗时13秒,迭代版本耗时30秒。两者都需要相同数量的
advance
调用才能终止(如果您在
for(ns.start()…)
循环中打印
nr.get_state()
数组,则两者的作用相同)


这是一个有趣的谜语!请帮助我找出为什么递归更有效/更可优化。

递归版本是尾部递归的一个示例,这意味着编译器可以将递归转换为迭代。现在,一旦执行了转换,递归函数将如下所示:

void advance(int index_to_advance = 0) {
    _state[ index_to_advance ] += _delta[ index_to_advance ];
    while ( !in_range(index_to_advance) && index_to_advance < N-1 ) {
        // restart index_to_advance
        _state[index_to_advance] = _lower[index_to_advance];

        // carry
        ++index_to_advance;
        _state[ index_to_advance ] += _delta[ index_to_advance ];
    }
  }
void-advance(int-index-to-advance=0){
_状态[index_to_advance]+=_delta[index_to_advance];
而(!在范围内(索引到索引前进)&索引到索引前进
正如您看到的,您的版本包含一个额外的测试和条件变量。如果仔细观察,循环相当于

for( ; index_to_advance < N-1 && !in_range(index_to_advance);++index_to_advance)
for(;index_to_advance
(最后删除
++index_to_advance
),优化器可能有更好的机会展开它


话虽如此,我认为这并不能解释巨大的时间差,尽管它确实解释了为什么递归版本不会比迭代版本慢很多。检查生成的程序集以查看编译器实际执行的操作。

仅为David Rodriguez所说的添加更多细节:

通过尾部递归优化,函数变为:

 void advance(int index_to_advance = 0) {
  top:
  _state[ index_to_advance ] += _delta[ index_to_advance ];
  if ( ! in_range(index_to_advance) ) {
    if (index_to_advance < N-1) {
      // restart index_to_advance
      _state[index_to_advance] = _lower[index_to_advance];

      // carry
      ++index_to_advance;
      goto top;
    }
  }
}
void-advance(int-index-to-advance=0){
顶部:
_状态[index_to_advance]+=_delta[index_to_advance];
如果(!在范围内(索引到前进)){
如果(索引到前进

这确实与我的系统上的递归版本(g++4.6.3-std=c++0x)具有相同的性能。

请尝试评测
valgrind--callgrind
是一个很好的分析器。我个人喜欢gdb。中断+回溯我看到的性能不一致。对于迭代版本,我在不同的运行中得到30秒或100秒。可能存在一个微妙的缓存问题。您确定“TCO”版本准确吗?我从未完成过(约30分钟)。我还没有调查过though@sehe:您是对的,转换不正确,第一行必须在循环内(即必须应用于每次迭代)…+1我觉得很奇怪,
标签…转到
会更有效(您可以使用
label…goto
在作用域之间移动并使编译器运行),但我可以理解为什么会出现这种情况。谢谢!
 void advance(int index_to_advance = 0) {
  top:
  _state[ index_to_advance ] += _delta[ index_to_advance ];
  if ( ! in_range(index_to_advance) ) {
    if (index_to_advance < N-1) {
      // restart index_to_advance
      _state[index_to_advance] = _lower[index_to_advance];

      // carry
      ++index_to_advance;
      goto top;
    }
  }
}