Seite 1 von 2

QTreeView performance

Verfasst: 10. Januar 2008 10:45
von OregonGhost
Moin,
ich schreibe zurzeit eine Anwendung, die unter anderem eine Art Echtzeitlog enthält. Es kommen eine große Menge Ereignisse (naja, bis zu 80 pro Sekunde) herein und ich möchte sie in einem TreeView darstellen. Da Sequenzen von Befehlen nicht immer mehrere Zeilen belegen sollen, werden sie zu einem Eintrag zusammen gefasst, der die einzelnen Bestandteile als Kinder bekommt (deshalb benötige ich einen TreeView). Die Performance dabei ist jedoch grottig, auch ohne dass ich automatisch zum letzten Eintrag scrolle. Mit automatischem Scrollen ist es eine Katastrophe.
Ich habe verschiedene Dinge probiert, um das Problem einzugrenzen. Festzustellen war, dass mit nicht sichtbarem TreeView die Performance sehr gut ist, es liegt also nicht wirklich an dem Code, der die Items generiert bzw. zusammenfasst.
Bei einem kleinen TreeView ist die Performance gut, jedoch eigentlich immer noch viel zu schlecht. Wenn das TreeView hingegen auf Vollbild gesetzt wird, belegt die Anwendung 50% CPU-Last (Dual Core, also quasi 100% auf einem Kern).
setUniformRowHeights(true) beschleunigt das ganze, aber es ist immer noch sehr langsam. Auch ein setUpdatesEnabled vor und nach dem Einfügen bringt nur minimale Beschleunigung.

Was kann man da tun?

Nachtrag: Ich habe testweise einen QTableView eingesetzt, der von einigen als die Lösung aller QTreeView-Performance-Probleme angepriesen wurde. Aber auch damit ist die Performance sehr schlecht.

Verfasst: 10. Januar 2008 12:30
von upsala
Alternativ noch eine QListView verwenden/testen, die hat einen 'Batch-Mode', event. ist dieser schneller.

Verfasst: 10. Januar 2008 15:53
von OregonGhost
QListView scheint zwar schneller zu gehen, zeigt aber auch wesentlich weniger Informationen an. Außerdem flackert es wie verrückt, witzigerweise nur, wenn ich nicht nach jedem Eintrag zum Ende scrolle.

Ich habe jetzt mal QTreeView etwas weiter getestet. Ein erheblicher Teil der Rechenzeit geht beim Zeichnen drauf, insbesondere das Zeichnen des Textes. Aber auch wenn man das Zeichnen weitestgehend weglässt (QItemDelegate mit leerer paint()-Methode), ist die Performance nicht zufriedenstellend. Den Großteil der Zeit verbringt das Programm in viewPortEvent, paintEvent und drawRow vom QTreeView. Erstaunlich viel Zeit wird auch in new und delete verbracht, die vom QTreeView aufgerufen werden. Und schließlich belegt auch QRect::x() einen sehr hohen Platz, weil es innerhalb von etwa 20 Sekunden Testzeit über zehn Millionen mal aufgerufen wird. Wie man's auch dreht und wendet, das ist alles nicht sehr ermutigend.

Ich habe testweise jetzt noch eingebaut, dass es maximal alle 500ms ein Update gibt - selbst damit ist die Anwendung noch recht träge und es sieht auch nicht besonders gut aus.

Noch jemand Tipps?

Edit: Smileys für diesen Beitrag deaktiviert.

Verfasst: 10. Januar 2008 16:33
von Christian81
Mal wieder das Problem von Sinn und Unsinn irgendwelcher Anforderungen... 80 Einträge/Sekunde! das kann eh keiner lesen.
Also sammeln und auf einmal reinschreiben, am besten mit updatesEnabled(false)

Verfasst: 10. Januar 2008 16:43
von OregonGhost
Nun, es handelt sich um ein Log-Programm für ein Bussystem, das 40 Telegramme pro Sekunde zzgl. 40 Antworten pro Sekunde zulässt. Wenn ich die Einträge sammle und z.B. alle 500ms eintrage, ist die Programmbedienung immer noch einigermaßen zäh (die Performance ist immer noch viel zu schlecht) und man bekommt nicht mehr wirklich einen Eindruck von dem, was auf dem Bus passiert. Das vorhandene Log-Tool, das sonst von reichlich Unzulänglichkeiten geplagt ist, kommt jedoch mit dieser Menge an Daten ohne Probleme zurecht und kann jeden Eintrag einzeln ans Ende der Liste eintragen und runterscrollen und benötigt dafür weniger als 10% CPU-Zeit. Dir mag es unsinnig erscheinen, so viele Einträge in hoher Geschwindigkeit anzuzeigen, aber da dieses Tool während der Entwicklung von Hardware verwendet wird, ist es sehr praktisch, die Abstände zwischen den Telegrammen auch während der Laufzeit optisch erfassen zu können. Man kann so auch von etwas weiter weg auf einen Blick sehen, wieviele Telegramme gerade reinkommen und in welchen Abständen. Wenn ich die Telegramme sammle, sieht man allenfalls, dass neue da sind, aber nicht wie viele und wie weit sie auseinander liegen.

Aber das spielt eigentlich auch keine Rolle. Es kann doch nicht sein, dass man das mit Windows Forms, Delphi, MFC oder nackter Win32-API ohne Probleme hinbekommt und mit Qt nicht. Es ist schließlich teuer genug.

Verfasst: 10. Januar 2008 16:49
von Christian81
Qt ist nichts für Echtzeitanwendungen. Wenn man sich schon allein den Aufbau des von Dir benutzten QTreeView ansieht (Stichwort MVA) ist es eigentlich ersichtlich dass man damit nichts in Echtzeit hinbekommen kann.
Und da Du ne kommerzielle Version hast, frag einfach TT. Dafür bezahlst Du schliesslich... und sind 1000¤ wirklich so viel?

Verfasst: 10. Januar 2008 17:01
von OregonGhost
Ich verstehe aber nicht, warum das so ist. Etwas MVA-ähnliches muss man auch in den anderen Systemen verwenden, um das zu erreichen, was ich vorhabe (einfach immer alle Daten an Windows weiterzureichen wird auch sehr langsam), und auch die anderen Systeme sind nicht speziell auf hohe Leistung ausgerichtet oder auf Echtzeit (vom OS mal ganz abgesehen). Und schließlich heißt es doch extra, dass man für hohe Performance QAbstractItemModel ableiten soll, und an der Leistung des Models selbst hängt es ja auch nicht.

Aber du hast recht, ich kann mich ja mal an Trolltech wenden. Mich wundert nur, dass die von mir erwartete Leistung so eine hohe Anforderung zu sein scheint.

Verfasst: 10. Januar 2008 17:48
von Christian81
Naja - es sind eben 80Hz. Diese ganze Model-Geschichte ist zwar sehr schön um Daten auf alle möglichen Arten darzustellen, aber diese Vielfalt an Möglichkeiten macht es eben nicht gerade schnell. Dann noch alles rendern (wieder alles frei definierbar) usw.
Ggf. machst Du auch noch ein paar Fehler in deinen Darstellungsroutinen - aber dazu kann man ohne Code nichts sagen.

Verfasst: 10. Januar 2008 18:05
von OregonGhost
Wie gesagt, habe lange gesucht. Wenn ich Qt das komplette Zeichnen übernehmen lasse, ist das der größte Rechenzeitfresser. Wenn ich das Zeichnen über den QItemDelegate weitestgehend deaktiviere, ist das bissel Zeichnen, was noch übrig ist, der große Fresser (also das Zeichnen der Scrollbar und des Hintergrundes der Items), wenn auch deutlich schneller. Ich arbeite jetzt erstmal so weiter, vielleicht probiere ich mal, QTreeView abzuleiten und gegebenenfalls besser an meine Bedürfnisse anzupassen.

Verfasst: 28. März 2008 00:16
von Neno
Ich bin auf dieses forum gestossen, als ich mal wieder stunden damit verbracht hatte meinen TreeView schnell zu machen und völlig frustriert Google nach QtreeView und Performance gefragt hatte. Ich versuche das seit langem und habe auch bereits eigene lösungen entwickelt. Das zur zeit von den Trollen ausgelieferte zeug taugt eigentlich nur dazu die API zu auf sinnhaftigkeit zu testen. Reale anwendungen mit grossen datenmengen kann man damit nicht bauen. Solange die entwickler bei TT nicht begreifen, dass die benutzung von methoden wie QList::indexOf() oder QList::contains() lineare suchen sind, werden die user der API immer auf O(n**2) laufzeiten treffen. Jetzt aktuell habe ich QTreeView gehackt um endlich weiter zu kommen; was ich da gesehen habe ist bitter - das ding ist kaum zu retten. Ich habe den Trollen natürlich schon diverse mitteilungen gemacht, aber die werden das grundlegende problem so schnell nicht lösen können. Wie auch, wenn die sogar in ihre Qt Quarterly beispiele abdrucken wo Fibonacchi zahlen rekursiv berechnet werden!
Soweit so gut ... wenn jemand interesse an details meiner erfahrungen hatte: einfach fragen.

Verfasst: 28. März 2008 08:49
von PeterLustig
Du kannst deine Lösungen zur Verbesserung der Performance der TreeViews ja gerne in die Tipps & Tricks-Ecke schreiben. Mich, und sicher auch andere, würde das sehr interessieren. Außerdem würden Leute, die zukünftig dieses Problem haben, dort eine Lösung finden können.

Verfasst: 28. März 2008 12:53
von nando
wie waere es mit einem eigenen thraed der die daten ins modell schreibt ?

Verfasst: 28. März 2008 13:35
von PeterLustig
Das wird aber nichts an der Prozessorleistung insgesamt ändern die verbraucht wird.

Verfasst: 28. März 2008 13:51
von Neno
Die daten im model sind nicht das problem. In einer variante verwende ich bereits einen thread, der die daten aus der db lädt, in einder anderen werden die daten erst geladen, nachdem der view sie angezeigt hat. Dazu liefert das model bei ersten aufruf leerfelder, beschafft dann die daten und lässt sie dann nochmal anzeigen. Das eigentliche problem ist, dem view die baumstruktur beizubringen. Dazu muss der nämlich eine interne struktur aufbauen, die für jede zeile mindestens einen eintrag in einem vector hat. Und für jeden offen knoten erzeugt der view auch noch einen richtig teuren QPersistentModelIndex (unnötigerweise). Ich verwende natürlich keinesfalls die item-basierten lösungen, die sind absolut unbrauchbar. Das problem ist schon der view selbst. Mein model schafft ohne schwierigkeiten 100.000 items in 0.5 s - wenn man den view damit konfrontiert, kann man sich dessen arbeit im taskmanager anschauen. Dann weiss man wenigstens, dass er noch läuft.

QTreeView performance

Verfasst: 28. März 2008 14:24
von khrl01
Ich denke ein QTreeView ist nicht die richtige Wahl für ein
"EchtzeitControl". Dazu müssen bei der Darstellung zuviele
Randbedingungen und Abhängigkeiten mit berücksichtigt werden.
(Parent/Child - Visible/Invisible - Collapsed/Expanded)
Selbst Applikationen die nativ unter Win32 erstellt sind,
zeigen bei TreeViews ab einer gewissen Anzahl von Einträgen,
massive Performance-Einbrüche!
Dort hilft man sich damit, dass nur die Bereiche die wirklich
sichtbar sind, aktualisiert werden (der rest wird dann bei
Bedarf aktualisiert).
Wenn der TreeView aber dazu benutzt werden soll, auch einen
Empfangsindikator darzustellen, hilft das obige Verfahren
gar nichts.
Ich würde an Deiner Stelle ggf. zwei Dinge in Erwägung ziehen:
(1) : Trenne die Aufgabe von Visualisierung und Empfangsindikator.
D.h. implementiere ein weiteres Control, mit dem Du den
Entwickler die Möglichkeit gibst, den Empfang mit zu verfolgen.

(2) : Wenn Du nur wenig der Funktionalität eines QTreeViews benötigst,
schreibe ein eigenes Control (ggf. basierend auf QAbstractItemView)
selbst.
Du könntest dabei ja etwas faken ;-)
D.h. Du nimmst ein einfaches performates Control, und wenn der
Anwender sich den Tree ansehen möchte, blende ein im Hintergrund
aufgebautes Control ein. Zu diesem Zeitpunkt möchte der User ja
Details sehen und nicht mehr den Fortschritt...


Grüsse
karl-heinz
www.techdrivers.de