2013-09-28 9 views
0

내 파형 차트의 렌더링 성능을 향상시킬 수있는 방법을 찾고 있습니다. (내가 가능한 생각만큼) 현재 내가 일상적인 렌더링 최적화 된 GDI 기반을 사용하고 있습니다 :많은 양의 선 그리기.

Private Sub Calculate2(ByVal aData()() As Double) 
    'aData size: 1000 traces with 200k points each -> Dim aData(1000, 200000) 

    'Some data preparations doing roughly the same as they would in the real app 
    Dim PS_Y As Double = 1 
    Dim Origin As PointF = New PointF(Rnd() * 100, Rnd() * 100) 
    PS_Y = Rnd() + 0.1 
    Dim Data(), ST As Double 
    Dim lPoints As New List(Of PointF) 
    Dim PS_X As Double = Rnd() + 0.1 

    'Graphics initialisation 
    Dim Img As New Bitmap(900, 600) 
    Dim ImgGR As Graphics = Graphics.FromImage(Img) 
    ImgGR.Clear(Color.White) 
    Dim WFPen As New Pen(Brushes.Black, 1) 

    'Cache property values for faster access: 
    Dim l As Integer = 100 'ChartRect.Left 
    Dim r As Integer = 1000 'ChartRect.Right 

    'Process trace by trace: 
    For i = 0 To aData.Length - 1 
     ST = Rnd() 'x distance of the points 
     Data = aData(i) 'y values, 1 per x value 

     If Data.Length = 0 Then Continue For 

     'scale precalculations, first & last displayed points: 
     Dim ScaleX As Double = ST * PS_X 
     Dim OrigX As Single = Origin.X 

     Dim iStart As Integer = (l - OrigX)/ScaleX 
     Dim iEnd As Integer = (r - OrigX)/ScaleX 
     If iStart < 0 Then iStart = 0 
     If iEnd < 0 Then iEnd = 0 
     If iEnd > Data.Length - 1 Then iEnd = Data.Length - 1 
     If iStart > Data.Length - 1 Then iStart = Data.Length - 1 

     'Make sure that for benchmarking purposes all points are displayed, next 2 lines do not exist in real code: 
     iStart = 0 
     iEnd = Data.Length - 1 

     If iEnd < iStart Then Continue For 

     'point calculations using the pecalculated values: 
     Dim APT(iEnd - iStart) As PointF 
     For j = iStart To iEnd 
      APT(j - iStart) = Origin + New SizeF(j * ScaleX, -(Data(j) * PS_Y)) 
     Next 



     ImgGR.DrawLines(WFPen, APT) 
     'Commenting out this line reduces the time needed for executing this whole routine from 42.4s to 4.76s 
     'Hence most of the time spent even with all the scaling is still in rendering the spline. 
    Next 

내가 Direct2D의와 접근 방식을 시도하지만 GDI에서 "DrawLines"방법보다 훨씬 느렸다 :

'Imports D2D = Microsoft.WindowsAPICodePack.DirectX.Direct2D1 
'Imports DX = Microsoft.WindowsAPICodePack.DirectX 
Dim TGT As D2D.RenderTarget 
Private Sub initd2d() 
    Dim fac As D2D.D2DFactory = D2D.D2DFactory.CreateFactory(Microsoft.WindowsAPICodePack.DirectX.Direct2D1.D2DFactoryType.SingleThreaded) 

    Dim imgf As DX.WindowsImagingComponent.ImagingFactory 
    imgf = DX.WindowsImagingComponent.ImagingFactory.Create 

    'Dim pf As New D2D.PixelFormat(DX.Graphics.Format.B8G8R8A8UNorm, D2D.AlphaMode.Ignore) 
    Dim pf As New D2D.PixelFormat(DX.Graphics.Format.Unknown, D2D.AlphaMode.Unknown) 

    Dim bmp As DX.WindowsImagingComponent.ImagingBitmap 
    bmp = imgf.CreateImagingBitmap(CUInt(900), CUInt(600), DX.WindowsImagingComponent.PixelFormats.Pbgra32Bpp, DX.WindowsImagingComponent.BitmapCreateCacheOption.CacheOnLoad) 

    Dim rtp As New D2D.RenderTargetProperties(D2D.RenderTargetType.Default, pf, 0, 0, D2D.RenderTargetUsages.None, Microsoft.WindowsAPICodePack.DirectX.Direct3D.FeatureLevel.Default) 
    TGT = fac.CreateWicBitmapRenderTarget(bmp, rtp) 


    TGT.Clear(New D2D.ColorF(Color.White.ToArgb)) 
End Sub 

'104,7s execution time: 
Private Sub drawd2d() 
    Dim p1 As New D2D.Point2F(1, 10.5) 
    Dim p2 As New D2D.Point2F(1.01, 10) 
    Dim b As D2D.Brush = TGT.CreateSolidColorBrush(New D2D.ColorF(0, 0, 255)) 
    TGT.BeginDraw() 

    For i = 0 To 200000 * 1000 
     TGT.DrawLine(p1, p2, b, 1) 
    Next 
End Sub 

데이터 크기가이 응용 프로그램에서 일반적으로 사용되므로 제발 내가 필요한 이유를 묻지 마십시오.

또한 데이터를 생성하는 응용 프로그램이 대략 3 초 내에 50M 포인트로 4 개의 추적을 렌더링하는 것처럼 관리하기 때문에 훨씬 빠르게 표시 할 수 있어야합니다. 이는 대략 동일한 데이터입니다.

누군가 비슷한 것을 한 적이 있다면 올바른 방향으로 나를 가리킬 수 있다면 매우 감사 할 것입니다. 또는 가능한 경우 나에게 PointF-Array 또는 유사한 구조를 렌더링 할 수있는 대안을 제공하십시오. 비트 맵.

편집 : 나머지 프로그램을로드 할 필요없이 원본 소프트웨어와 동일한 계산을 수행하기위한 벤치마킹 루틴입니다. Data()() 배열은 소프트웨어에 의해 동적으로 생성되므로 치수를 확인하고 조치해야합니다.

데이터로드 및 그림 표시 기능, 그리드 및 문제와 관련없는 다른 코드와 함께 정리 기능이 제거되었습니다.

EDIT2 : 데이터 생성 루틴을 포함 코드 샘플 :

Sub Main() 
    Dim T As New HiResTimer 
    Dim StartTime, StopTime As Long 

    'initd2d() 

    PrepareData(10, 200000) 



    StartTime = T.Value 
    For i = 1 To 1 
     'drawd2d() 
     Calculate2(100, 0, 200000) 
    Next 
    StopTime = T.Value 

    Dim Elapsed As Double = (StopTime - StartTime)/T.Frequency 
    Debug.Print("Time: " & Elapsed) 

End Sub 

Dim aData()() As Double 
Private Sub PrepareData(ByVal WaveformCount As Integer, ByVal Length As Integer) 
    Dim Offset As Double = 0 
    Dim Amplitude As Double = 100 

    Dim SineCount As Double = 4 
    Dim SineBase As Double = 2 * Math.PI/Length * SineCount 

    ReDim aData(WaveformCount - 1) 
    For i = 0 To WaveformCount - 1 
     ReDim aData(i)(Length - 1) 

     For j = 0 To Length - 1 
      aData(i)(j) = Amplitude * Math.Sin(SineBase * j) + Offset + Rnd() * Amplitude * 0.05 
     Next 
    Next 
End Sub 

Private Sub Calculate2(ByVal AmplitudeUsed As Double, ByVal OffsetUsed As Double, ByVal LengthUsed As Integer) 
    Dim PS_Y As Double 

    'Instead of making this random, here a real calculation for the scale (chartheight/biggest waveform amplitude) : 
    PS_Y = 600/(AmplitudeUsed * 2 + AmplitudeUsed * 0.1) ' Rnd() + 0.1 

    'Since our calculation method oscillates around zero with the same amplitude we can predict that we need the following offset: 
    Dim Origin As PointF = New PointF(0, 300) 


    Dim Data(), ST As Double 
    Dim lPoints As New List(Of PointF) 

    'set the x axis scale to make our waveform fit exactly: 
    Dim PS_X As Double = 900/LengthUsed 

    Dim Img As New Bitmap(900, 600) 
    Dim ImgGR As Graphics = Graphics.FromImage(Img) 
    ImgGR.Clear(Color.White) 
    Dim WFPen As New Pen(Brushes.Black, 1) 

    'theese 2 values simply define an area in the picture where the waveforms are actually visible to not overlap with the axis/legend, set it to something that makes sense 
    Dim l As Integer = 20 'ChartRect.Left 
    Dim r As Integer = 700 'ChartRect.Right 

    For i = 0 To aData.Length - 1 
     'Set sampletime to 1 second to keep the predefined scale from above, but still do the calculation as it would be needed with real data: 
     ST = 1 ' Rnd() 
     Data = aData(i) 

     If Data.Length = 0 Then Continue For 

     Dim ScaleX As Double = ST * PS_X 
     Dim OrigX As Single = Origin.X 

     Dim iStart As Integer = (l - OrigX)/ScaleX 
     Dim iEnd As Integer = (r - OrigX)/ScaleX 
     If iStart < 0 Then iStart = 0 
     If iEnd < 0 Then iEnd = 0 
     If iEnd > Data.Length - 1 Then iEnd = Data.Length - 1 
     If iStart > Data.Length - 1 Then iStart = Data.Length - 1 

     iStart = 0 
     iEnd = Data.Length - 1 

     If iEnd < iStart Then Continue For 

     Dim APT(iEnd - iStart) As PointF 
     For j = iStart To iEnd 
      APT(j - iStart) = Origin + New SizeF(j * ScaleX, -(Data(j) * PS_Y)) 
     Next 



     ImgGR.DrawLines(WFPen, APT) 

    Next 


    PictureBox1.Image = Img 
End Sub 
+0

혼란스러운 코드. 난 당신이 거기에 많은 불필요한 코드를 가지고 있다고 생각하고, 당신은'Data' 값을주고'Length'를 확인합니다 - 코드를 단순화하는 작업. 또한 당신은 그래픽 객체 인'Pens','Graphics' 등을 처분하지 않습니다. – OneFineDay

+0

@DonA 코드는 benchmaring 목적으로 만 사용합니다. 세부 사항을 편집하십시오. – Chris

+0

50M = 50,000,000 포인트입니까? 얼마나 많은 선들을 시각화 할 수 있습니까? – OneFineDay

답변

0

자동으로 그래프를 최적화하는 코드 블록, 그것은 많은 작은 파형을 위해 도움이되지 않습니다,하지만이 10,000에 기사와 규모가 큰 작동합니다

Private Function Calculate3(ByVal aData()() As Double, ByVal AmplitudeUsed As Double, ByVal OffsetUsed As Double, ByVal LengthUsed As Integer) As Bitmap 
    Dim PS_Y As Double 

    'Instead of making this random, here a real calculation for the scale (chartheight/biggest waveform amplitude) : 
    PS_Y = 600/(AmplitudeUsed * 2 + AmplitudeUsed * 0.1) ' Rnd() + 0.1 

    'Since our calculation method oscillates around zero with the same amplitude we can predict that we need the following offset: 
    Dim Origin As PointF = New PointF(0, 300) 


    Dim Data(), ST As Double 
    Dim lPoints As New List(Of PointF) 

    'set the x axis scale to make our waveform fit exactly: 
    Dim PS_X As Double = 900/LengthUsed 

    Dim Img As New Bitmap(900, 600) 
    Dim ImgGR As Graphics = Graphics.FromImage(Img) 
    ImgGR.Clear(Color.White) 
    Dim WFPen As New Pen(Brushes.Black, 1) 

    'theese 2 values simply define an area in the picture where the waveforms are actually visible to not overlap with the axis/legend, set it to something that makes sense 
    Dim l As Integer = 20 'ChartRect.Left 
    Dim r As Integer = 700 'ChartRect.Right 

    Dim APT() As PointF 
    For i = 0 To aData.Length - 1 
     Dim iStart As Integer 
     Dim iEnd As Integer 

     Dim ScaleX As Double 
     Dim OrigX As Single 

     'Set sampletime to 1 second to keep the predefined scale from above, but still do the calculation as it would be needed with real data: 
     ST = 1 ' Rnd() 
     Data = aData(i) 

     If Data.Length = 0 Then Continue For 

     ScaleX = ST * PS_X 
     OrigX = Origin.X 

     iStart = (l - OrigX)/ScaleX 
     iEnd = (r - OrigX)/ScaleX 
     If iStart < 0 Then iStart = 0 
     If iEnd < 0 Then iEnd = 0 
     If iEnd > Data.Length - 1 Then iEnd = Data.Length - 1 
     If iStart > Data.Length - 1 Then iStart = Data.Length - 1 

     iStart = 0 
     iEnd = Data.Length - 1 

     If iEnd < iStart Then Continue For 

     If ScaleX < 0.3 Then 'more than 3 lines per point, summarize 
      Dim iPT As Integer 

      Dim FirstX As Integer 
      Dim LastX As Integer 

      Dim MinY As Single 
      Dim MaxY As Single 
      Dim tVal As Single 

      Dim iSt As Integer 
      Dim iEn As Integer 


      FirstX = Math.Truncate(iStart * ScaleX) 
      LastX = Math.Ceiling(iEnd * ScaleX) 

      ReDim APT((LastX - FirstX) * 2 - 1) 
      For iX = FirstX To LastX - 1 
       MinY = Single.MaxValue 
       MaxY = Single.MinValue 

       iSt = Math.Truncate(iX/ScaleX) 
       iEn = Math.Truncate((iX + 1)/ScaleX) - 1 
       If iSt < 0 Then iSt = 0 
       If iEn > Data.Length - 1 Then iEn = Data.Length - 1 

       For iDat = iSt To iEn 
        tVal = -Data(iDat) * PS_Y 
        If tVal > MaxY Then MaxY = tVal 
        If tVal < MinY Then MinY = tVal 
       Next 

       iPT = (iX - FirstX) * 2 
       APT(iPT) = Origin + New SizeF(iX, MinY) 
       APT(iPT + 1) = Origin + New SizeF(iX, MaxY) 
      Next 


     Else 
      ReDim APT(iEnd - iStart) 
      For j = iStart To iEnd 
       APT(j - iStart) = Origin + New SizeF(j * ScaleX, -(Data(j) * PS_Y)) 
      Next 

     End If 




     ImgGR.DrawLines(WFPen, APT) 
    Next 


    Return Img 
End Function 

읽을 시간을내어 주셔서 모두 감사를 내 질문

0

당신은 점 사이의 거리를 확인하여 속도 및 이전 점에 너무 가까이있는 사람을 건너 뛸 수 있습니다.

거리 계산에서 sqrt를 건너 뛰고 필요한 경우 다른 최적화를 수행 할 수 있습니다.

이것이 유익한 지 알아 보려면 n/2 점을 사용하여 벤치 마크를 실행하십시오. 여기

+0

비슷한 작업을하고 있는데, 1 x 픽셀 이내의 모든 점을 1 줄로 요약했습니다. 이제는 한 번 추적하면 약 3 배 길어질 것입니다. [점] x 축 크기 [픽셀]보다 큽니다. 많은 수의 파형에는 도움이되지 않지만 실제 앱에는 필요한만큼 충분히 클 것 같습니다. 내가 최적에 도달하면 최적화를 게시 할 것입니다. – Chris