Ask a questionAsk a question
 

AnswerSearch string performance is very slow

  • Tuesday, November 13, 2007 7:29 PMArtur Belico Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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 Block

                TextPointer 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

  • Monday, November 19, 2007 8:17 AMMarco Zhou Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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

  • Friday, November 16, 2007 6:42 AMMarco Zhou Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
  • Friday, November 16, 2007 2:10 PMArtur Belico Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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


  • Friday, November 16, 2007 3:05 PMArtur Belico Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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


  • Monday, November 19, 2007 8:17 AMMarco Zhou Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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

  • Thursday, November 22, 2007 1:18 AMArtur Belico Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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



  • Thursday, November 22, 2007 4:27 AMArtur Belico Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    I have another bottleneck identified. Im using XamlReader.Load and its too slow. For instance:

    Code Block

                FlowDocument 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
  • Monday, November 26, 2007 2:29 AMMarco Zhou Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

  • Monday, November 26, 2007 3:27 AMArtur Belico Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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 Block

                FlowDocument 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
  • Monday, November 26, 2007 8:33 AMMarco Zhou Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

     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()