2013-05-20 11 views
8

Di seguito è una (molto ingenua) implementazione di Conway's Game of Life in WPF. E 'solo una demo ...Brushes.White rallenta la demo grafica in basso

XAML: codice

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

dietro:

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

Ecco come si presenta:

enter image description here

Se sostituisco new SolidColorBrush(new Color()) con Brushes.White il programma viene eseguito molto più lentamente. Perché?

Sto testando su Windows 7 64-bit utilizzando 2010 Express.

+2

Per restringere se si tratta di un problema di rendering o di recupero, se si mantiene un riferimento memorizzato nella cache al di fuori del loop e si utilizza tale riferimento al posto di 'Brushes.White', la velocità viene influenzata? I pennelli di sistema sono memorizzati nella cache in un 'dizionario ', che deve essere bloccato ogni volta che un oggetto viene recuperato. –

+2

Hai lo stesso comportamento con 'Brushes.Transparent'? – Rachel

+1

@SimonMcKenzie Buon suggerimento. Tuttavia, l'ho provato e non fa differenza. (A proposito, congratulazioni per MapSnap, fantastica app WP7!) – dharmatech

risposta

1

Perché new Color() ha un valore alfa pari a zero, il che significa che WPF non deve renderlo perché è completamente trasparente - d'altra parte l'alfa del colore bianco è 255, il che significa che è completamente di colore bianco che deve essere reso .

+0

Buon consiglio Jaska. Ma se fosse così, allora sarebbe altrettanto lento: 'nuovo SolidColorBrush (new Color() {R = 255, G = 255, B = 255, A = 255})'. Tuttavia, non lo è. – dharmatech

0

Non c'è niente di speciale nell'uso di Brushes.White.

Se si definisce il proprio pennello locale al di fuori del gestore di eventi macroStep e quindi si blocca, si comporterà esattamente identico all'utilizzo di Brushes.White. Se non lo si congela per primo, si comporterà molto, molto peggio.

La migliore prestazione è creare il pennello una volta all'inizio di ogni chiamata a macroStep, prima del ciclo, e quindi bloccarlo. È un rallentamento significativo se si crea un nuovo pennello all'interno del ciclo più interno.

Inoltre, se si aumenta l'intervallo sul timer per il codice che si comporta male, in realtà verrà risolto il problema di prestazioni. La mia ipotesi è che ci sia un qualche tipo di pulizia delle risorse che si verificherebbe su un thread in background dopo che ha finito il rendering ogni volta, che è legato alla parte interna del pennello, ma è affamato dall'essere in grado di fare la pulizia perché stai girando a destra e usando il pennello nella prossima iterazione. Per dimostrare questo, ho creato un pool di pennelli, e utilizzare un pennello diverso ogni volta:

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; 
}; 

Se si imposta il numero di spazzole a 1, questo darà lo stesso comportamento con Brushes.White. Ma se lo imposti su 2 o più, otterrai le prestazioni che ti aspetti.

+0

Grazie per il suggerimento Bryce. Hai testato questo codice sul tuo sistema, confrontando le diverse varianti? Sul mio sistema, la creazione di un nuovo pennello nel loop interno è ancora più veloce della creazione e del congelamento di un pennello, all'esterno o all'interno di 'macroStep'. È molto bizzarro – dharmatech

+1

@dharmatech, ho appena testato tutti e tre i casi: il tuo originale con "nuovo SolidColorBrush (nuovo colore())", suggerito da Bryce, con array di pennelli congelati e di nuovo originale con solo Pennelli. Bianco. E sulla mia macchina il più veloce è stato il secondo - con matrici di spazzole (nessuna sorpresa qui). Il più lento è stato l'ultimo - con Brushes.White. E il caso con "nuovo SolidColorBrush originale (nuovo colore())" era da qualche parte nel mezzo. Può valere la pena aggiungere la differenza tra il vincitore e il secondo sebbene fosse evidente, ma non tanto quanto tra il secondo e il terzo. – Sevenate