返回 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?