MasamuneXGP
asked on
RichEd20 Problem: RTF Text insertion too slow
Hello all. I'm currently writing a code editor that I'm trying to make as close to the VB IDE as possible. One thing it will have is colored keywords, comments, and strings. I already have a function that goes through the text, determines what needs to be colored, and spits out the appropriate RTF code. For my tests, I'm using 5000 lines, each with 4 color words. The line checking goes through all of them and returns one big string containing all 5000 lines with an RTF header and tags in the appropriate places.
Here's were the bottleneck happens. I can't seem to get this RTF text into the box without it taking WAY too long. At first I was using the standard RichTextBox control and inserting via .SelRTF. Later, I ditched that for vbAccelerator's RichEdit control and tried using the EM_STREAMIN method. The gain was minimal at best. The single line that inserts the text takes approximately 19 seconds to execute on my P4 1.7GHz machine. This is unacceptable.
VB's IDE is able to color the same 5000 lines in the blink of an eye... There has to be a faster way. Does anyone know how I can speed this up?
Here's were the bottleneck happens. I can't seem to get this RTF text into the box without it taking WAY too long. At first I was using the standard RichTextBox control and inserting via .SelRTF. Later, I ditched that for vbAccelerator's RichEdit control and tried using the EM_STREAMIN method. The gain was minimal at best. The single line that inserts the text takes approximately 19 seconds to execute on my P4 1.7GHz machine. This is unacceptable.
VB's IDE is able to color the same 5000 lines in the blink of an eye... There has to be a faster way. Does anyone know how I can speed this up?
ASKER
Erm... let me clarify. It is not my function that is lagging. I am well aware of the & operator's suckiness, and am using some pretty advanced techniques to generate the string quickly (the SafeArray hack for example). The lagging comes when I try to actually insert the text. It is this one line, where RTFBuild is the variable containing the string:
(VB's Rich TextBox)
.SelRTF = RTFBuild
(vbAccel's RichEdit)
.InsertContents SF_RTF, RTFBuild
Both of those take upwards of 19 seconds to execute. Hense my problem.
(VB's Rich TextBox)
.SelRTF = RTFBuild
(vbAccel's RichEdit)
.InsertContents SF_RTF, RTFBuild
Both of those take upwards of 19 seconds to execute. Hense my problem.
ASKER
I apologize for doubleposting, but this may be relevent. Here is what RTFBuild looks like before I insert it. Keep in mind it's just for testing purposes, so don't expect it to make sense or anything =p As you can see, each line has four color words in it, and that's definately what's slowing it down. Removing the color words has the text inserted much quicker (although still a tad laggy). I know my test is a little above the standard, but I'd really not like to compromise, as I do expect large amounts of code to be entered into it. I am a bit of a performance freak, and it bugs me to see the rest of my program so heavily optimized but slowed down by this one huge bottleneck... Anyway, here it is:
{\rtf1\ansi\deff0{\fonttbl {\f0\fnil\ fcharset0 Courier New;}}
{\colortbl ;\red0\green0\blue128;\red 0\green128 \blue0;\re d128\green 0\blue0;\r ed255\gree n0\blue0;}
\viewkind4\uc1\pard\cf0\la ng1033\f0\ fs20
\cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 "Testing"\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0
\par \cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 "Testing"\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0
\par \cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 "Testing"\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0
\par \cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 "Testing"\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0
....5000 of these....
\par \cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 "Testing"\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0
\par \cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 "Testing"\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0
}
{\rtf1\ansi\deff0{\fonttbl
{\colortbl ;\red0\green0\blue128;\red
\viewkind4\uc1\pard\cf0\la
\cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 "Testing"\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0
\par \cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 "Testing"\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0
\par \cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 "Testing"\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0
\par \cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 "Testing"\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0
....5000 of these....
\par \cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 "Testing"\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0
\par \cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 "Testing"\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0
}
If you are opening file, you'll use .TextRTF not .SelRtf; you shouldn't use multiple .SelRtf calls to insert multiple lines.
Any insertion will be followed by:
- internal optimization/reduction of rtf code - to avoid this, plan everything, no duplicate colors etc.
- formatting - it depends on right margin property and length (line length) of inserted text. Make sure there is no wrapping (like in VB code editor).
Any insertion will be followed by:
- internal optimization/reduction of rtf code - to avoid this, plan everything, no duplicate colors etc.
- formatting - it depends on right margin property and length (line length) of inserted text. Make sure there is no wrapping (like in VB code editor).
To check if there was some internal optimization check rtf code after the insertion - if cf5 is cf4, control did something.
Maybe you have code in SelChange or Change event which is executed during insertion. Use flags to exit those events:
' set flags
bIgnoreInChange = True
bIgnoreInSelChange = True
resetundo
rtbText.TextRTF = S2RTF(x, True)
' reset flags
bIgnoreInChange = False
bIgnoreInSelChange = False
Maybe you have code in SelChange or Change event which is executed during insertion. Use flags to exit those events:
' set flags
bIgnoreInChange = True
bIgnoreInSelChange = True
resetundo
rtbText.TextRTF = S2RTF(x, True)
' reset flags
bIgnoreInChange = False
bIgnoreInSelChange = False
ASKER
As you can see from my last post, I am indeed inserting them all in one fell swoop and with the best color optimization. And I have the RightMargin prop set to the max possible value, so word wrapping isn't an issue either.
Try TextRtf it should be faster than SelRtf.
ASKER
Alrighty, upon analyzing the RTF code output, it appears that my color tags are indeed being reordered. After much tweaking, I finally got the expression (RTFBuild = .TextRTF) to equal True. The end result: it saved me about 2.5 seconds. So now we're down to around 16 seconds instead of 19. Yay~~ =p
That event locking thing I am already doing, and I am using LockWindowUpdate to prevent it to refreshing as well. Experimenting shows .TextRTF to be no faster than .SelRTF. Since it appears this question isn't as easy to answer as I was hoping it would be, this is now 500 point question.
That event locking thing I am already doing, and I am using LockWindowUpdate to prevent it to refreshing as well. Experimenting shows .TextRTF to be no faster than .SelRTF. Since it appears this question isn't as easy to answer as I was hoping it would be, this is now 500 point question.
' Form1, add MS RichTextbox, do not change any property
Option Explicit
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Sub Form_Click()
Dim hdr As String, sLines() As String, tail As String, i As Long, tim0 As Long
' start timer
tim0 = GetTickCount
hdr = "{\rtf1\ansi\deff0{\fonttb l{\f0\fnil \fcharset0 Courier New;}}" & vbCrLf _
& "{\colortbl ;\red0\green0\blue128;\red 0\green128 \blue0;\re d128\green 0\blue0;\r ed255\gree n0\blue0;} " & vbCrLf _
& "\viewkind4\uc1\pard\cf0\l ang1033\f0 \fs20" & vbCrLf
tail = "}"
' fill array of lines
ReDim sLines(0 To 5000)
For i = 0 To UBound(sLines)
sLines(i) = "\cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 ""Testing""\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0"
Next
Me.RichTextBox1.RightMargi n = 60000
Me.RichTextBox1.TextRTF = hdr & Join(sLines, vbCrLf & "\par ") & tail
' show time
Caption = (GetTickCount - tim0) / 1000 & " seconds, length=" & Len(Me.RichTextBox1.Text)
End Sub
Option Explicit
Private Declare Function GetTickCount Lib "kernel32" () As Long
Private Sub Form_Click()
Dim hdr As String, sLines() As String, tail As String, i As Long, tim0 As Long
' start timer
tim0 = GetTickCount
hdr = "{\rtf1\ansi\deff0{\fonttb
& "{\colortbl ;\red0\green0\blue128;\red
& "\viewkind4\uc1\pard\cf0\l
tail = "}"
' fill array of lines
ReDim sLines(0 To 5000)
For i = 0 To UBound(sLines)
sLines(i) = "\cf1 For\cf0 x = 2 + 1 \cf1 To\cf0 Len(\cf3 ""Testing""\cf0 ) - 1 ^ 2 \cf1 Mod\cf0 1 + 2\cf2 'Comment\cf0"
Next
Me.RichTextBox1.RightMargi
Me.RichTextBox1.TextRTF = hdr & Join(sLines, vbCrLf & "\par ") & tail
' show time
Caption = (GetTickCount - tim0) / 1000 & " seconds, length=" & Len(Me.RichTextBox1.Text)
End Sub
ASKER
I started a new project, added an RTB, copied and pasted this exact code, compiled, ran, clicked the form. After finishing, the titlebar read "17.172 seconds, length=300058"
Have you tried this code on your own system? If so what processer speed do you have?
Have you tried this code on your own system? If so what processer speed do you have?
0.484 seconds on old 666 MHz Celeron, MS Rich Textbox 6.0 (SP4). If I increase window size, or change some properties I can make it 2-3 times slower. I'll put a temporary zip with .exe http://www.geocities.com/ameba_vb/temp/rtftest/rtftest.html
Maybe you can try different header:
hdr = "{\rtf1\ansi\deff0\deftab4 80{\fonttb l{\f0\fnil MS Sans Serif;}{\f1\fswiss\fcharse t238 Fixedsys;}}" & _
"{\colortbl\red0\green0\bl ue0;\red19 2\green192 \blue192;\ red0\green 143\blue0; \red255\gr een0\blue0 ;\red0\gre en0\blue12 7;\red170\ green0\blu e0;}" & vbCrLf _
& "\deflang1050\pard\plain\f 1\fs18 "
hdr = "{\rtf1\ansi\deff0\deftab4
"{\colortbl\red0\green0\bl
& "\deflang1050\pard\plain\f
ASKER
It takes 17 seconds on my P4 1.7GHz machine, 11 seconds on my P4 2.4GHz machine, and 48 seconds on my PIII 650MHz laptop.
Seeing as I am also using MS Rich Textbox 6.0 (SP4), I guess it must be something to do with file versions.... Would you mind sending me whatever copy of RichTx32.ocx and RichEd20.dll you have on that 666 Mhz machine?
Seeing as I am also using MS Rich Textbox 6.0 (SP4), I guess it must be something to do with file versions.... Would you mind sending me whatever copy of RichTx32.ocx and RichEd20.dll you have on that 666 Mhz machine?
ASKER
Tried the new header. Font was different, time was the same =\
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
You can find info on versions installed (to system32 directory) at http://support.microsoft.com/default.aspx?scid=/servicedesks/fileversion/dllinfo.asp&SD=MSDN&FR=0
type dll name: RichEd20.dll
type dll name: RichEd20.dll
ASKER
*GASP* It worked! It was indeed RichEd20.dll that was slowing it down. The confusing part is... the RichEd20.dll in my sys32 folder is actually a newer version than the one you sent me... but who cares, I'm just happy it finally works! One last question though: I'm going to be distributing this program. Should I just have the installer place this copy of RichEd20.dll in the installation folder? Or does it have to be regsvr32'd or something?
I suggest placing to installation directory - that's what installer does.
If you replaced newer version with older one in your System dir, that might not be good idea. Maybe MS has the reason, e.g. it is more secure, or something.
If you are sure your dll was debug version for some beta product, I guess manually removing it and replacing/registering is OK.
If you replaced newer version with older one in your System dir, that might not be good idea. Maybe MS has the reason, e.g. it is more secure, or something.
If you are sure your dll was debug version for some beta product, I guess manually removing it and replacing/registering is OK.
ASKER
Thank you very much for your help ameba! Enjoy your 500 points =)
Thanks :-)
> The line checking goes through all of them and returns one big string
> containing all 5000 lines with an RTF header and tags in the appropriate places.
Concatenation in VB isn't optimized, producing a string using '&' will be very slow. There are few solutions for faster string operations, e.g. there is a StringBuilder class by Karl E. Peterson, which uses byte array internally, but for your purpose Join() will work:
S2RTF = rtfhdr & Join(sLines(), vbCrLf & "\par ") & rtftail
where sLines() is array containing rtf code for each line.
That should work well for cases when editor is opening a file, or user is pasting a big block to editor.
Cheers!