2011-05-09 31 views
4

私は、グラフの左端から右端にグラフを描画するためのさまざまな方法を試していました。今までは、OKを実行するポリラインを使用したキャンバスを使用していましたが、まだ改善がありました。DrawingContext.DrawLineパフォーマンスの問題

私がDrawingContext.DrawLineを試したとき、私は信じられないほど悪いパフォーマンスを経験しましたが、なぜそれを理解できません。これは、それが問題を示して、私が思い付くことができる最も凝縮コードです:

<StackPanel> 
    <l:TestControl Height="16"/> 
    <!-- copy+paste the above line a few times --> 
</StackPanel> 

今すぐウィンドウのサイズを変更:中TestControlsの数に応じて、

public class TestControl : Control { 

    static Pen pen = new Pen(Brushes.Gray, 1.0); 
    static Random rnd = new Random(); 

    protected override void OnRender(DrawingContext drawingContext) { 

     var previousPoint = new Point(0, 0); 

     for (int x = 4; x < this.ActualWidth; x += 4) { 
      var newPoint = new Point(x, rnd.Next((int)this.ActualHeight)); 
      drawingContext.DrawLine(pen, previousPoint, newPoint); 
      previousPoint = newPoint; 
     } 
    } 
} 

そしてMainWindow.xamlはこれを含んでいますStackPanel私はVSの "Stop Debugger"ボタンを押しても目に見えない遅延(10個のコントロール)または30秒間の停止(100個のコントロール)を経験します...

私はこれについてはかなり混乱していますが、明らかに私は間違ったことをしていますが、コード私はそれが何であるかを見ていないので、シンプルです... 重要な場合には、.Net4を使用しています。

答えて

6

ペンを凍結することでパフォーマンスを得ることができます。あなたはSolid(デフォルト)以外のダッシュスタイルでペンを使用している場合、WPFに描画

static TestControl() 
{ 
    pen.Freeze(); 
} 
+0

ありがとうMarat、ちょっと前に気付いたよ:) – wilford

0

私の推測では、rnd.Next(...)への呼び出しがそれぞれのレンダリングに多くのオーバーヘッドを引き起こしていると思います。定数を指定してテストし、速度を比較することができます。

実際にレンダリングするたびに新しい座標を生成する必要がありますか?

+0

ランダム。次の時間は無駄です。私は私のマシンで150nsのようなものを測定しました。 – Jens

+0

はい、ジェンスが言ったように、ランダム。次は無害です。実際のアプリケーションでは、座標は乱数ではなく、すべてのレンダリングを変更するものではありません。これは単なる例です。 – wilford

+0

Charles Petzoldの記事(Dougによる)を読んでください:100Kのデータポイントが減速する/アプリケーションを壊すことを示しています。アプリケーションの幅が500ピクセルで、コントロールが100個の場合、データポイントは50Kです。リサイズすると余分な歪み(再描画がたくさんあります)素晴らしいメッセージではありませんが、ここでは限界に達しているようです。 –

0

WPFでグラフを描画する最も効率的な方法は、DrawingVisualを使用することです。

チャールズ・ペゾルドは、MSDNマガジンでそれを行う方法を説明する優れた記事を書いた:

Foundations: Writing More Efficient ItmesControls

技術は数千のデータポイントを表示するために働きます。

+0

ありがとう、私はすでにこの(非常に良い)記事を知っていて、ソースコードを使いこなしています。バリアントの1つでは、DrawingContext.DrawEllipseをループ内で100000回の反復で使用し、それでも正常に動作します。彼はペンを使わず、代わりにヌルを渡します。ペンを使用すると、彼のサンプルの演奏も本当に消えてしまいます。 DrawLineの場合、私は1つを使用して回避することはできません。 – wilford

1

もう一度遊んでみてください。フリーズペンに大きな影響がありました。今度は次のようにコンストラクタにペンを作成します。

public TestControl() { 
    if (pen == null) { 
     pen = new Pen(Brushes.Gray, 1.0); 
     pen.Freeze(); 
    } 
} 

パフォーマンスは今のところ私が期待しています。私はそれが何か単純なものでなければならないことを知っていた...

+0

本当ですか?私のテストでは何もしません...しかし、スタティックではなく非公開にしています。 –

+0

私のサンプル**のペンは**プライベート(および静的)です。私は可視性が何とか変わるとは思わないが、何千もの代わりに1つのインスタンスがある。あなたのテストで違いが見られないのは奇妙です。私のマシンでは、この小さな変更で、再描画の動作は完全に受け入れられない状態からリアルタイムになります。 – wilford

+0

ああ、そうです。私は静的なものを削除し、それを私的にしか作らなかった。プライベートで静的(凍結しない)にすると、パフォーマンスが大幅に低下します。 –

0

が非常に遅くなります。これは、ペンを受け入れるDrawingContextのすべてのdrawメソッド(DrawLineDrawGeometryなど)

0

に影響を与えます。この質問は本当に古いですが、私はDrawingContext.DrawLineのaswellを使用し、私のコードの実行を改善する方法を発見しました。

これは、1時間前の曲線を描くために私のコードだった:ここ

DrawingVisual dv = new DrawingVisual(); 
DrawingContext dc = dv.RenderOpen(); 

foreach (SerieVM serieVm in _curve.Series) { 
    Pen seriePen = new Pen(serieVm.Stroke, 1.0); 
    Point lastDrawnPoint = new Point(); 
    bool firstPoint = true; 
    foreach (CurveValuePointVM pointVm in serieVm.Points.Cast<CurveValuePointVM>()) { 
     if (pointVm.XValue < xMin || pointVm.XValue > xMax) continue; 

     double x = basePoint.X + (pointVm.XValue - xMin) * xSizePerValue; 
     double y = basePoint.Y - (pointVm.Value - yMin) * ySizePerValue; 
     Point coord = new Point(x, y); 

     if (firstPoint) { 
      firstPoint = false; 
     } else { 
      dc.DrawLine(seriePen, lastDrawnPoint, coord); 
     } 

     lastDrawnPoint = coord; 
    } 
} 

dc.Close(); 

は、コードは以下のようになります。

DrawingVisual dv = new DrawingVisual(); 
DrawingContext dc = dv.RenderOpen(); 

foreach (SerieVM serieVm in _curve.Series) { 
    StreamGeometry g = new StreamGeometry(); 
    StreamGeometryContext sgc = g.Open(); 

    Pen seriePen = new Pen(serieVm.Stroke, 1.0); 
    bool firstPoint = true; 
    foreach (CurveValuePointVM pointVm in serieVm.Points.Cast<CurveValuePointVM>()) { 
     if (pointVm.XValue < xMin || pointVm.XValue > xMax) continue; 

     double x = basePoint.X + (pointVm.XValue - xMin) * xSizePerValue; 
     double y = basePoint.Y - (pointVm.Value - yMin) * ySizePerValue; 
     Point coord = new Point(x, y); 

     if (firstPoint) { 
      firstPoint = false; 
      sgc.BeginFigure(coord, false, false); 
     } else { 
      sgc.LineTo(coord, true, false); 
     } 
    } 

    sgc.Close(); 
    dc.DrawGeometry(null, seriePen, g); 
} 

dc.Close(); 

古いコードが3000の二つの曲線をプロットするために〜140ミリ秒かかるだろうポイント。新しいものは約5msかかる。 StreamGeometryの使用は、DrawingContext.Drawlineよりはるかに効率的であるようです。

編集:私はdotnetフレームワークのバージョン3.5を使用しています

+0

私は、あなたのペンとStreamGeometryをフリーズすることでさらにパフォーマンスを圧迫できると確信しています! – wilford

関連する問題