2

我试图从一个应该计算移动平均值的函数中返回一个 QVector。我的问题是如何使功能更有效。数学很好,我更想知道我在返回 QVector 时是否做错了什么。这是我到目前为止的代码:

QVector<double> moving_exponential_average(const QVector<double>& a, double lambda) {
        if(lambda <0 ) {
            lambda = 0;
        }
        if(lambda >1) {
            lambda = 1;
        }
        QVector<double> b;
        b.reserve(a.size());

        b.append(a[0]);
        double l_inv = 1-lambda;
        for(unsigned int i = 1; i < a.size(); ++i) {
            b.append(a[i]*lambda+l_inv*b[i-1]);
        }
        return b;
    }

我使用默认构造函数来阻止 QVector 设置默认值。我尝试了同样的事情,调整大小要慢得多。你有什么建议可以优化吗?

问候

4

4 回答 4

5

QVector 实现共享其数据(http://qt-project.org/doc/qt-5.0/qtcore/implicit-sharing.html#implicitly-shared),因此您不会做错任何事。

于 2014-01-26T17:08:25.200 回答
3

Since you claim that the "return" takes longest, the problem may not be in the function itself, but at the site where the returned value is used.

Alas, here's where your code wastes time:

  1. In allocation of the QVector each time the average is invoked. Presumably it's called repeatedly, so there's no need to have it allocate a new vector each time.

  2. In QVector::operator[]. It has a bit more overhead than plain array access because there's this pesky isDetached call done on every call to operator[].

  3. In QVector::append. Not only does it call isDetached, but also it checks and modifies the length as well.

Note that there's absolutely nothing wrong with returning of your value. It's a trivial operation and takes next to no time. You're doing it OK when it comes to return - and return only. But you don't show us how you use the returned value, so I can't tell you if maybe you're doing something wrong there.

To prevent repeated allocations and operator[] overhead, you could use a class that keeps a vector ready for reuse, and use a pointer-to-vector's-data instead of using the vector directly.

To make it go any faster would probably require the use of SIMD intrinsics.

class Averager {
  QVector<double> m_result;
  Q_DISABLE_COPY(Averager)
public:
  QVector<double> movingExponentialAverage(const QVector<double> & a, double lambda) {
    if (lambda < 0) lambda = 0; else if (lambda > 1) lambda = 1;
    m_result.resize(a.size());
    double * b = m_result.data();
    double lInv = 1-lambda;
    for(int i = 1; i < a.size(); ++i) {
      b[i] = a[i] * lambda + b[i-1] * l_inv;
    }
    return m_result;
  }
};

void test() {
  Averager avg; 
  QVector<double> src;
  while (true) {
    update(src);
    const QVector<double> & dst = avg.movingExponentialAverage(src, 0.2);
    ...
  } 
于 2014-01-27T08:48:39.990 回答
3

您正在为每个调用分配一个新的 QVector。另一种方法是为函数提供输入向量和输出向量:

void moving_exponential_average(const QVector<double> &a, QVector<double> &b, double lambda)
{
    //store result in b vector
    //do not use append but use the [] operator, like
    b[0] = a[0];
    ...
    b[i] = a[i] * lambda + l_inv * b[i - 1];
}

QVector<double> a;
QVector<double> b;  //make same size as a
//then repeatedly call
while (notDone) {
    update(a);
    moving_exponential_average(a, b, lambda);
}

使用此代码,结果向量仅分配一次。

于 2014-01-26T21:05:06.983 回答
2

QVector一个共享类。复制是一个持续的操作,应该非常快。

于 2014-01-26T17:07:50.603 回答