다차원 수치 범위에 걸쳐 반복하는 단순 클래스 :이 함수의 재귀 버전이 더 빠른 이유는 무엇입니까?
#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;
}
I가 증가을 비 재귀 적 변형과 advance
함수 런타임를 교체 할 때 :
void advance(int index_to_advance = 0) {
bool carry;
do {
carry = false;
_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;
carry = true;
// advance(index_to_advance);
}
}
} while (carry);
}
런타임이었다 time
명령을 통해 유닉스 사용자 시간을 사용하여 가져 왔습니다. 이 코드는 옵션 -std=c++11 -O3
을 사용하여 gcc-4.7을 사용하여 컴파일되었습니다 (그러나 gcc-4.6의 c++0x
에서 작동해야한다고 생각합니다). 재귀 버전에는 13 초가 걸렸으며 반복 버전에는 30 초가 걸렸습니다. 둘 다 동일한 숫자의 advance
호출을 요구합니다 (for(ns.start()...)
루프 내에 nr.get_state()
어레이를 인쇄하는 경우 모두 동일한 작업을 수행합니다).
이것은 재미있는 수수께끼입니다. 재귀가 왜 더 효율적이고 최적화가 가능한지 알아 내도록 도와주세요.
프로파일 링을 시도하십시오. 'valgrind --allgrind'는 좋은 프로파일 러입니다. –
개인적으로 나는 gdb를 좋아합니다. Break + backtrace –
내가 보는 성능이 일치하지 않습니다. 반복 버전의 경우 다른 실행에서 30 초 또는 100 초가 소요됩니다. 아마도 미묘한 캐싱 문제가있을 수 있습니다. –