Russian Qt Forum

Qt => Общие вопросы => Тема начата: qtkoder777 от Январь 18, 2018, 17:52



Название: Большие тормоза из-за QVector?
Отправлено: qtkoder777 от Январь 18, 2018, 17:52
В функции производится около 4000 вставок в конец QVector. Реализуется линейный алгоритм построения полигональной сетки.
Даже если увеличим число операций в 100 раз, не понимаю как 400000 операций могут выполняться 1.5 секунды на проце 2.5 ГГц.
Может ли это быть из-за выделения памяти при каждой вставке в конец?
Раньше как то у меня были тормоза из-за структур вида QVector<QVector<T>>, что в принципе имеет место и здесь. С чем это связано?

Вставляются такие классы:

Код
C++ (Qt)
class Vertex
{
public:
QVector<double> coord;
QVector<double> normal;
QVector<double> texCoord;
};
 
class Edge
{
public:
int p1;
int p2;
int mid;
int e1;
int e2;
};


Название: Re: Большие тормоза из-за QVector?
Отправлено: Apktyc от Январь 18, 2018, 18:06
Если количество вставок известно, QVector::reserve() (http://doc.qt.io/qt-5/qvector.html#reserve) вам в помощь.
А может присмотреться к списку?


Название: Re: Большие тормоза из-за QVector?
Отправлено: Old от Январь 18, 2018, 18:07
Может ли это быть из-за выделения памяти при каждой вставке в конец?
Именно из-за этого, множественные переаллокации и копирования.
Как правило, вы знаете сколько элементов нужно будет добавить в вектор, резервируйте место сразу.


Название: Re: Большие тормоза из-за QVector?
Отправлено: Old от Январь 18, 2018, 18:08
А может присмотреться к списку?
Вы имеете ввиду QList (там тоже самое), или честный X-связный список?


Название: Re: Большие тормоза из-за QVector?
Отправлено: Apktyc от Январь 18, 2018, 18:48
Вы имеете ввиду QList (там тоже самое), или честный X-связный список?
Прочел, задумался, узнал про QLinkedList, просветился.  ;D


Название: Re: Большие тормоза из-за QVector?
Отправлено: __Heaven__ от Январь 18, 2018, 19:02
Я бы ещё присмотрелся к map (или set)
при этом QVector<double> сменил бы на struct MyVector{double x, y, z}
а в Edge заменил бы тип p1 и p2 на map::const_iterator
для чего нужны mid, e1, e2 - не понял


Название: Re: Большие тормоза из-за QVector?
Отправлено: qtkoder777 от Январь 18, 2018, 21:55
Я бы ещё присмотрелся к map (или set)
при этом QVector<double> сменил бы на struct MyVector{double x, y, z}
а в Edge заменил бы тип p1 и p2 на map::const_iterator
для чего нужны mid, e1, e2 - не понял

Проводится рекурсивное разделение треугольников на 4 части. mid - номер точки середины ребра, e1, e2 - номера рёбер, получаемых при делении данного.
Надо избежать повторного добавления точек деления у соседних треугольников. Раньше это и было сделано с помощью QMap<начало ребра, конец ребра, середина>. Сначала грешил на map, так как это C*log(n) к сложности.
Переделал без map, а тормоза остались.


Название: Re: Большие тормоза из-за QVector?
Отправлено: Igors от Январь 19, 2018, 09:14
Надо избежать повторного добавления точек деления у соседних треугольников.
В этом есть смысл если есть фейсы (индексы) которых в приведенном коде не видно. Тогда структуры примерно такие
Код
C++ (Qt)
struct Triangle {
int m_index[3];  // индексы вертексов  
int m_edges[3];  // индексы ребер  
};
 
struct Edge {
 bool operator < ( const Edge & other ) const { return m_ver < other.m_ver; }
 
// data
 typedef std::pair<int> TPair;
 TPair m_ver;    // индексы вертексов ребра (first < second)
 TPair m_fac;    // индексы фейсов (Triangle) шарящих ребро (second = -1 если ребро открыто)
};
Ну и дальше все очевидно: проходитесь по исходным фейсам, строите контейнер ребер и вписываете их индексы в m_edges. Потребуется мапа о которой потом можно забыть. Потом добавляете новые вертексы (по числу ребер) и выдаете на гора новые фейсы и вертексы


Название: Re: Большие тормоза из-за QVector?
Отправлено: __Heaven__ от Январь 19, 2018, 12:33
Под linux можно ещё попробовать запустить профилирование в callgrind. Делается это через интерфейс qtcreator и результаты удобно выводится. Там и можно глянуть, что тормозит. Может это вообще не Vertex и QVector :)


Название: Re: Большие тормоза из-за QVector?
Отправлено: Racheengel от Январь 19, 2018, 15:47
QVector медленный, на винде раза в 2-3 медленней, чем std::vector.
Эта проблема известна (есть тикеты на Qt-Bugs), может быть, в 6-й версии починят.


Название: Re: Большие тормоза из-за QVector?
Отправлено: Igors от Январь 19, 2018, 16:48
Эта проблема известна (есть тикеты ...
Как быстро все "обрастает легендами" :) Я знаю эту задачу, поверьте, какие-то нюансы контейнеров здесь ну совершенно ни при чем. Нужно заниматься алгоритмом (а не синтаксическим сахарином)



Название: Re: Большие тормоза из-за QVector?
Отправлено: Авварон от Январь 19, 2018, 18:42
QVector медленный, на винде раза в 2-3 медленней, чем std::vector.
Эта проблема известна (есть тикеты на Qt-Bugs), может быть, в 6-й версии починят.

Ссылку в студию


Название: Re: Большие тормоза из-за QVector?
Отправлено: Racheengel от Январь 23, 2018, 11:46
https://bugreports.qt.io/browse/QTBUG-44683
https://bugreports.qt.io/browse/QTBUG-44566
https://bugreports.qt.io/browse/QTBUG-49250

Проблема реальная, сами с таким столкнулись. После того, как сменили QVector на std::vector - сильно возрос performance. Конечно, алгоритмических проблем это не решит, но в любом случае не повредит.


Название: Re: Большие тормоза из-за QVector?
Отправлено: Авварон от Январь 23, 2018, 15:17
Ну половина тикетов из-за неумения пользоваться qvector, например неконстантный operator[] вместо at() или const версии (он детачит контейнер).
А так, надеюсь в qt6 закопают cow и не потому что использовать сложнее, а потому что qvector не умеет хранить move-only типы.


Название: Re: Большие тормоза из-за QVector?
Отправлено: qtkoder777 от Январь 29, 2018, 11:36
Эта проблема известна (есть тикеты ...
Как быстро все "обрастает легендами" :) Я знаю эту задачу, поверьте, какие-то нюансы контейнеров здесь ну совершенно ни при чем. Нужно заниматься алгоритмом (а не синтаксическим сахарином)



Вектор реально тормозит. Избавление от push-ей сократило время раз в 15.
Чисто переписать полученые 2000-3000 треугольников в массив для рисования занимает 30 мс. Вот и что с этим делать?


Название: Re: Большие тормоза из-за QVector?
Отправлено: Old от Январь 29, 2018, 11:56
Вектор реально тормозит.
Скорее это называется "неправильное использование вектора".
Если вы знаете, как устроен вектор, то никогда не будете расширять его по-элементно. Он для этого не предназначен, несмотря на то, что такая возможность есть.


Название: Re: Большие тормоза из-за QVector?
Отправлено: Авварон от Январь 29, 2018, 14:17
Эта проблема известна (есть тикеты ...
Как быстро все "обрастает легендами" :) Я знаю эту задачу, поверьте, какие-то нюансы контейнеров здесь ну совершенно ни при чем. Нужно заниматься алгоритмом (а не синтаксическим сахарином)



Вектор реально тормозит. Избавление от push-ей сократило время раз в 15.
Чисто переписать полученые 2000-3000 треугольников в массив для рисования занимает 30 мс. Вот и что с этим делать?

Ну и опять же проблема не в тормозах, а в функционале - отсутствие range insert.
Его кстати ничего не мешает добавить


Название: Re: Большие тормоза из-за QVector?
Отправлено: Igors от Январь 29, 2018, 14:41
Вектор реально тормозит. Избавление от push-ей сократило время раз в 15.
Чисто переписать полученые 2000-3000 треугольников в массив для рисования занимает 30 мс. Вот и что с этим делать?
Там на каждой итерации всего 2 resize (один для вертексов, др для фейсов). Наведите порядок в структурах и алгоритме, все за полчаса ляжет. Схемка выше, если что неясно - спрашивайте, попробую помочь


Название: Re: Большие тормоза из-за QVector?
Отправлено: qtkoder777 от Январь 30, 2018, 14:41
Надо избежать повторного добавления точек деления у соседних треугольников.
В этом есть смысл если есть фейсы (индексы) которых в приведенном коде не видно. Тогда структуры примерно такие
Код
C++ (Qt)
struct Triangle {
int m_index[3];  // индексы вертексов  
int m_iEdges[3];  // индексы ребер  
};
 
struct Edge {
 bool operator < ( const Edge & other ) const { return m_ver < other.m_ver; }
 
// data
 typedef std::pair<int> TPair;
 TPair m_ver;    // индексы вертексов ребра (first < second)
 TPair m_fac;    // индексы фейсов (Triangle) шарящих ребро (second = -1 если ребро открыто)
};
Ну и дальше все очевидно: проходитесь по исходным фейсам, строите контейнер ребер и вписываете их индексы в m_iEdges. Потребуется мапа о которой потом можно забыть. Потом добавляете новые вертексы (по числу ребер) и выдаете на гора новые фейсы и вертексы

Делаю несколько иначе, зато мапы нет. Функция IsVisible замедляет, но не радикально. Если дробить все фейсы без проверки тоже медленно. Все вектора зарезервированы заранее. Что в этом алгоритме не так?


Код
C++ (Qt)
                       class Vertex
{
public:
Vector3D coord;
Vector3D normal;
Vector3D texCoord;
};
 
class Edge
{
public:
int p1; //начало в правильном порядке обхода
int p2; //конец
int mid; //середина, или -1 если пока не делили
int e1;  //ребра полученные при делении
int e2;  //ребра полученные при делении
                                       Edge(int _p1 = -1, int _p2 = -1, int _mid = -1, int _e1m = -1, int _e2m = -1);
};
 
class Triangle
{
public:
Triangle(const Triangle &);
Triangle(Mesh * m = 0, int _p1 = -1, int _p2 = -1, int _p3 = -1, int _e12 = -1, int _e23 = -1, int _e31 = -1);
virtual ~Triangle();
int p1, p2, p3; //индексы вершин
int e12, e23, e31; //индексы рёбер
Vector3D  P1() const; //вершина 1
Vector3D  P2() const; //вершина 2
Vector3D P3() const; //вершина 3
                               bool IsVisible(float frustum[6][4], double r, Vector3D eye);
};
 
void Mesh::Subdivine(int polygons, const QVector<Triangle> & triangles, float frustum[6][4])
{
 
int iTr = triangles.size();
 
int vertex = vertices.size();//начальное число вертексов
 
 
for(int i=0;i<iTr;++i)
{
newTriangles[i] = triangles[i]; //записываем начальные фейсы
}
 
QVector<Triangle> * pNewTriangles = &newTriangles, * pNewTriangles1 = &newTriangles1;
QVector<Edge> * pEdges = &iEdges, * pNewEdges = &newEdges;
 
int iEdge; //номер добавляемого ребра
 
while(iTr < polygons) //мы хотим polygons фейсов
{
int iVisible = 0;
for(int in = 0; in < iTr; ++in)
{
Triangle t = (*pNewTriangles)[in];
if(t.IsVisible(frustum, eye, R)) //стоит ли дяльше разбивать этот треугольник
{
visibleTriangles[iVisible] = t;
++iVisible;
}
}
 
if(iVisible == 0) break;
 
iTr = 0;
iEdge=0;
 
                               //новых ребер будет не больше 9*iVisible
for(int iv = 0; iv < 9*iVisible; ++iv)
{
(*pNewEdges)[iv] = Edge();
}
 
for(int iv = 0; iv < iVisible; ++iv)
{
Triangle t = visibleTriangles[iv];
Vertex p12, p13, p23;
                                       //новые вершины
int i12, i13, i23,
e1m12,e2m12,e1m31,e3m31,e2m23,e3m23; //новые ребра
 
                                       //выбираем индексы поделенного ребра или делим ребро
if((*pEdges)[t.e12].mid >=0)
{
i12 = (*pEdges)[t.e12].mid; //точка середины
 
if((*pNewEdges)[(*pEdges)[t.e12].e1m].p1 == t.p1)
{
e1m12 = (*pEdges)[t.e12].e1m; //ребра нового фейса
e2m12 = (*pEdges)[t.e12].e2m;
}
else
{
e1m12 = (*pEdges)[t.e12].e2m;
e2m12 = (*pEdges)[t.e12].e1m;
}
}
else
{
p12.coord = (t.P1() + (t.P2() - t.P1())/2.).Normalized()*R; //проецируем середину на сферу
vertices[vertex] = p12; //новый вертекс
i12 = vertex;
++vertex;
 
                                               //новые 2 ребра
(*pEdges)[t.e12].mid = i12;
Edge e1(t.p1, i12), e2(t.p2, i12);
(*pNewEdges)[iEdge] = e1;
e1m12 = iEdge;
++iEdge;
(*pEdges)[t.e12].e1m = e1m12;
(*pNewEdges)[iEdge] = e2;
e2m12 = iEdge;
++iEdge;
(*pEdges)[t.e12].e2m = e2m12;
}
 
 
                                       //
                                       //тоже самое для оставшихся ребер
                                       //
 
                                       //добавляем 4 фейса
(*pNewTriangles1)[iTr] = Triangle(this, t.p1, i12, i13, e1m12, e1312, e1m31);
(*pNewTriangles1)[iTr+1] = Triangle(this, t.p2, i23, i12, e2m23, e1223, e2m12);
(*pNewTriangles1)[iTr+2] = Triangle(this, t.p3, i13, i23, e3m31, e2313, e3m23);
(*pNewTriangles1)[iTr+3] = Triangle(this, i12, i23, i13, e1223, e2313, e1312);
iTr += 4;
}
 
nEdges = iEdge;
 
qSwap(pNewTriangles, pNewTriangles1);
qSwap(pNewEdges, pEdges);
}
 
                       //переписываем результат
edges = *pEdges;
newTriangles = *pNewTriangles;
nTriangles = iTr;
nVertices = vertices.size();
}


Название: Re: Большие тормоза из-за QVector?
Отправлено: Igors от Январь 31, 2018, 12:31
Делаю несколько иначе, зато мапы нет. Функция IsVisible замедляет, но не радикально. Если дробить все фейсы без проверки тоже медленно. Все вектора зарезервированы заранее. Что в этом алгоритме не так?
Ну неплохо бы сформулировать задачу, хотя бы кратко, чтобы не приходилось о ней догадываться по фрагментам кода :)  Насколько я понял, у Вас бессмертная "отсечка по фрустуму", т.е. хотите отбросить полигоны что камера не видит напрямую. Если так, то нужно выжать макс скорости - и зачем чего-то делить, создавать новые вертексы?  Просто если тр-к видим - оставляем его, иначе удаляем, всех делов на 5 минут. Не нужно даже создавать второй контейнер, просто перемещаем видимые в начало и выдаем их число для отрисовки. Если же по каким-то причинам (мне неизвестным) все-таки нужно рэзать, то:

Здесь точно порылась собака
Код
C++ (Qt)
bool IsVisible(float frustum[6][4], double r, Vector3D eye);
 
Если есть frustum, то eye избыточен, а что делает r (и что за "проецирование на сферу") - не понял. Фрустум делит каждое ребро создавая на нем 0, 1 или 2 новых вертекса. Поэтому Edge лучше нарисовать так
Код
C++ (Qt)
class Edge {
public:
   int p1; // начало в правильном порядке обхода
   int p2; // конец
   int p3; // новое начало если ребро было обрезано фрустумом (p3 == p1 если нет, -1 если ребро невидимо)
   int p4; // новый конец... (p4 == p2 если нет, -1 если невидимо)
 
   bool operator < ( const Edge & other ) const
   {
      if (qMin(p1, p2) < qMin(other.p1, other.p2)) return triue;
      if (qMin(p1, p2) > qMin(other.p1, other.p2)) return false;
 
      return qMax(p1, p2) < qMax(other.p1, other.p2);
   }
  ...
};
На этапе предрасчета все равно надо построить все ребра и вписать их индексы в фейсы как говорилось выше. Отсечка под конкретный фрустум сводится к проходу по всем ребрам: если ребро пересекает фрустум, то создаем новые вертексы (1 или 2) и вписываем их индексы в p3 и p4. После этого проходим по всем тр-кам и строим новые - у нас для этого все есть.


Название: Re: Большие тормоза из-за QVector?
Отправлено: qtkoder777 от Январь 31, 2018, 13:08
Делаю несколько иначе, зато мапы нет. Функция IsVisible замедляет, но не радикально. Если дробить все фейсы без проверки тоже медленно. Все вектора зарезервированы заранее. Что в этом алгоритме не так?
Ну неплохо бы сформулировать задачу, хотя бы кратко, чтобы не приходилось о ней догадываться по фрагментам кода :)  Насколько я понял, у Вас бессмертная "отсечка по фрустуму", т.е. хотите отбросить полигоны что камера не видит напрямую. Если так, то нужно выжать макс скорости - и зачем чего-то делить, создавать новые вертексы?  

Задача нарисовать планету. Сначала строится меш для шара, разбивая икосаэдр, потом к вершинам применяется карта высот из текстуры. Получается планета с горами.
Отсекаю по фрустуму и по касательному конусу с помощью ограничивающих сфер.
(https://image.ibb.co/fcUNb6/img.jpg) (https://imgbb.com/)
Вот зачем оператор сравнения рёбер так и не понял.


Название: Re: Большие тормоза из-за QVector?
Отправлено: Igors от Январь 31, 2018, 14:44
Отсекаю по фрустуму и по касательному конусу с помощью ограничивающих сфер.
На всякий случай напомню что в обоих случаях если даже оба вертекса ребра невидимы - это еще ничего не значит, ребро может пересекать как фрустум, так и конус. Поэтому проверка на конус выглядит избыточной. Также есть мерзкий частный случай - ни одно ребро тр-ка не пересекает фрустум, но часть тр-ка все-таки видима (ну как бы тр-к насажен на угол фрустума)
Вот зачем оператор сравнения рёбер так и не понял.
В любом случае если резать - все начинается с построения ребер, напр так
Код
C++ (Qt)
QVector<Edge> BuildEdges( QVector<Triangle> & tri )
{
QVector<Edge> dst;
QMap<Edge, int> eMap;
 
for (int i = 0; i < tri.size(); ++i) {
 Triangle & t = tri[i];
 for (int j = 0; j < 3; ++j) {
   Edge edge = t.GetEdge(j);
   int & val = eMap[edge];
   if (!val) {    // ребро еще не добавлено в мапу
     dst.push_back(edge);
     val = edge.size();     // сохраняем в мапе индекс ребра + 1
   }
   t.SetEdgeIndex(j, val - 1);  // вписываем индекс ребра в фейс
 }
}
return dst;
}
Лучше хранить в фейсе p[3] и e[3]

[off]Да, а что ж такая тишина? Ведь совсем недавно подробности QVector обсуждались весьма охотно (хотя они здесь как корове седло). А вот содержательная часть почему-то никакого интереса не вызыват  :)[/off]


Название: Re: Большие тормоза из-за QVector?
Отправлено: qtkoder777 от Январь 31, 2018, 15:13
На всякий случай напомню что в обоих случаях если даже оба вертекса ребра невидимы - это еще ничего не значит, ребро может пересекать как фрустум, так и конус. Поэтому проверка на конус выглядит избыточной. Также есть мерзкий частный случай - ни одно ребро тр-ка не пересекает фрустум, но часть тр-ка все-таки видима (ну как бы тр-к насажен на угол фрустума)
Проверяется попадает ли во фрустум и конус описанный вокруг треугольника шар. А в этот шар попадут и все треугольники, на которые мы его можем раздробить. Будут некоторые лишние фейсы - по краям фрустума, ну и пусть рисуются. Проверка на конус предотвращает расчет треугольников-антиподов.

Сейчас думаю что дело в Vector3D.
Каждый раз когда создаётся временный Vector3D, создаются же и все функции-члены, а их много.
То есть для производительности придётся переписывать в процедурном стиле?


Название: Re: Большие тормоза из-за QVector?
Отправлено: Igors от Февраль 01, 2018, 05:41
Ага, ну вроде так: Вы хотите делать триангуляцию только для тех фейсов что прошли тест Visible. Причем хотите делать 2 триангуляции за 1 проход (т.е. фактически тр-ки делятся на 4 * 4 = 16 новых). Все ли я верно понял?

Сейчас думаю что дело в Vector3D.
Каждый раз когда создаётся временный Vector3D, создаются же и все функции-члены, а их много.
То есть для производительности придётся переписывать в процедурном стиле?
Нет. Инициализируются только данные что этого требуют (здесь конкретно 3 флота x, y, z) - и все. А сколько ф-ций членов (хоть тыщи) никак на производительность не влияет.


Название: Re: Большие тормоза из-за QVector?
Отправлено: Авварон от Февраль 01, 2018, 20:46
Господи, ну есть же десятки профилировщиков.


Название: Re: Большие тормоза из-за QVector?
Отправлено: Igors от Февраль 02, 2018, 12:38
Забегая чуть вперед: можно хорошо выжать, даже подключить все ядра, но все-таки для real-time нужна реализация на GPU, Но там такой геморрой  :'(