Более быстрый диапазон для цикла (C++11)

У меня есть код для перебора (многомерного )числового диапазона:

#include <array>
#include <limits>
#include <iostream>
#include <iterator>

template <int N>
class NumericRange : public std::iterator<double, std::input_iterator_tag>
{
public:
  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());
  }

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

  NumericRange<N> begin() const {
    NumericRange<N> result = *this;
    result.start();
    return result;
  }

  NumericRange<N> end() const {
    NumericRange<N> result = *this;
    result._state = _upper;
    return result;
  }

  bool operator !=(const NumericRange<N> & rhs) const {
    return in_range();
    //    return ! (*this == rhs);
  }

  bool operator ==(const NumericRange<N> & rhs) const {
    return _state == rhs._state && _lower == rhs._lower && _upper == rhs._upper && _delta == rhs._delta;
  }

  const NumericRange<N> & operator ++() {
    advance();
    if ( ! in_range() )
      _state = _upper;
    return *this;
  }

  const std::array<double, N> & operator *() 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);
      }
    }
  }
private:
  std::array<double, N> _lower, _upper, _delta, _state;
};

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()) {
    ++c;
  }
  std::cout << "took " << c << " steps" << std::endl;    
  return 0;
}

Компиляция сg++ -std=c++11 -O3(или -std=c++0xс gcc < 4,7 )выполняется примерно за 13,8 секунды на моем компьютере.

Если я изменю функцию main, чтобы использовать диапазон -, основанный на цикле for:

  for (const std::array<double, 7> & arr : nr) {
    ++c;
  }

время работы увеличивается до 29,8 секунд. По совпадению, это ~30-секундное время выполнения почти такое же, как время выполнения оригинала при использовании std::vector<double>вместо std::array<double, N>, что наводит меня на мысль, что компилятор не может развернуть код, созданный диапазоном -на основе цикла for.

Есть ли способ получить скорость оригинала и по-прежнему использовать диапазон -на основе циклов for?


Что я пробовал:

Я могу получить желаемую скорость с диапазоном -на основе цикла for, изменив две функции-члены вNumericRange:

bool operator !=(const NumericRange<N> & rhs) const {
  return in_range();
  //    return ! (*this == rhs);
}

const NumericRange<N> & operator ++() {
  advance();
  //    if ( ! in_range() )
  //      _state = _upper;
  return *this;
}

Однако этот код кажется плохо разработанным, потому что != operatorне работает должным образом. Обычно для числовых операций я использую <для завершения цикла, а не ==. Я думал найти первое из значений диапазона -из -, но сделать это аналитически может не дать точного ответа из-за числовой ошибки.

Как вы можете заставить != operatorвести себя аналогично <, не вводя в заблуждение других, кто увидит мой код? Я бы просто сделал функции begin()и end()закрытыми, но они должны быть общедоступными для диапазона -на основе цикла for.

Большое спасибо за твою помощь.

15
задан ildjarn 13 June 2012 в 06:23
поделиться