2014-01-26 5 views
2

Im пытается вернуть QVector из функции, которая должна вычислять скользящую среднюю. Мой вопрос заключается в том, как сделать функцию более эффективной. Математика в порядке, я больше задаюсь вопросом, не сделаю ли я что-то неправильно в возвращении 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 от установки значения по умолчанию. Я пробовал то же самое с изменением размера, которое намного медленнее. Есть ли у вас предложения, как это можно оптимизировать?

С уважением

+0

В вашем коде нет ничего значительно медленного (так как QVector выполняет copy-on-write). Если код слишком медленный для вас, используйте профилировщик для определения медленных линий. –

+1

@juanchopanza QVector, поскольку большинство объектов Qt немного особенны и не ведут себя как ценность, поэтому я думаю, что это не дубликат. – pmr

+0

@PavelStrakhov Я сделал профилирование. Возвращение кажется самой медленной частью. – user2058490

ответ

2

QVector является a shared class. Копирование является постоянной операцией и должно быть очень быстрым.

5

Реализация QVector делится своими данными (http://qt-project.org/doc/qt-5.0/qtcore/implicit-sharing.html#implicitly-shared), поэтому вы ничего не делаете неправильно.

+0

да, но если написать код без вызова функции в 3 раза быстрее. Я что-то не так с распределением? – user2058490

+0

Я не вижу ничего плохого. Я предполагаю, что ваш код без отдельной функции полностью одинаков - тогда это странно. Можете ли вы поэкспериментировать, что произойдет, если вместо того, чтобы возвращать вектор, вы передаете его как неконстантную ссылку и заполните функцию? –

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

С помощью этого кода, в результате вектор выделяется только один раз.

+0

Кроме того, если исходный вектор больше не нужен, вычисление может быть выполнено на месте по одному вектору. –

+0

@ Kurt Pattyn Я думаю, что это не имеет значения. Я пробовал обе версии, нет существенной разницы в их потреблении времени. – user2058490

3

Поскольку вы утверждаете, что «возврат» занимает больше времени, проблема может быть не в самой функции, а на том месте, где используется возвращаемое значение.

Увы, вот где ваш код время отходы:

  1. В выделении QVector каждый раз, когда средняя вызывается. Предположительно, это называется многократно, поэтому нет необходимости каждый раз выделять новый вектор.

  2. QVector::operator[]. У этого есть немного больше накладных расходов, чем простой доступ к массиву, потому что есть этот надоедливый вызов isDetached, сделанный при каждом вызове operator[].

  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); 
    ... 
    } 
Смежные вопросы