【问题标题】:Best way to return QVector返回 QVector 的最佳方式
【发布时间】:2014-01-26 17:04:42
【问题描述】:

我试图从一个应该计算移动平均值的函数中返回一个 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 设置默认值。 我尝试了同样的事情,调整大小要慢得多。 您对如何优化有什么建议吗?

问候

【问题讨论】:

  • 您的代码没有明显的慢(因为 QVector 执行写时复制)。如果代码对您来说太慢,请使用分析器来确定慢行。
  • @juanchopanza QVector,因为大多数 Qt 对象都有点特殊,不表现为值,所以我认为这不是重复的。
  • @PavelStrakhov 我做了分析。返回似乎是最慢的部分
  • 添加一些你如何调用函数的上下文。也许就地修改原始向量是一种选择?
  • 好的,所以它比什么慢?你一定有其他的理想可以与之比较。

标签: c++ qt optimization qvector


【解决方案1】:

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

【讨论】:

  • 是的,但是如果在没有函数调用的情况下编写代码,我的速度会快 3 倍。我的分配有问题吗?
  • 我看不出有什么问题。我假设你的代码没有单独的函数是完全一样的——那就很奇怪了。如果不返回向量,而是将它作为非常量引用传递并填充函数,你能做实验吗?
【解决方案2】:

您正在为每个调用分配一个新的 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);
}

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

【讨论】:

  • 另外,如果不再需要原始向量,可以在单个向量上就地完成计算。
  • @Kurt Pattyn 我认为这没有什么区别。我尝试了两个版本,它们的时间消耗没有显着差异。
【解决方案3】:

由于您声称“返回”耗时最长,因此问题可能不在函数本身,而在使用返回值的位置。

唉,这就是您的代码浪费时间的地方:

  1. 在每次调用平均值时分配QVector。估计是被重复调用了,所以没必要每次都分配一个新的vector。

  2. 在QVector::operator[]。与普通数组访问相比,它的开销要多一些,因为每次调用 operator[] 时都会执行这个讨厌的 isDetached 调用。

  3. 在QVector::append。它不仅调用isDetached,还检查和修改长度。

请注意,返回您的价值绝对没有错。这是一个微不足道的操作,几乎不需要时间。退货时您做得很好 - 并且只退货。但是你没有向我们展示你是如何使用返回值的,所以我不能告诉你你是否在那里做错了什么。

为防止重复分配和operator[] 开销,您可以使用一个类来保持向量可供重用,并使用指向向量数据的指针而不是直接使用向量。

要使其运行得更快,可能需要使用 SIMD 内部函数。

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);
    ...
  } 

【讨论】:

    【解决方案4】:

    QVector 是 a shared class。复制是一个持续的操作,应该非常快。

    【讨论】:

      猜你喜欢
      • 2020-09-03
      • 2015-06-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多