http://bugs.winehq.org/show_bug.cgi?id=13355
Dylan Smith dylan.ah.smith@gmail.com changed:
What |Removed |Added ---------------------------------------------------------------------------- CC| |dylan.ah.smith@gmail.com Keywords|download, source, testcase |
--- Comment #14 from Dylan Smith dylan.ah.smith@gmail.com 2008-10-29 19:36:30 --- == Diagnosis ==
I looked into it, and I found that most of the time was spent wrapping the text. The more wrapping that occurs, the slower it takes, so it will take less time with a smaller font size or larger window.
I tried to find out what was taking a long time with wrapping, and I noticed that it was wrapping the entire document 4 times when handing WM_SETTEXT.
1. The text is wrapped as required when it is initially set in WM_SETTEXT.
2. After the text is set ME_UpdateScrollbar is called while processing WM_SETTEXT. Previously there was no scrollbar shown, and the wrapped text is larger vertically than the window, so the scrollbar is shown with ShowScrollBar. This sends a WM_SIZE message to the richedit control where it unconditionally wraps all the text.
3. After wrapping all the text while processing WM_SIZE, ME_UpdateScrollbar is called again. Since this WM_SIZE message is being processed immediately, the scrollbar state stored in the richedit control has not been updated yet, so the bScrollBarWasVisible variable is set to TRUE, and it calls ShowScrollBar (which does nothing) and then wraps all the text.
4. Once WM_SIZE is finished, ME_UpdateRepaint continues. As mentioned in #3, all the text is wrapped after calling ShowScrollBar.
== Proposed Solution ==
There are a few mistakes the code made that could be corrected: 1. WM_SIZE should only re-wrap the document if the horizontal width changes. 2. ME_UpdateScrollbar should call ShowScrollBar after updating the it's internal copy of the scrollbar state. 3. ME_UpdateScrollbar should use the fact that WM_SIZE is called by ShowScrollBar (although this should be tested to ensure it is consistent with Windows)
This would still leave 2 re-wraps, but the first one was only needed to find out that scrollbar would be shown. This first re-wrap could be interrupted when it gets to a paragraph that is below the end of the view, and realizes the scrollbar will be shown. This would only leave a relatively small overhead in wrapping a large document.
Lastly, I noticed that a lot of the time was probably wasted on memory allocated, deallocation, and copying text. This is because paragraphs are split into runs, where a run is a continuous string of text (i.e. not spanning multiple lines) with the same style (e.g. font, size, weight, etc.). Each re-wrap consists of two phases, one is joining the runs as if they are on the same line, and the second is splitting the runs at line breaks.
Splitting runs means the string is split into two chunks of memory, where the second one is newly allocated, and the text in the string after the split is copied into the newly allocated memory. Each split is independent, so this is repeated for each line of a paragraph.
This leads to excess memory being allocated since run for the first line of a paragraph will have enough space for all the lines of the paragraph. The second line will have enough space for all but one of the lines of the paragraph... and so on. This leads to allocating an amount of memory of approximately N*(N/2) compared to N for the text itself. The memory copying for the split happens in a similar manner, with the first step involving copying all but the first line of the paragraph into the memory for the second line.
This memory allocation, deallocation and copying of text could be cut down if the same string could be shared amongst multiple runs (lines in this case) in a paragraph. The run would then contain a pointer to a position in the string, the style of the text, and any other information about how to display that section of the string.
This would avoid a lot of processing, but would need some redesign of the code to manage it properly (e.g. you can't free the memory for the string of one run independently of the others). The simplest solution would be to use one string per paragraph. This may slow insertions for large paragraphs, but it would make up for it with the less time needed to wrap the paragraph.