Search string performance is very slow
- Hello,
Im trying to find a char in a big FlowFocument. I filled the Flowdocument with 20K characters and put what I wanted to find in the end. Then I start the search from the beginning of the flowdocument. It takes more than 5 seconds to find it. This is a part of the code:
Code BlockTextPointer end = CurrentPosition.GetNextInsertionPosition(direction);
string substr = text.Substring(0, 1);
while (end != null)
{
if (new TextRange(CurrentPosition, end).Text == substr)
{
Console.WriteLine(new TextRange(CurrentPosition, end).Text + " " + substr);
}
CurrentPosition = end;
end = CurrentPosition.GetNextInsertionPosition(direction);
}
The text im searching in, has formatting. What can I do to speed up things? Is WPF slow at this?
Thank you,
Artur Carvalho
Answers
Well, I've mocked up a sample of how to select text in RichTextBox, here is the code which performs the find:
public partial class Page1 : Page
{
private TextPointer navigator = null;
private Boolean newFind = true;
public Page1()
{
InitializeComponent();
this.textBox.TextChanged += delegate
{
newFind = true;
};
this.btn.Click += delegate
{
String findText = textBox.Text.Trim();
if (String.IsNullOrEmpty(findText))
{
return;
}
if (newFind)
{
navigator = rtb.Selection.IsEmpty ?
rtb.Document.ContentStart :
rtb.Selection.End.GetNextInsertionPosition(LogicalDirection.Forward);
newFind = false;
}
while (navigator != null && navigator.CompareTo(rtb.Document.ContentEnd) < 0)
{
TextPointer nextPointer = navigator;
Int32 count = 0;
while (nextPointer != null && count < findText.Length)
{
nextPointer = nextPointer.GetNextInsertionPosition(LogicalDirection.Forward);
count++;
}
if (nextPointer != null)
{
TextRange textRange = new TextRange(navigator, nextPointer);
String text = textRange.Text;
if (text == findText)
{
rtb.Focus();
rtb.Selection.Select(textRange.Start, textRange.End);
navigator = nextPointer.GetNextInsertionPosition(LogicalDirection.Forward);
break;
}
}
navigator = navigator.GetNextInsertionPosition(LogicalDirection.Forward);
if (navigator == null)
{
navigator = rtb.Document.ContentStart;
}
}
};
}
}
You can refer to the complete sample here:
http://www.cnblogs.com/Files/sheva/FindInRtbDemo.zip
Hope this helps
All Replies
This thread might help you a bit:
http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=923088&SiteID=1
- Marco, thanks for the reply.
Ive looked into the thread,saw the posts and Prajakta's blog, but I could not find something that could help me.
I figured out that what was slowing me down was using the TextRange. When I use TextRange several times in a loop it gets pretty slow. Im trying other approaches, but they are all too convoluted.
Best Regards,
Artur Carvalho - Marco, thanks for the reply.
Ive looked into the thread,saw the posts and Prajakta's blog, but I could not find something that could help me.
I figured out that what was slowing me down was using the TextRange. When I use TextRange several times in a loop it gets pretty slow. Im trying other approaches, but they are all too convoluted.
Best Regards,
Artur Carvalho Well, I've mocked up a sample of how to select text in RichTextBox, here is the code which performs the find:
public partial class Page1 : Page
{
private TextPointer navigator = null;
private Boolean newFind = true;
public Page1()
{
InitializeComponent();
this.textBox.TextChanged += delegate
{
newFind = true;
};
this.btn.Click += delegate
{
String findText = textBox.Text.Trim();
if (String.IsNullOrEmpty(findText))
{
return;
}
if (newFind)
{
navigator = rtb.Selection.IsEmpty ?
rtb.Document.ContentStart :
rtb.Selection.End.GetNextInsertionPosition(LogicalDirection.Forward);
newFind = false;
}
while (navigator != null && navigator.CompareTo(rtb.Document.ContentEnd) < 0)
{
TextPointer nextPointer = navigator;
Int32 count = 0;
while (nextPointer != null && count < findText.Length)
{
nextPointer = nextPointer.GetNextInsertionPosition(LogicalDirection.Forward);
count++;
}
if (nextPointer != null)
{
TextRange textRange = new TextRange(navigator, nextPointer);
String text = textRange.Text;
if (text == findText)
{
rtb.Focus();
rtb.Selection.Select(textRange.Start, textRange.End);
navigator = nextPointer.GetNextInsertionPosition(LogicalDirection.Forward);
break;
}
}
navigator = navigator.GetNextInsertionPosition(LogicalDirection.Forward);
if (navigator == null)
{
navigator = rtb.Document.ContentStart;
}
}
};
}
}
You can refer to the complete sample here:
http://www.cnblogs.com/Files/sheva/FindInRtbDemo.zip
Hope this helps
- Marco thanks for the reply.
That sample is very similar to what I have now and its slow for my needs. But what I want is to create a list of search results. Something like the find all in visual studio. When I search for, lets say, the letter "a" i get a list of search results. The list of results appears almost immediately in VS.
What can I do to achieve the performance of VS?
Best Regards,
Artur Carvalho - I have another bottleneck identified. Im using XamlReader.Load and its too slow. For instance:
Code BlockFlowDocument doc = new FlowDocument();
string bla = "blablablablablablablabla";
for (int i = 0; i < 1000; i++)
{
doc.Blocks.Add(new Paragraph(new Run(bla)));
}
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
string s = XamlWriter.Save(doc);
watch.Stop();
Console.WriteLine("Time spent: " + watch.Elapsed.ToString());
watch.Reset();
watch.Start();
doc = (FlowDocument)XamlReader.Load(new XmlTextReader(new StringReader(s)));
watch.Stop();
Console.WriteLine("Time spent: " + watch.Elapsed.ToString());
returns:
Time spent: 00:00:04.3581776
Time spent: 00:00:00.2180939
The load is taking .2 seconds, if I have 20 files to search into, I'll get more than 4 seconds.
Is there a faster way to do this?
Best Regards,
Artur Carvalho Hi Artur,
I am sorry that the code I posted above has a serious flaw.
I've written a blog post about this, you can take a look at the two solutions I came up with in the post to see if they can meet your performance requirement. Below is the link to the post:
http://shevaspace.spaces.live.com/blog/cns!FD9A0F1F8DD06954!638.entry
Regarding the XamlReader performance issue. Depending on the size and complexity of the XAML passed to XamlReader, XAML deserialization might be a time consuming process. If you have large and complex XAML files which needs to be deserialized, you'd better load and deserialize them asynchronously, this usually can achieve the perceived performance.
Hope this helps
- Hi Marco,
Thanks for the reply. No need for saying your sorry when im the one asking a favor.
- Your code doesn't compile, I can't find the Linq libraries. Is Linq part of .Net3.5? Or are the libraries in some weird place in the system?
I used this bit of code to load from a file instead of XamlReader, I haven't profiled it yet, but it looks faster:
Code BlockFlowDocument doc = new FlowDocument();
using (Stream stream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(text)))
{
TextRange contents = new TextRange(doc.ContentStart, doc.ContentEnd);
contents.Load(stream, DataFormats.Xaml);
}
Im deserializing asynchrounously by using Backgroundworker. At the moment I created a solution that gets the offsets only with the text that represents the FlowDocument object. I was getting more than 10 seconds to search in 7 big documents, now its in less than .5 seconds. The problem is that its an ugly solution, I can almost see hairs coming out of it...
Best Regards,
Artur Carvalho Artur Carvalho wrote: Your code doesn't compile, I can't find the Linq libraries. Is Linq part of .Net3.5? Or are the libraries in some weird place in the system?
Well, LINQ is part of the .NET 3.5 redistributable, as long as you are programming under .NET 3.5, you should get it for free. I've fixed the sample project to make it target .NET 3.0 instead (thanks to Visual Studio 2008's multi tageting feature
). You can download the source here:
http://www.cnblogs.com/Files/sheva/SearchInFlowDocumentDemo.zip
Artur Carvalho wrote: I used this bit of code to load from a file instead of XamlReader
You are on the right track when using TextRange.Load() instead of XamlReader.Load()


