0

我在类似矢量的类中使用表达式模板进行转换,例如移动平均线。在这里,与标准算术运算不同,operator[](size_t i)不会对 element 进行单个访问i,而是需要评估整个循环,例如对于向量的移动平均值v

double operator[](size_t i) const
{
    double ad=0.0;
    for(int j=i-period+1; j<=i; ++j)
       ad+=v[j];
    return ad/period;
}

(这不是真正的功能,因为必须关心非负索引,但现在这无关紧要)。

在使用这种移动平均线结构时,我担心代码会变得相当低效,尤其是在采用双重或三重移动平均线的情况下。然后获得嵌套循环,因此获得周期大小的二次或三次缩放。

我的问题是,编译器是否如此聪明地以某种方式优化这些冗余循环?还是不是这种情况,必须手动处理中间存储(我猜是这样)?如何在下面的示例代码中合理地做到这一点?


示例代码,改编自Wikipedia,使用 Visual Studio 2013 编译:

CRTP 基类和实际向量:

#include <vector>

template <typename E>
struct VecExpression
{
    double operator[](int i) const { return static_cast<E const&>(*this)[i]; }
};

struct Vec : public VecExpression<Vec>
{
    Vec(size_t N) : data(N) {}
    double operator[](int i) const { return data[i]; }
    double& operator[](int i) { return data[i]; }

    std::vector<double> data;
};

移动平均线类:

template <typename VectorType>
struct VecMovingAverage : public VecExpression<VecMovingAverage<VectorType> >
{
    VecMovingAverage(VectorType const& _vector, int _period) : vector(_vector), period(_period) {}
    double operator[](int i) const
    {
        int s = std::max(i - period + 1, 0);
        double ad = 0.0;
        for (int j = s; j <= i; ++j)
            ad += vector[j];
        return ad / (i - s + 1);
    }

    VectorType const& vector;
    int period;
};

template<typename VectorType>
auto MovingAverage(VectorType const& vector, int period = 10) -> VecMovingAverage<VectorType>
{
    return VecMovingAverage<VectorType>(vector, period);
}

现在我上面提到的恐惧出现在这样的表达中,

Vec vec(100);
auto tripleMA= MovingAverage(MovingAverage(MovingAverage(vec,20),20),20);
std::cout << tripleMA[40] << std::endl;

我想这需要20^3对单个 ... 进行评估operator[]


编辑:一个明显的解决方案是存储结果。移动std::vector<double> data到基类,然后将移动平均类更改为(未经测试)

template <typename VectorType, bool Store>
struct VecMovingAverage : public VecExpression<VecMovingAverage<VectorType, Store> >
{
    VecMovingAverage(VectorType const& _vector, int _period) : vector(_vector), period(_period) {}
    double operator[](int i) const
    {
        if(Store && i<data.size())
        {
            return data[i];
        }
        else
        {
           int s = std::max(i - period + 1, 0);
           double ad = 0.0;
           for (int j = s; j <= i; ++j)
              ad += vector[j];
           ad /= (i - s + 1)

           if(Store)
           {
               data.resize(i+1);
               data[i]=ad;
           }
           return ad;
        }
    }

    VectorType const& vector;
    int period;
};

然后可以在函数中选择存储结果:

template<typename VectorType>
auto MovingAverage(VectorType const& vector, int period = 10) -> VecMovingAverage<VectorType>
{
    static const bool Store=true;
    return VecMovingAverage<VectorType, Store>(vector, period);
}

这可以扩展为仅将存储应用于多个应用程序等。

4

0 回答 0