背景介绍
笔者之前接到一个需求,需要在WPF上实时显示病人实时的生理信号(心电图等)。团队开发,需求很快做完了(Unit test 效果图如下)
但是后来发布到产品上发现,资源占用比本机的要大。本地监控后,发现随着时间推移内存和Page Faults 都在增长,如果在产品上长期(几个月甚至更长时间)运行可能会导致性能问题。那么就要做性能优化了。
Page Faults
MSDN:The number of times that data had to be retrieved from disk for this process because it was not found in memory. This value is accumulated from the time the process is started.
Page faults/sec is the rate of page faults over time.
Page faults:不能说明什么问题,但是证明内存读写频率较高
A memory page fault occurs when requested data is not found in memory. The system generates a fault, which normally indicates that the system looks for data in the paging file. In this circumstance, however, the missing data is identified as being located within an area of memory that cannot be paged out to disk. The system faults, but cannot find, the data and is unable to recover. Faulty hardware, a buggy system service, antivirus software, and a corrupted NTFS volume can all generate this type of error.
当在内存中找不到请求的数据时,会发生内存页错误。系统生成一个故障,通常表示系统在分页文件中查找数据。然而,在这种情况下,丢失的数据被标识为位于无法分页到磁盘的内存区域内。系统出现故障,但无法找到数据,无法恢复。有故障的硬件、有缺陷的系统服务、防病毒软件和损坏的NTFS卷都会产生此类错误。
性能优化
初始方案:我们最开始是用WPF内置的Polyline来绘图的。
查看MSDN 后发现Polyline是比较冗余的,WPF中图像渲染是Visual 类提供的。
WPF主要类型的职责
Visual: Provides rendering support in WPF, which includes hit testing, coordinate transformation, and bounding box calculations.
UIElement: 在Visual基础上 主要增加了可以响应用户输入(包括通过处理事件路由或命令路由控制输入的发送位置)以及可以引发通过元素树传递路由的路由事件。
更详细内容可以查看MSDN ,就不一一列举了。
在绘制生理信号这个case中 只需要图形可以实时渲染到container中即可,只需要Visual类型提供的功能。
改进后方案
发现了DrawingVisual 这个类型更加的轻量级,没有Polyline 类型继承的冗余属性。
优化Demo:文章来源:https://www.toymoban.com/news/detail-442810.html
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
namespace MVVMDemoTest.DrawVisual
{
// Create a host visual derived from the FrameworkElement class.
// This class provides layout, event handling, and container support for
// the child visual objects.
public class MyVisualHost : FrameworkElement
{
// Create a collection of child visual objects.
private VisualCollection _children;
public MyVisualHost()
{
_children = new VisualCollection(this);
_children.Add(CreateDrawingVisualRectangle());
//_children.Add(CreateDrawingVisualText());
//_children.Add(CreateDrawingVisualEllipses());
// Add the event handler for MouseLeftButtonUp.
this.MouseLeftButtonUp += new System.Windows.Input.MouseButtonEventHandler(MyVisualHost_MouseLeftButtonUp);
}
// Create a DrawingVisual that contains a rectangle.
private DrawingVisual CreateDrawingVisualRectangle()
{
DrawingVisual drawingVisual = new DrawingVisual();
// Retrieve the DrawingContext in order to create new drawing content.
DrawingContext drawingContext = drawingVisual.RenderOpen();
// Create a rectangle and draw it in the DrawingContext.
Rect rect = new Rect(new System.Windows.Point(160, 100), new System.Windows.Size(320, 80));
drawingContext.DrawLine(new Pen(Brushes.Yellow, 2), new Point(100,100),new Point(300,100) );
drawingContext.DrawRectangle(Brushes.LightBlue, (System.Windows.Media.Pen)null, rect);
drawingContext.DrawText(new FormattedText(
"this is a test",
CultureInfo.GetCultureInfo("en-us"),
FlowDirection.LeftToRight,
new Typeface("Verdana"),
32,
Brushes.Black),new Point(200,200));
var StreamGeometry = new StreamGeometry();
StreamGeometry.Clear();
var points = new List<Point>()
{
new Point(20,20),
new Point(40,3),
new Point(60,200),
new Point(0,0)
};
using (var ctx = StreamGeometry.Open())
{
ctx.BeginFigure(new Point(0,0), false, false);
ctx.PolyLineTo(points, true, false);
}
drawingContext.DrawGeometry(Brushes.Yellow,new Pen(Brushes.Black,2), StreamGeometry);
// Persist the drawing content.
drawingContext.Close();
return drawingVisual;
}
// Provide a required override for the VisualChildrenCount property.
protected override int VisualChildrenCount
{
get { return _children.Count; }
}
// Provide a required override for the GetVisualChild method.
protected override Visual GetVisualChild(int index)
{
if (index < 0 || index >= _children.Count)
{
throw new ArgumentOutOfRangeException();
}
return _children[index];
}
// Capture the mouse event and hit test the coordinate point value against
// the child visual objects.
void MyVisualHost_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// Retrieve the coordinates of the mouse button event.
System.Windows.Point pt = e.GetPosition((UIElement)sender);
// Initiate the hit test by setting up a hit test result callback method.
VisualTreeHelper.HitTest(this, null, new HitTestResultCallback(myCallback), new PointHitTestParameters(pt));
}
// If a child visual object is hit, toggle its opacity to visually indicate a hit.
public HitTestResultBehavior myCallback(HitTestResult result)
{
if (result.VisualHit.GetType() == typeof(DrawingVisual))
{
if (((DrawingVisual)result.VisualHit).Opacity == 1.0)
{
((DrawingVisual)result.VisualHit).Opacity = 0.4;
}
else
{
((DrawingVisual)result.VisualHit).Opacity = 1.0;
}
}
// Stop the hit test enumeration of objects in the visual tree.
return HitTestResultBehavior.Stop;
}
}
}
总结
WPF提供的功能丰富强大,但是由于提供的功能过多,导致有一定的性能问题,这是已知。因此我们在设计和使用WPF的时候,一定要尽可能使用轻量级的模块,必要时,可以自己继承的方式完成。文章来源地址https://www.toymoban.com/news/detail-442810.html
到了这里,关于WPF 性能优化-高刷新绘图的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!