【问题标题】:Brushes.White slows graphics demo downBrushes.White 减慢图形演示的速度
【发布时间】:2023-03-13 12:34:01
【问题描述】:

以下是 WPF 中 Conway 的生命游戏的(非常幼稚的)实现。这只是一个演示......

xaml:

<Window x:Class="wpf_conway_life_2013_05_19.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="500" Width="500">
    <Grid>
        <Canvas Name="canvas"
            Width="auto"
            Height="auto" 
            HorizontalAlignment="Stretch"  
            VerticalAlignment="Stretch">
        </Canvas>
    </Grid>
</Window>

后面的代码:

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace wpf_conway_life_2013_05_19
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var random = new Random();

            var data = new int[100, 100];

            var dataB = new int[100, 100];

            Func<int, int, int> at = (x, y) =>
                {
                    if (x < 0) x = 100 + x;
                    if (x >= 100) x = x % 100;
                    if (y < 0) y = 100 + y;
                    if (y >= 100) y = y % 100;

                    return data[x, y];
                };

            for (var x = 0; x < 100; x++)
                for (var y = 0; y < 100; y++)
                    data[x, y] = random.Next(2);

            var rectangles = new Rectangle[100, 100];

            for (var x = 0; x < 100; x++)
                for (var y = 0; y < 100; y++)
                {
                    rectangles[x, y] = new Rectangle();

                    canvas.Children.Add(rectangles[x, y]);
                }

            canvas.SizeChanged += (s, e) =>
                {
                    for (var x = 0; x < 100; x++)
                    {
                        for (var y = 0; y < 100; y++)
                        {
                            rectangles[x, y].Width = canvas.ActualWidth / 100;
                            rectangles[x, y].Height = canvas.ActualHeight / 100;

                            Canvas.SetLeft(rectangles[x, y], (canvas.ActualWidth / 100) * x);
                            Canvas.SetTop(rectangles[x, y], (canvas.ActualHeight / 100) * y);
                        }
                    }
                };

            Action macroStep = () =>
                {
                    dataB = new int[100, 100];

                    for (var x = 0; x < 100; x++)
                    {
                        for (var y = 0; y < 100; y++)
                        {
                            var neighbors = 0;

                            for (var i = -1; i <= 1; i++)
                                for (var j = -1; j <= 1; j++)
                                    if (i == 0 && j == 0)
                                        continue;
                                    else
                                        neighbors += at(x + i, y + j);

                            dataB[x, y] = data[x, y];

                            if (neighbors < 2) dataB[x, y] = 0;
                            if (neighbors == 3) dataB[x, y] = 1;
                            if (neighbors > 3) dataB[x, y] = 0;

                            rectangles[x, y].Fill = dataB[x, y] == 0 ? new SolidColorBrush(new Color()) : Brushes.Black;
                        }
                    }

                    data = dataB;
                };

            var timer = new DispatcherTimer();

            timer.Tick += (s, e) => macroStep();

            timer.Start();
        }
    }
}

它是这样的:

如果我将new SolidColorBrush(new Color()) 替换为Brushes.White,程序运行速度会慢得多。为什么?

我正在使用 2010 Express 在 Windows 7 64 位上进行测试。

【问题讨论】:

  • 为了缩小是渲染还是检索问题,如果您在循环之外保留对画笔的缓存引用并使用该引用代替Brushes.White,速度会受到影响吗?系统画笔缓存在Dictionary&lt;uint, SolidColorBrush&gt; 中,每次检索项目时都必须将其锁定。
  • 你和Brushes.Transparent有同样的行为吗?
  • @SimonMcKenzie 好建议。但是,我尝试了它,它没有任何区别。 (顺便说一句,祝贺 MapSnap;非常酷的 WP7 应用!)
  • 谢谢!你必须试一试;)虽然我不知道,但我想知道 WPF 是否会并行一些渲染,如果所有项目共享相同的画笔,这可能会受到影响。如果在循环外创建新的画笔实例,性能一样吗?
  • @Rachel Brushes.Transparent 也很慢。有趣的是,它一开始并不像Brushes.White 那样慢,但经过大约 5 次迭代后,它变得慢得多。

标签: c# wpf conways-game-of-life brushes


【解决方案1】:

使用Brushes.White 并没有什么特别之处。

如果您在 macroStep 事件处理程序之外定义自己的本地画笔,然后冻结它,它的行为将与使用 Brushes.White 完全相同。如果你不先冻结它,它的表现会差很多很多。

最佳性能是在每次调用宏步骤开始时,在循环之前创建一次画笔,然后将其冻结。如果您在最里面的循环中创建一个新画笔,则会显着降低速度。

此外,如果您为表现不佳的代码增加计时器的时间间隔,它实际上会解决性能问题。我的猜测是,每次完成渲染后,都会在后台线程上进行某种资源清理,这与画笔的内部结构有关,但它无法进行清理,因为你正在右转并在下一次迭代中使用画笔。为了证明这一点,我创建了一个画笔池,并且每次都使用不同的画笔:

SolidColorBrush[] brushes = new SolidColorBrush[2];
for (int i = 0; i < brushes.Length; i++)
{
    var brush = new SolidColorBrush(new Color());
    brush.Freeze();
    brushes[i] = brush;
}
int brushIx = 0;

Action macroStep = () =>
{
    dataB = new int[100, 100];
    var brush = brushes[brushIx++ % brushes.Length];
...
    rectangles[x, y].Fill = dataB[x, y] == 0
        ? brush
        : Brushes.Black;
    data = dataB;
};

如果您将画笔数量设置为 1,这将产生与使用 Brushes.White 相同的行为。但如果您将其设置为 2 或更多,您将获得您期望的性能。

【讨论】:

  • 感谢布莱斯的建议。你是否在你的系统上测试过这段代码,比较不同的变化?在我的系统上,在内部循环中创建一个新画笔仍然比在macroStep 外部或内部创建和冻结画笔更快。这很奇怪。
  • @dharmatech,我刚刚测试了所有三个案例 - 您的原始案例是 Bryce 建议的“new SolidColorBrush(new Color())” - 使用冻结的画笔阵列,而您的原始案例只是刷子。白色。在我的机器上,最快的是第二个 - 带有一系列画笔(这并不奇怪)。最慢的是最后一个 - 使用 Brushes.White。原始的“new SolidColorBrush(new Color())”的情况在中间的某个地方。可能值得添加获胜者和第二名之间的差异,尽管它很明显,但没有第二名和第三名之间的差异那么大。
【解决方案2】:

因为new Color() 的 alpha 值为零,这意味着 WPF 不必渲染它,因为它是完全透明的 - 另一方面,白色的 alpha 是 255,这意味着它必须是完全纯白色渲染。

【讨论】:

  • 好建议 Jaska。但如果是这样的话,那么这将同样缓慢:new SolidColorBrush(new Color() { R = 255, G = 255, B = 255, A = 255 })。然而,事实并非如此。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-18
  • 2013-04-16
  • 2011-10-04
  • 2012-02-06
相关资源
最近更新 更多