virtuelles ListView mit LVIF_DI_SETITEM optimieren
-
Hallo zusammen,
ich habe eine virtuelles ListView und möchte diese optimieren, da sie mir eine recht hohe Prozessorlast beschert.
Daher wollte ich bei den Spalten, die sich nicht ändern (bzw. nur selten) den Cache via
item.mask |= LVIF_DI_SETITEM;
aktivieren.Nur leider funktioniert das nicht... Warum? Jemand eine Idee?
Edit: Um das noch etwas zu erläutern:
Ich habe eine MDI-Anwendung mit einer ListView. Nachträglich habe ich dieses ListView dann als virtuelles ListView abgeändert, indem ich das
LVS_OWNERDATA
als Style inPreCreateWindow()
hinzugefügt habe und einenLVN_GETDISPINFO
-Handler (OnLvnGetdispinfo
) hinzufügen lassen habe. Statt die Liste mitSetItem()
zu befüllen teile ich der Liste nur die Anzahl der Elemente mit (SetItemCount()
).Das funktioniert auch einwandfrei: Es wird
OnLvnGetdispinfo(...)
aufgerufen und anhand der übergebenenNMLVDISPINFO
-Struktur erkenne ich welche Zeile (.item.iIteem
) und Spalte (.item.iSubItem
) angefragt wurde. MittelsStringCchCopy
kopiere ich dann den Text aus meinem Datenbestand (CString
) in die Struktur.Da meine Liste aufgrund neuer Daten oft aktualisiert wird und dies immer nur eine Spalte betrifft, wollte ich erreichen, dass die fünf "statischen" Spalten nicht ständig neu angefordert werden. Dafür habe ich dann
item.mask |= LVIF_DI_SETITEM
gesetzt.Das soll bewirken, dass der Wert gespeichert wird und nicht mehr via
LVN_GETDISPINFO
angefordert wird. Doch es hat leider keine Auswirkung.void CMyView::OnLvnGetdispinfo(NMHDR *pNMHDR, LRESULT *pResult) {NMLVDISPINFO* dispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR); if (dispInfo->item.iItem < 0 || static_cast<size_t>(dispInfo->item.iItem) >= m_dataPoints.size()) { return; // requesting invalid item } if (dispInfo->item.mask & LVIF_TEXT == LVIF_TEXT) { const DataPoint* dp = m_dataPoints[dispInfo->item.iItem]; switch (dispInfo->item.iSubItem) { case Colums::TagName: StringCchCopy(dispInfo->item.pszText, dispInfo->item.cchTextMax, dp->tagName); dispInfo->item.mask |= LVIF_DI_SETITEM; // merke Dir den Wert und frage nicht nochmal nach! break; //... } } *pResult = 0; }
-
Wo ist nun Dein Problem?
Es werden doch sowieso nur die Daten angefordert, die angezeigt werden. Mehr Effektivität erreichst Du damit nicht. Bei den paar Zeilen und Items, die in einer Ansicht sichtbar sind, ist das Problem wohl minimal.Oder habe ich was falsch verstanden.
BTW: Ich verwende weitaus lieber die internen Cache Hints um das Verhalten meines List-Controls zu optimieren.
-
Mein Problem ist, dass ich nur durch das Aktualisieren des ListView hier auf meinen Laptop (Pentium M 1x2GHz) eine dauerhafte Prozessorauslast von über 10% erhalte, da meine ListView relativ häufig aktualisiert wird.
Daher wollte ich, dass nur die Items, die sich auch ändern aktualisiert werden.
Mit dem setzten von
LVIF_DI_SETITEM
soll das auch gehen. Tut es aber bei mir leider nicht...Könntest Du mir Deine Technik näher erläutern?
-
Ich glaube, dass kann nicht für den Fall gelten, wenn Du alle Daten verwaltetst, wie es eben bei LVS_OWNERDATA ist.
IMHO geht dieser Parameter nur dann wenn für jede Zeile/Spalte explizit CALLBACK definiert ist.In Deinem Fall sagst Du ja: ALle Daten verwalte ich!
Ich habe eine Datenbank oft hinter den Daten. Ich behandle LVN_ODCACHEHINT
http://msdn.microsoft.com/en-us/library/bb774855(VS.85).aspxIch weiß was das Control anzeigen will und lese alle Daten auf einen Schlag, wenn es geht.
Ob es in Deinem Fall hilft weiß ich nicht.
Ich bin mir aber ziemlich sicher, dass es nicht die paar Nachrichten sind.
Lass mal die statischen Spalten weg und Du wirst sehen, dass die Performance woanders flöten geht.
-
Ich habe im Debug-Mode nochmal getestet:
Ohne den eigentlichen Datenaustausch und einer zyklischen Benachrichtigung des ListViews alle 300ms komme ich auf 5-8% Last.
Wenn ich nur eine Spalte einfüge sinkt die Last auf 2-4%. Also schon ein Unterschied.
Die Nachrichten selber sind wohl nicht das Problem, aber jedesmal müssen die Strings kopiert werden bzw. erst mit Format() aus integer geformt werden.
-
Format verwendet Allokationen.
Verwende direkt sprintf_s in den Buffer. Das spart auch das kopieren.