tengo algo de código para iterar sobre un (multivariante) rango numérico:gama más rápido para el bucle (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;
}
Compilar con g++ -std=c++11 -O3
(o -std=c++0x
con gcc < 4.7) discurre en unos 13,8 segundos en mi computadora.
Si cambio de la función de main
para utilizar un bucle basado en la gama:
for (const std::array<double, 7> & arr : nr) {
++c;
}
el tiempo de ejecución se incrementa a 29,8 segundos. Casualmente, este ~ 30 segundos de tiempo de ejecución es casi el mismo que el tiempo de ejecución del original al usar std::vector<double>
en lugar de std::array<double, N>
, lo que me lleva a creer que el compilador no puede desenrollar el código producido por el bucle for-based.
¿Hay alguna manera de tener la velocidad del original y aún usar bucles basados en rangos?
Lo que he intentado:
que pueda obtener la velocidad deseada con un bucle for cambiando dos funciones miembro en NumericRange
a base de gama: Sin embargo
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;
}
, este código se siente mal diseñado porque el != operator
no funciona como se esperaba. Normalmente para las operaciones numéricas, uso <
para terminar un lo op en lugar de ==
. Pensé en encontrar el primer valor fuera de rango, pero hacerlo analíticamente puede no dar una respuesta exacta debido a un error numérico.
¿Cómo se fuerza al != operator
a comportarse de manera similar a un <
sin engañar a otros que verán mi código? Simplemente haría que las funciones begin()
y end()
fueran privadas, pero deben ser públicas para el bucle for basado en rango.
Muchas gracias por su ayuda.
Solo una sugerencia, en el bucle 'for' basado en rango, ¿por qué no usar' auto'? Es decir. 'para (auto arr: nr)'? –
@JoachimPileborg Tienes razón, eso funcionaría y requeriría menos pulsaciones de teclas. Solo intentaba dejar lo más claro posible lo que estaba haciendo (es decir, mostrar que el cambio de rendimiento no se debía a que estaba copiando el resultado por valor varias veces). – user
La palabra clave 'auto' también debe seleccionar el tipo * más apropiado *. – dirkgently