Date: | June 14, 2005 / year-entry #151 |
Tags: | code |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20050614-18/?p=35323 |
Comments: | 12 |
Summary: | As we noted last time, adding items to the listview takes an absurd amount of time. Today, we'll make a failed attempt at improving this because it lets me illustrate a listview technique and it lays the groundwork for the real fix next time. Instead of creating the items in their entirety, let's set their... |
As we noted last time, adding items to the listview takes an absurd amount of time. Today, we'll make a failed attempt at improving this because it lets me illustrate a listview technique and it lays the groundwork for the real fix next time.
Instead of creating the items in their entirety,
let's set their text to class RootWindow : public Window { ... LRESULT OnCreate(); LRESULT OnNotify(NMHDR* pnm); void OnGetDispInfo(NMLVDISPINFO* pnmv); ... }; LRESULT RootWindow::OnCreate() { ...
Instead of setting the strings when we create the listview items,
we set their texts to Sidebar: In our case, obtaining the missing data is very fast. If it were slow, we could have optimized the function further by adding the line pnmv->item.mask |= LVIF_DI_SETITEM; to the end. This tells the listview, "Please cache these results and don't ask me for them again." That way, we do the slow computation only once.
After making these changes (though not the (Note also that our program is now relying heavily on the fact that a vector is a fast random-access data structure.) We'll do better next time. |
Comments (12)
Comments are closed. |
A little typo in:
"if (pnmv->item.iItem <; 0 ||"
I’m pretty new to Win32 programming (a little bit late but what the heck) and I’m surprised with amounts of possibilities of making Windows GUI software blazingly fast with a little bit of work and knowledge. What I’m not surprised with is that there aren’t many software developers who can or want to take advantage of these nice toys – I think it’s worth it anyway.
I’d love to see some examples of all those callbacks list views, tree views etc. used The Truly Right Way (TM). Any good sites maybe? Thanks in advance!
And thank you Raymond for your really great, informative blog.
We are still looping through all ~20,000 entries, and instead of inserting the actual text, just inserting item.pszText = LPSTR_TEXTCALLBACK, which is why we don’t see a significant speed-up I assume? I would have thought this might even be slower than the first method, with the added overhead of callbacks etc…
I’m curious as to whether the source of the delay is overhead due to sending 20,000 messages, or whether the slowdown is caused by adding an item to the listview.
snram: The delay is probably a bit of both, but I imagine most of the delay is from adding the items.
This is not a virtual list yet ; when virtual list view is used, there is no need to insert any items, and it will be lightning fast. I guess thats what RC is upto.
I’ve worked with this a lot at my prior company. simply adding the item kills performance. And removing the items is just as bad. The way that I hacked it, and this was using VB6, so it was a bit uglier.. was to add items as needed.
I ended up faking out the scrollbar so that I’d add a few pages at a time to the listview with their data, and then I’d adjust the scrollbar so that the scrollbar sizing was correct. Then you adjust if the user clicks pagedown, down, etc. and when they click on the scrollbar. Then if a different dataset is bound to the listview, I would prune the ‘extra’ list items if there were too many, or else I’d leave it as is and grow the listview as the data is paged down.
If a user clicks on a column to sort, then I go ahead and load all of the data as normal (it’s automatically cached) and do the sorting.
Looks like it starts to free from the 2nd most recently allocated block:
StringPool::~StringPool()
{
HEADER* phdr = m_phdrCur;
while (phdr) {
HEADER hdr = *phdr;
VirtualFree(hdr.m_phdrPrev, hdr.m_cb, MEM_RELEASE);
phdr = hdr.m_phdrPrev;
}
}
how about:
StringPool::~StringPool()
{
while (m_phdrCur) {
HEADER*prev = phdr->m_phdrPrev;
VirtualFree(m_phdrCur, m_phdrCur->m_cb, MEM_RELEASE);
m_phdrCur=prev;
}
}
Probably LVS_OWNERDATA style is needed. You set number of items first but do not insert them. This should improve performance.
Off-topic: "We’ll do better next time." seems to be the mantra of Windows in particular and of (all) software makers in general :) Sorry for being a dirty bastard, I just couldn’t hold it.
Owner-data listviews let you take over data management from the listview.
Hi,
I used to call
::SendMessage(m_hwndLV, WM_SETREDRAW, (WPARAM)FALSE, 0);
before inserting all the items, and the same call but with TRUE as wParam parameter, followed by a call
::RedrawWindow(m_hwndLV, NULL, NULL, RDW_INVALIDATE);
to redraw the window. Inserting items then dropped from ~10 seconds to about 1 second. But if that would have used, it would make not that much difference, would it? Or are there any problems using WM_SETREDRAW?
It is true that the ownerdata is the best, but when using VB, to keep it simple and pluggable, sometimes it is best to use the listview as-is and just ‘attach’ the functionality to an existing listview, instead of having to actually create it.
As far as the redraw function, that does not fix the loading speed issue, and it causes horrible refreshing in some cases. The listview does a much ‘better’ job at refreshing itself by using the dirty areas, you just have to work with it to make it be efficient.