Я пытаюсь вернуть 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 делится своими данными ( http://qt-project.org/doc/qt-5.0/qtcore/implicit-sharing.html#implicitly-shared ) так что ты не делаешь ничего плохого.
Вы выделяете новый 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);
}
С помощью этого кода результирующий вектор выделяется только один раз.
Поскольку вы утверждаете, что «возврат» занимает больше всего времени, проблема может быть не в самой функции, а на сайте, где используется возвращаемое значение.
Увы, вот где ваш код тратит время:
В распределении QVector
каждый раз, когда вызывается среднее значение. Предположительно, он вызывается неоднократно, поэтому нет необходимости каждый раз выделять новый вектор.
В QVector::operator[]
, У него немного больше накладных расходов, чем у простого доступа к массиву, потому что isDetached
звонок сделан на каждый звонок operator[]
,
В 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);
...
}
QVector
является общий класс. Копирование — это постоянная операция, которая должна быть очень быстрой.