The MASM Forum Archive 2004 to 2012

General Forums => The Workshop => Topic started by: Gunner on January 07, 2011, 02:26:29 AM

Title: How do I speed up this treeview insert?
Post by: Gunner on January 07, 2011, 02:26:29 AM
I use a Treeview in many of my apps and really like em...  But am having issues with speed on an app I am working on.. 
I open a csv file and parse each line into an array.
Then I parse each array member for field 3, and field 4, and the whole field of strings will be in the tvitem lparam for later parsing...
I can load a 2 meg file (about 6500 lines) in about 2 seconds by doing JUST the above...  The slowdown happens when I add the items to the tree..

I take field 3, if tree is empty, then add as root and field 4 as child..  If tree is not empty,
Compare all root text and if it is in tree then add field 4 as child....

This is no problem with 100 or so treeview items.. but with the 2 meg file it takes about 10 or so seconds..

How can I speed this up?  I just don't know what to search for on the board  :(

        mov     ebx, RecordInfo
       
        push    0
        push    0
        push    TVM_GETCOUNT
        push    hTVMainUser
        call    SendMessage
        test    eax, eax

        jnz     AddEm

AddFirstOne:
        mov     tvis.hParent, TVI_ROOT
        mov     tvis.hInsertAfter, TVI_SORT
        mov     tvis.item.imask, TVIF_TEXT or TVIF_STATE or TVIF_IMAGE or TVIF_SELECTEDIMAGE
        mov     tvis.item.iImage, 0
        mov     tvis.item.iSelectedImage, 0
        mov     tvis.item.state, TVIS_BOLD or TVIS_EXPANDED
        mov     tvis.item.stateMask, TVIS_BOLD or TVIS_EXPANDED
        mov     eax, (LogFields PTR [ebx]).szDate
        mov     tvis.item.pszText, eax     
        invoke  SendMessage, hTVMainUser, TVM_INSERTITEM, 0, addr tvis

        mov     tvis.item.imask, TVIF_TEXT or TVIF_PARAM or TVIF_IMAGE or TVIF_SELECTEDIMAGE
        mov     tvis.item.iImage, 1
        mov     tvis.item.iSelectedImage, 1
        mov     tvis.hParent, eax
        mov     eax, (LogFields PTR [ebx]).szIP
        mov     tvis.item.pszText,  eax
        mov     tvis.item.lParam, ebx
        invoke  SendMessage, hTVMainUser, TVM_INSERTITEM, 0, addr tvis
        ret

AddEm:
        ; Get root node handle
        push    0
        push    TVGN_ROOT
        push    TVM_GETNEXTITEM
        push    hTVMainUser
        call    SendMessage
        test    eax, eax
        jnz     @F
        ret
           
@@:
        mov     edi, eax
        invoke  memfill, addr RootText, sizeof RootText, NULL
        mov     tvi._mask, TVIF_TEXT
        mov     tvi.hItem, edi
        mov     tvi.cchTextMax, 50
        lea     ecx, RootText
        mov     tvi.pszText, ecx
        lea     eax, tvi
        push    eax
        push    0
        push    TVM_GETITEM
        push    hTVMainUser
        call    SendMessage     
        lea     eax, RootText
       
        invoke  Cmpi, eax, (LogFields PTR [ebx]).szDate
        .if eax == 0
            mov     tvis.item.imask, TVIF_TEXT or TVIF_PARAM or TVIF_IMAGE or TVIF_SELECTEDIMAGE
            mov     tvis.item.iImage, 1
            mov     tvis.item.iSelectedImage, 1
            mov     tvis.hParent, edi
            mov     eax, [ebx].LogFields.szIP
            mov     tvis.item.pszText, eax
            mov     tvis.item.lParam, ebx
            invoke  SendMessage, hTVMainUser, TVM_INSERTITEM, 0, addr tvis
            ret
        .endif

CheckNextRoot:
        push    edi
        push    TVGN_NEXT
        push    TVM_GETNEXTITEM
        push    hTVMainUser
        call    SendMessage
        mov     esi, eax
        test    eax, eax
        jz      AddFirstOne     
   
        invoke  memfill, addr RootText, sizeof RootText, NULL
        mov     tvi._mask, TVIF_TEXT
        mov     tvi.hItem, esi
        mov     tvi.cchTextMax, 50
        lea     ecx, RootText
        mov     tvi.pszText, ecx
        lea     eax, tvi
        push    eax
        push    0
        push    TVM_GETITEM
        push    hTVMainUser
        call    SendMessage     
        lea     eax, RootText
       
        invoke  Cmpi, eax, (LogFields PTR [ebx]).szDate
        .if eax == 0
            mov     tvis.item.imask, TVIF_TEXT or TVIF_PARAM or TVIF_IMAGE or TVIF_SELECTEDIMAGE
            mov     tvis.item.iImage, 1
            mov     tvis.item.iSelectedImage, 1
            mov     tvis.hParent, esi
            mov     eax, (LogFields PTR [ebx]).szIP
            mov     tvis.item.pszText, eax
            mov     tvis.item.lParam, ebx
            invoke  SendMessage, hTVMainUser, TVM_INSERTITEM, 0, addr tvis
            ret
        .endif
        mov     edi, esi
        jmp     CheckNextRoot
Title: Re: How do I speed up this treeview insert?
Post by: oex on January 07, 2011, 03:07:12 AM
Is treeview visible? If it is hide it.... Sorry if stupid answer :lol
Title: Re: How do I speed up this treeview insert?
Post by: dedndave on January 07, 2011, 03:27:36 AM
well - it could probably be sped up a little
but, i doubt that sending the messages is the big bottleneck
it is how the messages are handled that is time-consuming
you are showing us the wrong end of the stick
Title: Re: How do I speed up this treeview insert?
Post by: Gunner on January 07, 2011, 03:29:29 AM
Give me a sec
Title: Re: How do I speed up this treeview insert?
Post by: sinsi on January 07, 2011, 03:31:43 AM
WM_SETREDRAW maybe?
Title: Re: How do I speed up this treeview insert?
Post by: dedndave on January 07, 2011, 03:32:55 AM
but, if you have to pass the address of a structure - and you want to go a little faster...
       push     structure.member6
       push     structureMember5
       push     structureMember4
       push     structureMember3
       push     structureMember2
       push     structureMember1
       INVOKE  SomeAPI,parm1,parm2,esp
       add     esp,(4 * NumberOfDwordMembers)   ;24 total bytes, in this example

note that the structure pointer is passed as the last parm in this example, as it is in most of what i see in your code
if it is not the last parm (first parm pushed), you have to play it a little differently

it also makes for smaller code, as opposed to all those MOV's
Title: Re: How do I speed up this treeview insert?
Post by: Gunner on January 07, 2011, 03:37:07 AM
the code that calls this is a menu handler
LoadTree proc uses esi edi ebx
LOCAL   lpMem:DWORD
LOCAL   lpLen:DWORD
LOCAL   pArray:DWORD
LOCAL   lcnt:DWORD
LOCAL   pArray2:DWORD
LOCAL   lcnt2:DWORD
LOCAL   TempCount[8]:BYTE


    invoke  read_disk_file, addr lpszLogFile, addr lpMem, addr lpLen
    test    eax, eax
    jnz     OpenIt
    invoke  MessageBox, Main.hMain, offset szErrMsg, offset szErrTitle, MB_ICONERROR
    jmp     Done
   
OpenIt:
    invoke  ShowWindow, hTVMainUser, SW_HIDE
    invoke  ShowWindow, hProgress, SW_SHOW
    invoke  InvalidateRect, hProgress, 0, TRUE
    invoke  ltok, lpMem, addr pArray
    mov     lcnt, eax
    invoke  memfill, addr lpszTotalRecords, sizeof lpszTotalRecords, NULL
    invoke  dwtoa, lcnt, addr lpszTotalRecords
    invoke  memfill, addr lpszRecords, sizeof lpszRecords, NULL
    invoke  szMultiCat, 3, addr lpszRecords, offset szOf, addr lpszTotalRecords, offset szRecords
    mov esi, pArray
    mov edi, lcnt
    xor ebx, ebx
    invoke  GetWindowDC, hProgress
    mov     hDC, eax
    invoke  SelectObject, hDC, hFontBold
    mov     hObj,eax
  lbl0:
    add ebx, 1
   
    invoke  memfill, addr lpszLoading, sizeof lpszLoading, NULL
   invoke   memfill, addr TempCount, sizeof TempCount, NULL
   invoke   dwtoa, ebx, addr TempCount
    invoke  szMultiCat, 3, addr lpszLoading, offset szLoading, addr TempCount, addr lpszRecords
    invoke  szLen, addr lpszLoading
    invoke TextOut,hDC,1,1,addr lpszLoading, eax



;    invoke ParseLine, [esi]
;;;;;;;;;;;;;;;;;;;;;;;;;
        pushad
        mov     esi, [esi]
        invoke  HeapAlloc, hMainHeap, HEAP_ZERO_MEMORY, sizeof LogFields
        mov     ebx, eax
        invoke  szLen, esi
        add     eax, 1
        invoke  HeapAlloc, hMainHeap, HEAP_ZERO_MEMORY, eax
        mov     (LogFields PTR [ebx]).szFullString, eax
        invoke  szCopy, esi, (LogFields PTR [ebx]).szFullString

    SkipRecordField: ; Fixed field     
        invoke  InString, 1, esi, offset szComma
        add     esi, eax
   
    GetDateField:
        invoke  HeapAlloc, hMainHeap, HEAP_ZERO_MEMORY, 12
        mov     (LogFields PTR [ebx]).szDate, eax
        mov     edi, (LogFields PTR [ebx]).szDate
    @@:
        mov     ax, word ptr [esi]
        add     esi, 2
        cmp     al, ","
        je      SkipTimeField
        mov     word ptr [edi], ax
        add     edi, 2
        mov     byte ptr [edi], "-"
        inc     edi
        jmp     @B
       
    SkipTimeField: ; Fixed field
    ;PrintText "Getting Time"
        dec     edi
        mov     byte ptr [edi], 00H
       
        invoke  InString, 1, esi, offset szComma
        add     esi, eax
       
        invoke  InString, 1, esi, offset szComma
        mov     edi, eax
        invoke  HeapAlloc, hMainHeap, HEAP_ZERO_MEMORY, eax
        mov     (LogFields PTR [ebx]).szIP, eax
        dec     edi
        invoke  szLeft, esi, (LogFields PTR [ebx]).szIP, edi
        invoke  szRemove, (LogFields PTR [ebx]).szIP, (LogFields PTR [ebx]).szIP, offset szQuote
        invoke  AddItem, ebx
        popad
;;;;;;;;;;;;;;;;;;;;;

    add esi, 4

    cmp ebx, edi
   
    jl lbl0
     ;
Done:
    invoke  GlobalFree, pArray
    invoke  GlobalFree, lpMem
   
    invoke  ShowWindow, hProgress, SW_HIDE 
    invoke  ShowWindow, hTVMainUser, SW_SHOW
       
    invoke  DeleteObject,hObj
    invoke  ReleaseDC, hProgress, hDC
    ret
LoadTree endp


AddItem is the code in the first post..  But the bottleneck seems to be in AddItem, because if I don't call it, I can load and parse in a jiffy  :bg
Title: Re: How do I speed up this treeview insert?
Post by: donkey on January 07, 2011, 03:40:51 AM
When adding items to the treeview its best not to have it redrawn constantly, one of the most time consuming aspects of the control. Use LockWindowUpdate to lock out the window until you are done with populating it.

http://msdn.microsoft.com/en-us/library/dd145034%28v=vs.85%29.aspx
Title: Re: How do I speed up this treeview insert?
Post by: dedndave on January 07, 2011, 03:44:04 AM
sure am glad we have Edgar around   :U
Title: Re: How do I speed up this treeview insert?
Post by: Gunner on January 07, 2011, 03:49:26 AM
Quote from: donkey on January 07, 2011, 03:40:51 AM
When adding items to the treeview its best not to have it redrawn constantly, one of the most time consuming aspects of the control. Use LockWindowUpdate to lock out the window until you are done with populating it.

http://msdn.microsoft.com/en-us/library/dd145034%28v=vs.85%29.aspx

Yes, that is one of the first things I tried...  Doesn't seem to make a difference, since I hide the tree during loading.. I dunno.
Title: Re: How do I speed up this treeview insert?
Post by: sinsi on January 07, 2011, 03:52:36 AM
LockWindowUpdate was my first thought, but MSDN says
QuoteLockWindowUpdate is not intended for general-purpose suppression of window redraw. Use the WM_SETREDRAW message to disable redrawing of a particular window.

Title: Re: How do I speed up this treeview insert?
Post by: Gunner on January 07, 2011, 03:54:01 AM
Quote from: sinsi on January 07, 2011, 03:31:43 AM
WM_SETREDRAW maybe?

Maybe?  YES!  That seems to do the trick!  Using that message, got the tree loading down from about 10 or so seconds to about 6 seconds!  For a 2 meg file, I think that is livable... but can it get better?

There is TVN_GETDISPINFO/TVN_SETDISPINFO would that help?  Don't know how to use it though...
Title: Re: How do I speed up this treeview insert?
Post by: oex on January 07, 2011, 04:12:50 AM
It is a very long time since I used.... I used to have (very) large treeviews on one application I wrote and although there were methods to speed it up which you have probably used all of here and more another method I used was to selectively add as required but as I say this was 5-10 years back maybe and in VB6 so if there were issues with this also I dont remember :lol
Title: Re: How do I speed up this treeview insert?
Post by: dedndave on January 07, 2011, 04:36:20 AM
all those memfill calls shouldn't be needed, Rob
invoke  memfill, addr lpszTotalRecords, sizeof lpszTotalRecords, NULL
just use
mov byte ptr lpszTotalRecords,0
or, if AL happens to be 0
mov lpszTotalRecords,al


this one isn't needed at all
invoke   memfill, addr TempCount, sizeof TempCount, NULL
Title: Re: How do I speed up this treeview insert?
Post by: jj2007 on January 07, 2011, 07:58:53 AM
Hi Gunner,
The loading part could be done faster - I load (with MB Recall) a 13MB csv file in about 32 ms. But the bottleneck is not there.
Check if there is an equivalent to the LB_INITSTORAGE message - that would yield a factor 30-40 improvement. However, MSDN has a rather pessimistic view:

Performance Tips (http://msdn.microsoft.com/en-us/library/ms529261%28VS.85%29.aspx#WebControls_TreeView_Performance)

This section provides practical recommendations for using the TreeView.
Tree Size

Rendering enormous hierarchical data sets in a single TreeView data island is not recommended. As a general rule, try to keep the number of nodes in a tree under 1000.

Each time an update is made to the UI of a TreeView control, for example, whenever a node is expanded or collapsed, additional data is appended to the view state, which maintains a history of state changes in the Web page. This means that each time a view is updated with a large data island, the amount of data appended to the view state history is increased. In ASP.NET, there is a limit in the size of the view state, so the combination of very large tree hierarchies combined with multiple updates may cause performance problems, and in certain cases portions of the history could be lost.
Title: Re: How do I speed up this treeview insert?
Post by: Gunner on January 11, 2011, 01:08:19 AM
Thanks everyone for the great tips, they all help speed up the tree loading from my original post!!!!  I have found a faster way..  When I insert a root node, I save the text to a listview item, and the handle to the node in the lparam of the lvitem, next time I go and load an item, instead of looping through ALL the tree nodes (which is where the bottleneck was)  I just do a LVM_FINDITEM and if the item is in the listview, get the lparam and and use that as the parent.   Got the loading down to about 2 and a half seconds for about 6000 items  :clap: 

I have a feeling I could remove the listview and do the same with code only... not sure though!  My quest continues for more speed!
Title: Re: How do I speed up this treeview insert?
Post by: Tedd on January 14, 2011, 01:08:34 PM
You can insert only the top-level nodes, and then respond to TVN_ITEMEXPANDING (it's delivered through WM_NOTIFY) and insert the expanded node's children at that point.
This will avoid the extra listview and searching.


TVN_GETDISPINFO isn't quite suitable - it's meant for items that change dynamically, so you can update the text/attributes whenever they're drawn - you'd still need to insert all of the items.
Title: Re: How do I speed up this treeview insert?
Post by: oex on January 14, 2011, 10:34:19 PM
Quote from: Tedd on January 14, 2011, 01:08:34 PM
You can insert only the top-level nodes, and then respond to TVN_ITEMEXPANDING (it's delivered through WM_NOTIFY) and insert the expanded node's children at that point.
This will avoid the extra listview and searching.

This isnt ideal, it is better to add 2 levels so the children are already there otherwise there is a delay on expand.... One level extra should suffice however it is just a pain to manage the items to add.... Long time since I did this in VB and this was easier than in ASM....
Title: Re: How do I speed up this treeview insert?
Post by: sinsi on January 25, 2011, 03:38:32 PM
An interesting read from Mr Chen
There's a default implementation for WM_SETREDRAW, but you might be able to do better (http://blogs.msdn.com/b/oldnewthing/archive/2011/01/24/10119211.aspx)