How do I increase performance of FlowDocumentScrollViewer?

前端 未结 2 721
野性不改
野性不改 2020-12-31 11:57

In a previous question, I asked how to get real-time logging output in a WPF textbox-like element (WPF append text blocks UI thread heavily but WinForms doesn't?). The a

2条回答
  •  心在旅途
    2020-12-31 12:24

    I execute the sample you've provided, apart from few threading issues the biggest issue is amount of data. which slows down the text rendering as the it grows.

    I tried to rewrite your code in a different way. I used Tasks, BlockingCollection and Virtualization to improve the performance with assumptions that main interest of the application is logging speed.

    Bridge.cs

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows.Documents;
    using System.Threading;
    using System.Collections.Concurrent;
    using System.Threading.Tasks;
    
    namespace PerformanceTest
    {
        public class Bridge
        {
            int counterLimit;
    
            public BlockingCollection output;
            public BlockingCollection impOutput;
            public BlockingCollection errors;
            public BlockingCollection impErrors;
            public BlockingCollection logs;
    
    
            protected static Bridge controller = new Bridge();
    
            public static Bridge Controller
            {
                get
                {
                    return controller;
                }
            }
    
            public MainWindow Window
            {
                set
                {
                    if (value != null)
                    {
                        output = value.outputProducer;
                        impOutput = value.impOutputProducer;
                        errors = value.errorProducer;
                        impErrors = value.impErrorProducer;
                        logs = value.logsProducer;
                    }
                }
            }
    
            public bool Running
            {
                get;
                set;
            }
    
            private Bridge()
            {
                //20000 lines seems to slow down tabbing enough to prove my point.
                //increase this number to get even worse results.
                counterLimit = 40000;
            }
    
            public void PrintLotsOfText()
            {
                Task.Run(() => GenerateOutput());
                Task.Run(() => GenerateError());
            }
    
            private void GenerateOutput()
            {
                //There is tons of output text, so print super fast if possible.
                int counter = 1;
                while (Running && counter < counterLimit)
                {
                    PrintOutput("I will never say this horrible word again as long I live. This is confession #" + counter++ + ".");
                    //Task.Delay(1).Wait();
                }
            }
    
            private void GenerateError()
            {
                int counter = 1;
                while (Running && counter < counterLimit)
                {
                    PrintError("I will never forgive your errors as long I live. This is confession #" + counter++ + ".");
                    //Task.Delay(1).Wait();
                }
            }
    
            #region Printing
            delegate void StringArgDelegate(String s);
            delegate void InlineArgDelegate(Inline inline);
            public void PrintOutput(String s)
            {
                output.TryAdd(s);
                PrintLog("d " + s);
            }
    
            public void PrintImportantOutput(String s)
            {
                impOutput.TryAdd(s);
                PrintLog("D " + s);
            }
    
            public void PrintError(String s)
            {
                errors.TryAdd(s);
                PrintLog("e " + s);
            }
    
            public void PrintImportantError(String s)
            {
                impErrors.TryAdd(s);
                PrintLog("E " + s);
            }
    
            public void PrintLog(String s)
            {
                String text = s;
                logs.TryAdd(text);
            }
            #endregion
        }
    }
    

    MainWindow.xaml

    
        
            
                
                    
                
                
                    
                        

    MainWindow.xaml.cs

    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Documents;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Imaging;
    using System.Windows.Navigation;
    using System.Windows.Shapes;
    using System.Windows.Threading;
    
    namespace PerformanceTest
    {
        /// 
        /// Interaction logic for MainWindow.xaml
        /// 
        public partial class MainWindow : Window
        {
            public BlockingCollection outputProducer = new BlockingCollection();
            public BlockingCollection impOutputProducer = new BlockingCollection();
            public BlockingCollection errorProducer = new BlockingCollection();
            public BlockingCollection impErrorProducer = new BlockingCollection();
            public BlockingCollection logsProducer = new BlockingCollection();
    
            public ObservableCollection Output { get; set; }
            public ObservableCollection Errors { get; set; }
            public ObservableCollection Logs { get; set; }
    
            Dispatcher dispatcher;
    
            public MainWindow()
            {
                Bridge.Controller.Window = this;
                try
                {
                    InitializeComponent();
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.InnerException.ToString());
                    Console.WriteLine(ex.StackTrace);
                }
    
                dispatcher = Dispatcher;
                Output = new ObservableCollection();
                Errors = new ObservableCollection();
                Logs = new ObservableCollection();
                Task.Run(() => Print(outputProducer, Output));
                Task.Run(() => Print(errorProducer, Errors));
                Task.Run(() => Print(logsProducer, Logs));
            }
    
            public void Print(BlockingCollection producer, ObservableCollection target)
            {
                try
                {
                    foreach (var str in producer.GetConsumingEnumerable())
                    {
                        dispatcher.Invoke(() =>
                        {
                            target.Insert(0, str);
                        }, DispatcherPriority.Background);
                    }
                }
                catch (TaskCanceledException)
                {
                    //window closing before print finish
                }
            }
    
            private void StartButton_Click(object sender, RoutedEventArgs e)
            {
                if (!Bridge.Controller.Running)
                {
                    Bridge.Controller.Running = true;
                    Bridge.Controller.PrintLotsOfText();
                }
            }
    
            private void EndButton_Click(object sender, RoutedEventArgs e)
            {
                Bridge.Controller.Running = false;
            }
        }
    }
    
    
    

    For a full working sample download PerformanceTest.zip and see if this is close to what you need. I have only rewritten only some portion. If this example is heading to the desired direction, we can implement rest of the functionality. un-comment Task.Delay(1).Wait(); in Bridge.cs if you may want to slow down the production to see the mixed logs otherwise the logs generation is too fast so it appear one after other in logs tab.

    提交回复
    热议问题