0

in my wpf application, I have a datagrid with 100 columns, and there's a data updating thread to update all datagrid cell binding data every 1 second.

The cpu usage will be about 15% on my "slow" computer, but if I've popup a datagrid context menu or a top menu, the cpu usage will be up to 30%, and it won't get down any more even though I've close the menu. And if I've suspended the data updating thread, the CPU usage get down to 0%, resume the data updating thread, the CPU usage get up to 30% again. I've cut down the number of columns to 20, and the CPU usage goes up too but not so obviousely.

I've try build the project with .net framework 4.x, .net core 3.x, .net 5, .net 6, debug or release, the cpu usage will get up and won't get down any way. I've try to set the context menu to null and do the garbage collection, but it makes no difference.

Wanted to know if there's good practice to make the CPU usage get down and what would be the best way to do that?

Here is my source code, it's simple but a little bit long.

using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;

namespace datagrid_perf_simple
{
    public class PriceAll
    {
        private double[] arr_ = new double[100];
        
        public PriceAll(double d)
        {
            for (int i = 0; i < 100; i++)
            {
                arr_[i] = d;
            }
        }

        public void UpdatePrice()
        {
            for (int i = 0; i < 100; i++)
            {
                arr_[i] += 0.001;
            }
        }
        public double Price0 => arr_[0];
        public double Price1 => arr_[1];
        public double Price2 => arr_[2];
        public double Price3 => arr_[3];
        public double Price4 => arr_[4];
        public double Price5 => arr_[5];
        public double Price6 => arr_[6];
        public double Price7 => arr_[7];
        public double Price8 => arr_[8];
        public double Price9 => arr_[9];
        public double Price10 => arr_[10];
        public double Price11 => arr_[11];
        public double Price12 => arr_[12];
        public double Price13 => arr_[13];
        public double Price14 => arr_[14];
        public double Price15 => arr_[15];
        public double Price16 => arr_[16];
        public double Price17 => arr_[17];
        public double Price18 => arr_[18];
        public double Price19 => arr_[19];
        public double Price20 => arr_[20];
        public double Price21 => arr_[21];
        public double Price22 => arr_[22];
        public double Price23 => arr_[23];
        public double Price24 => arr_[24];
        public double Price25 => arr_[25];
        public double Price26 => arr_[26];
        public double Price27 => arr_[27];
        public double Price28 => arr_[28];
        public double Price29 => arr_[29];
        public double Price30 => arr_[30];
        public double Price31 => arr_[31];
        public double Price32 => arr_[32];
        public double Price33 => arr_[33];
        public double Price34 => arr_[34];
        public double Price35 => arr_[35];
        public double Price36 => arr_[36];
        public double Price37 => arr_[37];
        public double Price38 => arr_[38];
        public double Price39 => arr_[39];
        public double Price40 => arr_[40];
        public double Price41 => arr_[41];
        public double Price42 => arr_[42];
        public double Price43 => arr_[43];
        public double Price44 => arr_[44];
        public double Price45 => arr_[45];
        public double Price46 => arr_[46];
        public double Price47 => arr_[47];
        public double Price48 => arr_[48];
        public double Price49 => arr_[49];
        public double Price50 => arr_[50];
        public double Price51 => arr_[51];
        public double Price52 => arr_[52];
        public double Price53 => arr_[53];
        public double Price54 => arr_[54];
        public double Price55 => arr_[55];
        public double Price56 => arr_[56];
        public double Price57 => arr_[57];
        public double Price58 => arr_[58];
        public double Price59 => arr_[59];
        public double Price60 => arr_[60];
        public double Price61 => arr_[61];
        public double Price62 => arr_[62];
        public double Price63 => arr_[63];
        public double Price64 => arr_[64];
        public double Price65 => arr_[65];
        public double Price66 => arr_[66];
        public double Price67 => arr_[67];
        public double Price68 => arr_[68];
        public double Price69 => arr_[69];
        public double Price70 => arr_[70];
        public double Price71 => arr_[71];
        public double Price72 => arr_[72];
        public double Price73 => arr_[73];
        public double Price74 => arr_[74];
        public double Price75 => arr_[75];
        public double Price76 => arr_[76];
        public double Price77 => arr_[77];
        public double Price78 => arr_[78];
        public double Price79 => arr_[79];
        public double Price80 => arr_[80];
        public double Price81 => arr_[81];
        public double Price82 => arr_[82];
        public double Price83 => arr_[83];
        public double Price84 => arr_[84];
        public double Price85 => arr_[85];
        public double Price86 => arr_[86];
        public double Price87 => arr_[87];
        public double Price88 => arr_[88];
        public double Price89 => arr_[89];
        public double Price90 => arr_[90];
        public double Price91 => arr_[91];
        public double Price92 => arr_[92];
        public double Price93 => arr_[93];
        public double Price94 => arr_[94];
        public double Price95 => arr_[95];
        public double Price96 => arr_[96];
        public double Price97 => arr_[97];
        public double Price98 => arr_[98];
        public double Price99 => arr_[99];
    }

    public class TestItem : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string s)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(s));
        }

        public PriceAll Pa { get; set; }

    }

    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private DateTime lastUpdateTime_, lastChangeTime_;
        private bool closeFlag_ = false;
        private bool suspendFlag_ = false;
        public ObservableCollection<TestItem> testItems_ = new ObservableCollection<TestItem>();
        public ObservableCollection<TestItem> TestItems => testItems_;

        public MainWindow()
        {
            InitializeComponent();

            this.DataContext = this;

            for (int i = 0; i < 100; i++)
            {
                var item = new TestItem();
                item.Pa = new PriceAll(1.234);
                TestItems.Add(item);
            }

            if (true)
            {
                var t = new System.Threading.Thread(() =>
                {
                    for (int i = 1; i < 100000; ++i)
                    {
                        System.Threading.Thread.Sleep(100);
                        if (suspendFlag_)
                        {
                            continue;
                        }
                        if (closeFlag_)
                        {
                            break;
                        }
                        lock (this)
                        {
                            foreach (var item in TestItems)
                            {
                                for (int j = 0; j < 100; j++)
                                {
                                    item.Pa.UpdatePrice();
                                }
                                //item.OnPropertyChanged("Pa");
                            }
                            lastChangeTime_ = DateTime.Now;
                        }
                    }
                });
                t.Start();
            }

            if (true)
            {
                var t = new System.Threading.Thread(() =>
                {
                    for (; ; )
                    {
                        System.Threading.Thread.Sleep(1000);
                        if (suspendFlag_)
                        {
                            continue;
                        }
                        if (closeFlag_)
                        {
                            break;
                        }
                        lock (this)
                        {
                            if (lastChangeTime_ > lastUpdateTime_)
                            {
                                lastUpdateTime_ = lastChangeTime_;
                                foreach (var item in TestItems)
                                {
                                    item.OnPropertyChanged("Pa");
                                }
                            }
                        }
                    }
                });
                t.Start();
            }
        }

        private void testGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
            if (sender == this.testGrid && testGrid.SelectedCells.Count == 1)
            {
                var col = testGrid.SelectedCells[0].Column;
            }
        }

        private void Window_Closing(object sender, CancelEventArgs e)
        {
            closeFlag_ = true;
        }

        private void MenuItem_Click(object sender, RoutedEventArgs e)
        {
            //this.testGrid.ContextMenu = null;
            //this.testGrid.ContextMenu.IsEnabled = false;
        }

        private void testGrid_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            //if (this.testGrid.ContextMenu == null)
            //{
            //    var contextMenu = new ContextMenu();
            //    var item = new MenuItem();
            //    item.Header = "Plot time series chart";
            //    item.Click += new RoutedEventHandler(MenuItem_Click);
            //    contextMenu.Items.Add(item);

            //    //contextMenu.IsOpen = true;
            //    this.testGrid.ContextMenu = contextMenu;
            //}
        }

        private void BtnSuspend_Click(object sender, RoutedEventArgs e)
        {
            if (this.btnSuspend.Content as string == "Suspend")
            {
                this.btnSuspend.Content = "Run";
                this.suspendFlag_ = true;
            }
            else
            {
                this.btnSuspend.Content = "Suspend";
                this.suspendFlag_ = false;
            }
        }

        private void miExit_Click(object sender, RoutedEventArgs e)
        {

        }

        private void BtnGc_Click(object sender, RoutedEventArgs e)
        {
            GC.Collect();
        }
    }
}


And this is my xaml:

<Window x:Class="datagrid_perf_simple.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:datagrid_perf_simple"
        mc:Ignorable="d" Title="MainWindow" Height="450" Width="800" Closing="Window_Closing">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition Height="30" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Menu Grid.Row="0" x:Name="menu" HorizontalAlignment="Left" VerticalAlignment="Top" IsMainMenu="True">
            <MenuItem Header="File">
                <MenuItem x:Name="miExit" Header="Exit" Click="miExit_Click"></MenuItem>
            </MenuItem>
        </Menu>
        <StackPanel Grid.Row="1" Orientation="Horizontal">
            <Button x:Name="btnGc" Content="GC" Click="BtnGc_Click" />
            <Button x:Name="btnSuspend" Content="Suspend" Click="BtnSuspend_Click" />
        </StackPanel>
        <DataGrid Grid.Row="2" x:Name="testGrid" ItemsSource="{Binding TestItems}" 
                  SelectionUnit="Cell" IsReadOnly="True" AutoGenerateColumns="False" 
                  MouseDoubleClick="testGrid_MouseDoubleClick" Visibility="Visible" Margin="0" 
                  AlternatingRowBackground="LightCyan" FontSize="13" MouseRightButtonUp="testGrid_MouseRightButtonUp">
            <DataGrid.ContextMenu>
                <ContextMenu>
                    <MenuItem Header="Plot time series chart" Click="MenuItem_Click" />
                </ContextMenu>
            </DataGrid.ContextMenu>
            <DataGrid.Columns>
                <DataGridTextColumn Header="price0" Binding="{Binding Pa.Price0, Mode=OneWay}" />
                <DataGridTextColumn Header="price1" Binding="{Binding Pa.Price1, Mode=OneWay}" />
                <DataGridTextColumn Header="price2" Binding="{Binding Pa.Price2, Mode=OneWay}" />
                <DataGridTextColumn Header="price3" Binding="{Binding Pa.Price3, Mode=OneWay}" />
                <DataGridTextColumn Header="price4" Binding="{Binding Pa.Price4, Mode=OneWay}" />
                <DataGridTextColumn Header="price5" Binding="{Binding Pa.Price5, Mode=OneWay}" />
                <DataGridTextColumn Header="price6" Binding="{Binding Pa.Price6, Mode=OneWay}" />
                <DataGridTextColumn Header="price7" Binding="{Binding Pa.Price7, Mode=OneWay}" />
                <DataGridTextColumn Header="price8" Binding="{Binding Pa.Price8, Mode=OneWay}" />
                <DataGridTextColumn Header="price9" Binding="{Binding Pa.Price9, Mode=OneWay}" />
                <DataGridTextColumn Header="price10" Binding="{Binding Pa.Price10, Mode=OneWay}" />
                <DataGridTextColumn Header="price11" Binding="{Binding Pa.Price11, Mode=OneWay}" />
                <DataGridTextColumn Header="price12" Binding="{Binding Pa.Price12, Mode=OneWay}" />
                <DataGridTextColumn Header="price13" Binding="{Binding Pa.Price13, Mode=OneWay}" />
                <DataGridTextColumn Header="price14" Binding="{Binding Pa.Price14, Mode=OneWay}" />
                <DataGridTextColumn Header="price15" Binding="{Binding Pa.Price15, Mode=OneWay}" />
                <DataGridTextColumn Header="price16" Binding="{Binding Pa.Price16, Mode=OneWay}" />
                <DataGridTextColumn Header="price17" Binding="{Binding Pa.Price17, Mode=OneWay}" />
                <DataGridTextColumn Header="price18" Binding="{Binding Pa.Price18, Mode=OneWay}" />
                <DataGridTextColumn Header="price19" Binding="{Binding Pa.Price19, Mode=OneWay}" />
                <DataGridTextColumn Header="price20" Binding="{Binding Pa.Price20, Mode=OneWay}" />
                <DataGridTextColumn Header="price21" Binding="{Binding Pa.Price21, Mode=OneWay}" />
                <DataGridTextColumn Header="price22" Binding="{Binding Pa.Price22, Mode=OneWay}" />
                <DataGridTextColumn Header="price23" Binding="{Binding Pa.Price23, Mode=OneWay}" />
                <DataGridTextColumn Header="price24" Binding="{Binding Pa.Price24, Mode=OneWay}" />
                <DataGridTextColumn Header="price25" Binding="{Binding Pa.Price25, Mode=OneWay}" />
                <DataGridTextColumn Header="price26" Binding="{Binding Pa.Price26, Mode=OneWay}" />
                <DataGridTextColumn Header="price27" Binding="{Binding Pa.Price27, Mode=OneWay}" />
                <DataGridTextColumn Header="price28" Binding="{Binding Pa.Price28, Mode=OneWay}" />
                <DataGridTextColumn Header="price29" Binding="{Binding Pa.Price29, Mode=OneWay}" />
                <DataGridTextColumn Header="price30" Binding="{Binding Pa.Price30, Mode=OneWay}" />
                <DataGridTextColumn Header="price31" Binding="{Binding Pa.Price31, Mode=OneWay}" />
                <DataGridTextColumn Header="price32" Binding="{Binding Pa.Price32, Mode=OneWay}" />
                <DataGridTextColumn Header="price33" Binding="{Binding Pa.Price33, Mode=OneWay}" />
                <DataGridTextColumn Header="price34" Binding="{Binding Pa.Price34, Mode=OneWay}" />
                <DataGridTextColumn Header="price35" Binding="{Binding Pa.Price35, Mode=OneWay}" />
                <DataGridTextColumn Header="price36" Binding="{Binding Pa.Price36, Mode=OneWay}" />
                <DataGridTextColumn Header="price37" Binding="{Binding Pa.Price37, Mode=OneWay}" />
                <DataGridTextColumn Header="price38" Binding="{Binding Pa.Price38, Mode=OneWay}" />
                <DataGridTextColumn Header="price39" Binding="{Binding Pa.Price39, Mode=OneWay}" />
                <DataGridTextColumn Header="price40" Binding="{Binding Pa.Price40, Mode=OneWay}" />
                <DataGridTextColumn Header="price41" Binding="{Binding Pa.Price41, Mode=OneWay}" />
                <DataGridTextColumn Header="price42" Binding="{Binding Pa.Price42, Mode=OneWay}" />
                <DataGridTextColumn Header="price43" Binding="{Binding Pa.Price43, Mode=OneWay}" />
                <DataGridTextColumn Header="price44" Binding="{Binding Pa.Price44, Mode=OneWay}" />
                <DataGridTextColumn Header="price45" Binding="{Binding Pa.Price45, Mode=OneWay}" />
                <DataGridTextColumn Header="price46" Binding="{Binding Pa.Price46, Mode=OneWay}" />
                <DataGridTextColumn Header="price47" Binding="{Binding Pa.Price47, Mode=OneWay}" />
                <DataGridTextColumn Header="price48" Binding="{Binding Pa.Price48, Mode=OneWay}" />
                <DataGridTextColumn Header="price49" Binding="{Binding Pa.Price49, Mode=OneWay}" />
                <DataGridTextColumn Header="price50" Binding="{Binding Pa.Price50, Mode=OneWay}" />
                <DataGridTextColumn Header="price51" Binding="{Binding Pa.Price51, Mode=OneWay}" />
                <DataGridTextColumn Header="price52" Binding="{Binding Pa.Price52, Mode=OneWay}" />
                <DataGridTextColumn Header="price53" Binding="{Binding Pa.Price53, Mode=OneWay}" />
                <DataGridTextColumn Header="price54" Binding="{Binding Pa.Price54, Mode=OneWay}" />
                <DataGridTextColumn Header="price55" Binding="{Binding Pa.Price55, Mode=OneWay}" />
                <DataGridTextColumn Header="price56" Binding="{Binding Pa.Price56, Mode=OneWay}" />
                <DataGridTextColumn Header="price57" Binding="{Binding Pa.Price57, Mode=OneWay}" />
                <DataGridTextColumn Header="price58" Binding="{Binding Pa.Price58, Mode=OneWay}" />
                <DataGridTextColumn Header="price59" Binding="{Binding Pa.Price59, Mode=OneWay}" />
                <DataGridTextColumn Header="price60" Binding="{Binding Pa.Price60, Mode=OneWay}" />
                <DataGridTextColumn Header="price61" Binding="{Binding Pa.Price61, Mode=OneWay}" />
                <DataGridTextColumn Header="price62" Binding="{Binding Pa.Price62, Mode=OneWay}" />
                <DataGridTextColumn Header="price63" Binding="{Binding Pa.Price63, Mode=OneWay}" />
                <DataGridTextColumn Header="price64" Binding="{Binding Pa.Price64, Mode=OneWay}" />
                <DataGridTextColumn Header="price65" Binding="{Binding Pa.Price65, Mode=OneWay}" />
                <DataGridTextColumn Header="price66" Binding="{Binding Pa.Price66, Mode=OneWay}" />
                <DataGridTextColumn Header="price67" Binding="{Binding Pa.Price67, Mode=OneWay}" />
                <DataGridTextColumn Header="price68" Binding="{Binding Pa.Price68, Mode=OneWay}" />
                <DataGridTextColumn Header="price69" Binding="{Binding Pa.Price69, Mode=OneWay}" />
                <DataGridTextColumn Header="price70" Binding="{Binding Pa.Price70, Mode=OneWay}" />
                <DataGridTextColumn Header="price71" Binding="{Binding Pa.Price71, Mode=OneWay}" />
                <DataGridTextColumn Header="price72" Binding="{Binding Pa.Price72, Mode=OneWay}" />
                <DataGridTextColumn Header="price73" Binding="{Binding Pa.Price73, Mode=OneWay}" />
                <DataGridTextColumn Header="price74" Binding="{Binding Pa.Price74, Mode=OneWay}" />
                <DataGridTextColumn Header="price75" Binding="{Binding Pa.Price75, Mode=OneWay}" />
                <DataGridTextColumn Header="price76" Binding="{Binding Pa.Price76, Mode=OneWay}" />
                <DataGridTextColumn Header="price77" Binding="{Binding Pa.Price77, Mode=OneWay}" />
                <DataGridTextColumn Header="price78" Binding="{Binding Pa.Price78, Mode=OneWay}" />
                <DataGridTextColumn Header="price79" Binding="{Binding Pa.Price79, Mode=OneWay}" />
                <DataGridTextColumn Header="price80" Binding="{Binding Pa.Price80, Mode=OneWay}" />
                <DataGridTextColumn Header="price81" Binding="{Binding Pa.Price81, Mode=OneWay}" />
                <DataGridTextColumn Header="price82" Binding="{Binding Pa.Price82, Mode=OneWay}" />
                <DataGridTextColumn Header="price83" Binding="{Binding Pa.Price83, Mode=OneWay}" />
                <DataGridTextColumn Header="price84" Binding="{Binding Pa.Price84, Mode=OneWay}" />
                <DataGridTextColumn Header="price85" Binding="{Binding Pa.Price85, Mode=OneWay}" />
                <DataGridTextColumn Header="price86" Binding="{Binding Pa.Price86, Mode=OneWay}" />
                <DataGridTextColumn Header="price87" Binding="{Binding Pa.Price87, Mode=OneWay}" />
                <DataGridTextColumn Header="price88" Binding="{Binding Pa.Price88, Mode=OneWay}" />
                <DataGridTextColumn Header="price89" Binding="{Binding Pa.Price89, Mode=OneWay}" />
                <DataGridTextColumn Header="price90" Binding="{Binding Pa.Price90, Mode=OneWay}" />
                <DataGridTextColumn Header="price91" Binding="{Binding Pa.Price91, Mode=OneWay}" />
                <DataGridTextColumn Header="price92" Binding="{Binding Pa.Price92, Mode=OneWay}" />
                <DataGridTextColumn Header="price93" Binding="{Binding Pa.Price93, Mode=OneWay}" />
                <DataGridTextColumn Header="price94" Binding="{Binding Pa.Price94, Mode=OneWay}" />
                <DataGridTextColumn Header="price95" Binding="{Binding Pa.Price95, Mode=OneWay}" />
                <DataGridTextColumn Header="price96" Binding="{Binding Pa.Price96, Mode=OneWay}" />
                <DataGridTextColumn Header="price97" Binding="{Binding Pa.Price97, Mode=OneWay}" />
                <DataGridTextColumn Header="price98" Binding="{Binding Pa.Price98, Mode=OneWay}" />
                <DataGridTextColumn Header="price99" Binding="{Binding Pa.Price99, Mode=OneWay}" />
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

  • 2
    When you interrupt your program in a debugger, what function is in the middle of running? Do this a few times, and you'll see which thread is consistently running in (a child of) some function. See the method described in [How do I profile C++ code running on Linux?](https://stackoverflow.com/a/378024). Any profiling tools should similarly help in identifying which functions are spending a lot of CPU time. Once you know that, you'll have somewhere to start looking, to figure out *why* they might be getting called so often, or find an infinite loop a thread is stuck in. – Peter Cordes Jul 12 '22 at 10:31
  • Maybe you can suspend drawing when updating the price and resume it afterwards? – MrSpt Jul 12 '22 at 10:39
  • @PeterCordes It's a good advice. I've Try to interrupt the program, and the thread info seams no difference before or after the menu pop out and closed. It's seam like the probem with menu. I'm a C# beginner, and still working on diagnose tools of vsiual studio. – guoshengwei Jul 13 '22 at 06:34
  • @MrSpt Actually there's a lock when drawing (PropertyChanged?.Invoke) or updating, they are running serially. I guess drawing is suspend when updating data. – guoshengwei Jul 13 '22 at 06:39

0 Answers0