泥庭

2015年4月11日

[WPF]描画パフォーマンスのお話(Polylineの場合)

Filed under: C#, performance, WPF — タグ: , — yone64 @ 11:06 PM
WPFの描画速度には、いつも悩ませてもらってます。
最近、Polylineで描画している際にちょっと不思議(?)な現象に出会ったので、一応メモ程度に。

さて、1つの大きなPolylineと、そのPolylineをいくつか(例えば100個程度)に分割したものとどちらの描画コストが小さいか。
分割すると余分にオーバーヘッド的コストがかかりそうな気がするので、1つのオブジェクトのほうが早い気がするのですが、そうではないようです。
うーむ、理由がわからん。描画ってマルチスレッドで処理されてる?
<Window x:Class="WpfApplication40.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <StackPanel Orientation="Vertical">
            <TextBox x:Name="TextBox"/>
            <TextBlock x:Name="TextBlock" />
            <Button Content="Button" Height="23" Width="75" Click="ButtonBase_OnClick"/>
        </StackPanel>
        <Viewbox Grid.Column="1" Stretch="Fill">
            <ItemsControl Height="2000" Width="5000" ItemsSource="{Binding}">
                <ItemsControl.ItemsPanel>
                    <ItemsPanelTemplate>
                        <Canvas />
                    </ItemsPanelTemplate>
                </ItemsControl.ItemsPanel>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Path Data="{Binding}" Stroke="Black" StrokeThickness="1"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Viewbox>
    </Grid>
</Window>
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using System.Windows.Threading;

namespace WpfApplication40
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.ButtonBase_OnClick(null, null);

            var timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromMilliseconds(0.1);
            timer.Tick += (sender, args) =>
            {
                var now = DateTime.Now;
                this.TextBlock.Text = now.ToString("HHmmss.fff");
            };
            timer.Start();
        }

        private const int PointCount = 100000;

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            int count;
            count = int.TryParse(this.TextBox.Text, out count) ? count : 1;
            count = Math.Max(count, 1);

            var seed = new Random().NextDouble();

            Task.Run(() =>
            {
                var points =
                    Enumerable.Range(0, PointCount)
                              .Select(
                                      i =>
                                          new Point(i / (PointCount / 5000.0), Math.Sin(i / 50.0 + seed) * 1000 + 1000))
                              .ToList();

                return Enumerable.Range(0, count).Select(i =>
                {
                    var p = points.Skip(i * points.Count / count).Take(points.Count / count).ToList();
                    var geo = new StreamGeometry();
                    using (var context = geo.Open())
                    {
                        context.BeginFigure(p[0], false, false);
                        context.PolyLineTo(p.Skip(1).ToList(), true, true);
                    }
                    geo.Freeze();
                    return geo;
                }).ToList();
            }).ContinueWith(t =>
            {
                this.DataContext = t.Result;
            }, TaskScheduler.FromCurrentSynchronizationContext());

        }

    }
}
実行していただくと、TextBoxがあるので、分割数を入れてボタンをクリックしましょう。(入力されたものが非整数の場合は1になります。)
描画が止まっている間は時計も進まないので、おおよそのUIが固まっている時間がわかります。
Windowを最大化とかする際などは、Polylineが1つの場合と100個ぐらいの場合では大きく違ってくることがわかると思います。
どうしてなんでしょうね。何故なんでしょうね。何かご存知の方と、ドキュメントを募集しています。あと、うちの環境だとこうだったってのも歓迎。
# あと、こういう場合の描画時間の計測方法も知りたい。

【追記】上記コードはメモリリーク(リソースリーク?)します。いないとは思いますが、本番環境でご使用の際はご注意ください。
(参照)バックグラウンドスレッドでUI要素を作るともっと問題は深刻かもしれない。(WPF)
広告

コメントする »

まだコメントはありません。

RSS feed for comments on this post. TrackBack URI

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

WordPress.com で無料サイトやブログを作成.

%d人のブロガーが「いいね」をつけました。