I have a database application where you can scroll through records, and one of the memo fields uses a DBRichEdit component to display/edit the field.
I was trying to use LiveSpelling, but have found it to be *very* slow, even if I write an OnSpellingCheck routine that always returns Mispelled := false.
From tests with AQTime, it appears that the slow speed is due to the background RVWordEnumThread. The OnChange event calls StartLiveSpelling to spellcheck the current field, and that seems to create a new thread.
It looks like all this thread does is parse the memo into words and check each word. But why is this so slow. I'm talking about memo fields that are actually *very* short. Even if there is just one or two words in the field, it seems to take a long time for this thread to function.
Maybe it's the overhead of always creating and destroying this thread. Or perhaps there is something wrong with the thread that is causing it to be very slow. I tried to understand how the thread is being synchronized, but I wasn't able to figure it out. I tried lowering the Sleep(50) value, but that didn't make any difference at all.
Is anyone else seeing this? Is there anything that can be done to speed it up?
Live Spellcheck slow speed
-
- Site Admin
- Posts: 17534
- Joined: Sat Aug 27, 2005 10:28 am
- Contact:
Actually, it was designed not for speed but for consuming low CPU resources.
No, new thread is not created after OnChange. When making changes, the thread is suspended and then resumed after the change was completed. Suspending the thread while making changes is absolutely necessary.
Possible delays may be because:
- all editing operations wait while the thread finishes checking the current word;
- the thread waits some time before resuming, so, when the user makes many quick changes in the document (for example, very fast typing), it sleeps (to prevent slowing down of editing).
But all these delays are barely noticeable even on my slow computer (P3 800).
The checking position is reset to the place where the change was made, and the modified item is rechecked (sometimes completely, sometimes not, depending on the operation). All subsequent text items which were checked before are marked as checked, so they are not rechecked again.
What do you mean by slow checking? I just tested on my slow computer, I made a spell checking of the default document in the ActionTest demo. In the worst case (Misspelled := True for all words), it took less than 3 seconds, including redrawing misspelled words one by one.
No, new thread is not created after OnChange. When making changes, the thread is suspended and then resumed after the change was completed. Suspending the thread while making changes is absolutely necessary.
Possible delays may be because:
- all editing operations wait while the thread finishes checking the current word;
- the thread waits some time before resuming, so, when the user makes many quick changes in the document (for example, very fast typing), it sleeps (to prevent slowing down of editing).
But all these delays are barely noticeable even on my slow computer (P3 800).
The checking position is reset to the place where the change was made, and the modified item is rechecked (sometimes completely, sometimes not, depending on the operation). All subsequent text items which were checked before are marked as checked, so they are not rechecked again.
What do you mean by slow checking? I just tested on my slow computer, I made a spell checking of the default document in the ActionTest demo. In the worst case (Misspelled := True for all words), it took less than 3 seconds, including redrawing misspelled words one by one.
I think I've learned a bit more about the problem.
First, to describe the program I'm working on a bit more: Make a database with a Memo field. Add a DBGrid on the key column in a list so that you can navigate the database by clicking on a record, or press the arrow keys to move up or down in this list (changing the database record). Then add a DBRichViewEdit control to display the memo field.
So, as you use the arrow keys to move up and down through the grid, the Memo field is updated to show the value of the current record.
OK, now this is a very fast database browser. I can hold down the up or down arrow key and run through the database very very fast. In fact, the DBRichViewEdit is very nice at not updating when the system is busy, so often it will pause and only update when you release the arrow key. Very nice and fast.
OK, so now we add spellchecking. We call StartLiveSpelling and set the mode to rvlspOnChange. This works fine when typing changes into the DBRichViewEdit control. But when I use the arrow keys in the DBGrid to navigate to a new record, the contents of the DBRichViewEdit changes, but the spellchecker isn't called on the new contents. So I have an AfterScroll event for the Dataset set to call the StartLiveSpelling of the DBRichViewEdit. So now each time the dataset changes to a new record, the spell checking routine is called to check the new record.
This is when it gets slow. As I said, even when there are just a few words in the memo field, and even if I set Mispelled to false for every word. Holding down the up or down arrow keys now takes about a half-second between each record.
I ran AQTime, and it seems the time is being taken by the Suspend and Sychronize calls. If you just press the up or down arrow keys fast, it seems to work fine. It's only when you hold down the keys that the delay starts to happen.
OK, sorry this has been such a long explanation, but here is the new information I discovered: the problem only seems to happen when the RichViewEdit stops updating the screen because it's busy. Holding down the arrow keys seem to trigger this. While preventing the screen refresh seems to work great without spellchecking, it looks like that whatever is being done to optimize the screen refresh is preventing the spellcheck thread from Synchonizing. So the spellcheck thread is waiting for the main thread, causing the slow down.
I'm not sure what triggers the "delayed refresh" of the RichViewEdit control or where the code is that handles this.
I think that in my specific situation (small memo fields that need to be checked fast) I might abandon the entire background thread idea and just check each word myself in a tight loop. I understand your reasons for using a background thread and it's fancy design for being able to abort in the middle. But it seems like the overhead of this thread and the synchonization issues are really causing me problems. Since I'm not checking large documents, I can probably do better with just a simple loop in the OnChange event handler.
Anyway, any other suggestions are welcome.
First, to describe the program I'm working on a bit more: Make a database with a Memo field. Add a DBGrid on the key column in a list so that you can navigate the database by clicking on a record, or press the arrow keys to move up or down in this list (changing the database record). Then add a DBRichViewEdit control to display the memo field.
So, as you use the arrow keys to move up and down through the grid, the Memo field is updated to show the value of the current record.
OK, now this is a very fast database browser. I can hold down the up or down arrow key and run through the database very very fast. In fact, the DBRichViewEdit is very nice at not updating when the system is busy, so often it will pause and only update when you release the arrow key. Very nice and fast.
OK, so now we add spellchecking. We call StartLiveSpelling and set the mode to rvlspOnChange. This works fine when typing changes into the DBRichViewEdit control. But when I use the arrow keys in the DBGrid to navigate to a new record, the contents of the DBRichViewEdit changes, but the spellchecker isn't called on the new contents. So I have an AfterScroll event for the Dataset set to call the StartLiveSpelling of the DBRichViewEdit. So now each time the dataset changes to a new record, the spell checking routine is called to check the new record.
This is when it gets slow. As I said, even when there are just a few words in the memo field, and even if I set Mispelled to false for every word. Holding down the up or down arrow keys now takes about a half-second between each record.
I ran AQTime, and it seems the time is being taken by the Suspend and Sychronize calls. If you just press the up or down arrow keys fast, it seems to work fine. It's only when you hold down the keys that the delay starts to happen.
OK, sorry this has been such a long explanation, but here is the new information I discovered: the problem only seems to happen when the RichViewEdit stops updating the screen because it's busy. Holding down the arrow keys seem to trigger this. While preventing the screen refresh seems to work great without spellchecking, it looks like that whatever is being done to optimize the screen refresh is preventing the spellcheck thread from Synchonizing. So the spellcheck thread is waiting for the main thread, causing the slow down.
I'm not sure what triggers the "delayed refresh" of the RichViewEdit control or where the code is that handles this.
I think that in my specific situation (small memo fields that need to be checked fast) I might abandon the entire background thread idea and just check each word myself in a tight loop. I understand your reasons for using a background thread and it's fancy design for being able to abort in the middle. But it seems like the overhead of this thread and the synchonization issues are really causing me problems. Since I'm not checking large documents, I can probably do better with just a simple loop in the OnChange event handler.
Anyway, any other suggestions are welcome.
Looks like it's definitely the thread overhead that is causing me all of the speed problems.
I essentially copied the Execute method of the thread and have put that into a Timer event so that if there is a 200ms pause in typing, the entire memo control is spellchecked. For my smaller memo contents, this is *much* faster than your background thread method.
I'd like to request that in your next version, you make the various LiveSpell methods in RichView *virtual* methods so that they can be overridden. This will allow someone like me to implement the spellcheck thread differently. All of your routines like AdjustLiveSpellingOnKeypress and AdjustLiveSpellingOnDelete, etc, etc all need to be overridden if I don't want to use the background thread, and right now these are all static methods.
I essentially copied the Execute method of the thread and have put that into a Timer event so that if there is a 200ms pause in typing, the entire memo control is spellchecked. For my smaller memo contents, this is *much* faster than your background thread method.
I'd like to request that in your next version, you make the various LiveSpell methods in RichView *virtual* methods so that they can be overridden. This will allow someone like me to implement the spellcheck thread differently. All of your routines like AdjustLiveSpellingOnKeypress and AdjustLiveSpellingOnDelete, etc, etc all need to be overridden if I don't want to use the background thread, and right now these are all static methods.