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 in PreCreateWindow() hinzugefügt habe und einen LVN_GETDISPINFO -Handler ( OnLvnGetdispinfo ) hinzufügen lassen habe. Statt die Liste mit SetItem() 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 übergebenen NMLVDISPINFO -Struktur erkenne ich welche Zeile ( .item.iIteem ) und Spalte ( .item.iSubItem ) angefragt wurde. Mittels StringCchCopy 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;
    }
    

  • Mod

    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?


  • Mod

    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).aspx

    Ich 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.


  • Mod

    Format verwendet Allokationen.

    Verwende direkt sprintf_s in den Buffer. Das spart auch das kopieren.


Anmelden zum Antworten