返回 QVector 的最佳方式

Posted

技术标签:

【中文标题】返回 QVector 的最佳方式【英文标题】:Best way to return 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 我做了分析。返回似乎是最慢的部分 添加一些你如何调用函数的上下文。也许就地修改原始向量是一种选择? 好的,所以它比什么慢?你一定有其他的理想可以与之比较。 【参考方案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】:

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

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

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

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

    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。复制是一个持续的操作,应该非常快。

【讨论】:

以上是关于返回 QVector 的最佳方式的主要内容,如果未能解决你的问题,请参考以下文章

Qt - QVector 和模型视图 - 从 listView 获取自定义类的最佳方法是啥?

QVector<int>[index] 返回另一个 QVector?

初始化 QImages 的 QVector

QVector size() 返回大小为零

如何以简单的方式更改堆上 QVector 数组中包含的对象的值?

QML/Javascript 中的 QVector