2009-12-02 6 views
4

C++에서 GDI +를 사용하여 차트 컨트롤을 그립니다. 위의 2 가지 기능간에 성능 차이가 있는지 알고 싶습니다. 난 DrawLines()에 대한 코드를 작성하는 게으른 아니지만 그렇게하면 내 코드가 복잡해집니다. 따라서 가독성을 떨어 뜨리고 잠재적으로 오류와 버그를 증가시키면서 코드 실행 속도를 빠르게할지 여부를 결정해야합니다.DrawLine과 DrawLines의 성능 차이는 무엇입니까?

도움을 주시면 감사하겠습니다. Eraj.

답변

6

대부분의 그리기 작업에서이 둘 사이에 큰 차이는 없어야하지만, 필자는 그 차이점을 비교하기위한 테스트 프로젝트를 작성했습니다 (실제로는 3 개).

내 컴퓨터에서 매우 많은 행 (x25000)의 경우 DrawLines() (640ms)가 DrawLine() (420ms)보다 약 50 % 빠릅니다. 솔직히 여기에서 나는 처음으로 질문을 잘못 읽고 C#에서 초기 테스트를 작성했습니다. .NET 그래픽은 GDI +를 기반으로하기 때문에 성능은 두 가지 사이에서 거의 동일합니다.

호기심에서 벗어나 나는 규칙적인 GDI를 시도했다. Win32 PolyLine (530ms) 기능을 사용하면 약 45 %의 속도로 약 20 % 빨라졌습니다. 이 속도는 GDI + DrawLines()를 사용하는 것보다 116 % 빠릅니다. 더 놀라운 것은 아마도 GDI + DrawLine() 대신 win32 LineTo()를 사용하면 125ms 이하의 시간이 걸릴 것입니다. 125ms 및 45000 라인의 가정 시간으로,이 방법은 적어도 800 %보다 빠릅니다. (타이머 해상도와 스레드 타이밍이 어려운 QueryPerformanceCounter에 의지하고 높은 주파수의 다른 타이밍 방법없이이 임계 값의 성능을 측정 할 수 있습니다.)

그러나, 나는이 상당한 병목 현상에가 있다는 가정을 만들기에주의해야 드로잉 코드. 수행 할 수있는 많은 성능 향상은 어떤 객체를 그려야하는지와 아무 관련이 없습니다. 아마도 귀하의 요구 사항에 따라 귀하의 통제를 위해 정상적인 작동 과정에서 수백 가지 항목을 그려야 할 수도 있습니다. 이 경우 디버깅 드로잉 문제는 많은 시간과 비용을 들이고 나머지 컨트롤이나 애플리케이션을 개선하는 데는 도움이되지 않을 수 있으므로 드로잉 코드를 최대한 간단하고 버그없이 작성하는 것이 좋습니다.

또한 수천 개의 항목을 적극적으로 업데이트해야하는 경우 백 버퍼 솔루션으로 이동하면 훨씬 더 높은 성능을 얻을 수 있습니다. 또한 오프 스크린 버퍼를 관리하는 것 외에도 컨트롤을 그리는 코드를 쉽게 개발할 수 있어야합니다.

다음은 내 소스 코드 예제입니다. 각각 마우스 클릭을 처리하여 대량 드로잉과 항목 별 드로잉을 번갈아 사용합니다. 베어 MFC SDI 응용 프로그램

이 호스팅

GDI +는, 누군가가 이미/해체 된 GDI +를 초기화 GDI + 헤더 및 작성된 코드를 선언했다고 가정합니다. ChildView.cpp의에서는 된 PreCreateWindow()

m_bCompositeMode 첨가

// Attributes 
public: 
    bool m_bCompositeMode; 

// Operations 
public: 
    void RedrawScene(Graphics &g, int lineCount, int width, int height); 
    PointF *CreatePoints(int lineCount, int width, int height); 
    void ReportTime(Graphics &g, int lineCount, DWORD tickSpan); 

public: 
    afx_msg void OnLButtonUp(UINT nFlags, CPoint point); 

ChildView.h에있어서

= 거짓;

OnPaint() 및 메시지 맵 변경을 포함하여 ChildView.cpp의 나머지 부분입니다.당신이 두 가지를 비교하는 경우 기본 Form1 클래스

과 더불어, basebones 윈폼 응용 프로그램에서 호스팅

BEGIN_MESSAGE_MAP(CChildView, CWnd) 
    ON_WM_PAINT() 
    ON_WM_LBUTTONUP() 
END_MESSAGE_MAP() 

void CChildView::OnPaint() 
{ 
    CPaintDC dc(this); // device context for painting 
    RECT rcClient; 
    ::GetClientRect(this->GetSafeHwnd(), &rcClient); 

    Graphics g(dc.GetSafeHdc()); 
    g.Clear(Color(0, 0, 0)); 

    RedrawScene(g, 25000, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); 
} 

void CChildView::RedrawScene(Graphics &g, int lineCount, int width, int height) 
{ 
    DWORD tickStart = 0; 
    DWORD tickEnd = 0; 

    Pen p(Color(0, 0, 0x7F)); 
    PointF *pts = CreatePoints(lineCount, width, height); 
    tickStart = GetTickCount(); 
    if (m_bCompositeMode) 
    { 
     g.DrawLines(&p, pts, lineCount); 
    } 
    else 
    { 
     int i = 0; 
     int imax = lineCount - 1; 
     for (i = 0; i < imax; i++) 
     { 
      g.DrawLine(&p, pts[i], pts[i + 1]); 
     } 
    } 
    tickEnd = GetTickCount(); 
    delete[] pts; 
    ReportTime(g, lineCount, tickEnd - tickStart); 
} 

void CChildView::ReportTime(Graphics &g, int lineCount, DWORD tickSpan) 
{ 
    CString strDisp; 
    if(m_bCompositeMode) 
    { 
    strDisp.Format(_T("Graphics::DrawLines(Pen *, PointF *, INT) x%d took %dms"), lineCount, tickSpan); 
    } 
    else 
    { 
    strDisp.Format(_T("Graphics::DrawLine(Pen *, PointF, PointF) x%d took %dms"), lineCount, tickSpan); 
    } 

    // Note: sloppy, but simple. 
    Font font(L"Arial", 14.0f); 
    PointF ptOrigin(0.0f, 0.0f); 
    SolidBrush br(Color(255, 255, 255)); 
    Status s = g.DrawString(strDisp, -1, &font, ptOrigin, &br); 
} 

PointF* CChildView::CreatePoints(int lineCount, int width, int height) 
{ 
    if(lineCount <= 0) 
    { 
    PointF *ptEmpty = new PointF[2]; 
    ptEmpty[0].X = 0; 
    ptEmpty[0].Y = 0; 
    ptEmpty[1].X = 0; 
    ptEmpty[1].Y = 0; 
    return ptEmpty; 
    } 

    PointF *pts = new PointF[lineCount + 1]; 
    int i = 1; 
    while(i < lineCount) 
    { 
    pts[i].X = (float)(rand() % width); 
    pts[i].Y = (float)(rand() % height); 
    i++; 
    } 
    return pts; 
} 

void CChildView::OnLButtonUp(UINT nFlags, CPoint point) 
{ 
    m_bCompositeMode = !m_bCompositeMode; 
    this->Invalidate(); 

    CWnd::OnLButtonUp(nFlags, point); 
} 

C# .NET을은 MFC 버전의 크기와 동일한 형태의 기본 크기를 설정 . 크기 변경 처리기도 추가 할 수 있습니다. 단지와 같이 ChildView.cpp의

업데이트 된 PreCreateWindow (에서 ChildView.h에

// Attributes 
public: 
    bool m_bCompositeMode; 

// Operations 
public: 
    void RedrawScene(HDC hdc, int lineCount, int width, int height); 
    POINT *CreatePoints(int lineCount, int width, int height); 
    void ReportTime(HDC hdc, int lineCount, DWORD tickSpan); 

public: 
    afx_msg void OnLButtonUp(UINT nFlags, CPoint point); 

에서 앱

베어 MFC SDI에서 호스팅

public Form1() 
{ 
    InitializeComponent(); 
    bCompositeMode = false; 
} 

bool bCompositeMode; 

private void Form1_Paint(object sender, PaintEventArgs e) 
{ 
    e.Graphics.Clear(Color.Black); 
    RedrawScene(e.Graphics, 25000, this.ClientRectangle.Width, this.ClientRectangle.Height); 
} 

private void RedrawScene(Graphics g, int lineCount, int width, int height) 
{ 
    DateTime dtStart = DateTime.MinValue; 
    DateTime dtEnd = DateTime.MinValue; 
    using (Pen p = new Pen(Color.Navy)) 
    { 
     Point[] pts = CreatePoints(lineCount, width, height); 
     dtStart = DateTime.Now; 
     if (bCompositeMode) 
     { 
      g.DrawLines(p, pts); 
     } 
     else 
     { 
      int i = 0; 
      int imax = pts.Length - 1; 
      for (i = 0; i < imax; i++) 
      { 
       g.DrawLine(p, pts[i], pts[i + 1]); 
      } 
     } 
     dtEnd = DateTime.Now; 
    } 
    ReportTime(g, lineCount, dtEnd - dtStart); 
} 

private void ReportTime(Graphics g, int lineCount, TimeSpan ts) 
{ 
    string strDisp = null; 
    if (bCompositeMode) 
    { 
     strDisp = string.Format("DrawLines(Pen, Point[]) x{0} took {1}ms", lineCount, ts.Milliseconds); 
    } 
    else 
    { 
     strDisp = string.Format("DrawLine(Pen, Point, Point) x{0} took {1}ms", lineCount, ts.Milliseconds); 
    } 

    // Note: sloppy, but simple. 
    using (Font font = new Font(FontFamily.GenericSansSerif, 14.0f, FontStyle.Regular)) 
    { 
     g.DrawString(strDisp, font, Brushes.White, 0.0f, 0.0f); 
    } 
} 

private Point[] CreatePoints(int count, int width, int height) 
{ 
    Random rnd = new Random(); 
    if (count <= 0) { return new Point[] { new Point(0,0), new Point(0,0)}; } 
    Point[] pts = new Point[count + 1]; 
    pts[0] = new Point(0, 0); 
    int i = 1; 
    while (i <= count) 
    { 
     pts[i] = new Point(rnd.Next(width), rnd.Next(height)); 
     i++; 
    } 
    return pts; 
} 

private void Form1_Click(object sender, EventArgs e) 
{ 
    bCompositeMode = !bCompositeMode; 
    Invalidate(); 
} 

정기 GDI) GDI + 샘플.

BEGIN_MESSAGE_MAP(CChildView, CWnd) 
    ON_WM_PAINT() 
    ON_WM_LBUTTONUP() 
END_MESSAGE_MAP() 

void CChildView::OnPaint() 
{ 
    CPaintDC dc(this); // device context for painting 
    HDC hdc = dc.GetSafeHdc(); 

    HBRUSH brClear = (HBRUSH)::GetStockObject(BLACK_BRUSH); 
    RECT rcClient; 
    ::GetClientRect(this->m_hWnd, &rcClient); 
    ::FillRect(hdc, &rcClient, brClear); 
    ::DeleteObject(brClear); 

    RedrawScene(hdc, 45000, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top); 
} 

void CChildView::RedrawScene(HDC hdc, int lineCount, int width, int height) 
{ 
    DWORD tickStart = 0; 
    DWORD tickEnd = 0; 

    HPEN p = ::CreatePen(PS_SOLID, 1, RGB(0, 0, 0x7F)); 
    POINT *pts = CreatePoints(lineCount, width, height); 
    HGDIOBJ prevPen = SelectObject(hdc, p); 
    tickStart = GetTickCount(); 
    if(m_bCompositeMode) 
    { 
    ::Polyline(hdc, pts, lineCount); 
    } 
    else 
    { 
    ::MoveToEx(hdc, pts[0].x, pts[0].y, &(pts[0])); 
    int i = 0; 
    int imax = lineCount; 
    for(i = 1; i < imax; i++) 
    { 
     ::LineTo(hdc, pts[i].x, pts[i].y); 
    } 
    } 
    tickEnd = GetTickCount(); 
    ::SelectObject(hdc, prevPen); 
    delete pts; 
    ::DeleteObject(p); 

    ReportTime(hdc, lineCount, tickEnd - tickStart); 
} 

POINT *CChildView::CreatePoints(int lineCount, int width, int height) 
{ 
    if(lineCount <= 0) 
    { 
    POINT *ptEmpty = new POINT[2]; 
    memset(&ptEmpty, 0, sizeof(POINT) * 2); 
    return ptEmpty; 
    } 

    POINT *pts = new POINT[lineCount + 1]; 
    int i = 1; 
    while(i < lineCount) 
    { 
    pts[i].x = rand() % width; 
    pts[i].y = rand() % height; 
    i++; 
    } 
    return pts; 
} 

void CChildView::ReportTime(HDC hdc, int lineCount, DWORD tickSpan) 
{ 
    CString strDisp; 
    if(m_bCompositeMode) 
    { 
    strDisp.Format(_T("PolyLine(HDC, POINT *, int) x%d took %dms"), lineCount, tickSpan); 
    } 
    else 
    { 
    strDisp.Format(_T("LineTo(HDC, HPEN, int, int) x%d took %dms"), lineCount, tickSpan); 
    } 

    HFONT font = (HFONT)::GetStockObject(SYSTEM_FONT); 
    HFONT fontPrev = (HFONT)::SelectObject(hdc, font); 

    RECT rcClient; 
    ::GetClientRect(this->m_hWnd, &rcClient); 
    ::ExtTextOut(hdc, 0, 0, ETO_CLIPPED, &rcClient, strDisp.GetString(), strDisp.GetLength(), NULL); 
    ::SelectObject(hdc, fontPrev); 
    ::DeleteObject(font); 
} 

void CChildView::OnLButtonUp(UINT nFlags, CPoint point) 
{ 
    m_bCompositeMode = !m_bCompositeMode; 
    this->Invalidate(); 

    CWnd::OnLButtonUp(nFlags, point); 
} 
+0

안녕하세요 meklarian, 귀하의 회신은 매우 유용합니다. 나는 ppl이 자신의 경험을 바탕으로 아이디어를 제출할 것을 기대하고있었습니다. Bt u는 실제로 출력을 테스트하는 코드를 작성했습니다. 고맙습니다. 귀하의 연구 결과는 내가해야 할 일을 이해하는 데 매우 도움이됩니다. 드로잉 반복의 표시에 관해서는, 내 차트 ctrl은 시리즈 당 최소 10000 포인트를 지원해야하므로 타이밍이 유효해야합니다. – OverTheEdge

+1

문제 없으니 기꺼이 도와주세요. 여기서 샘플의 성능을 더 높일 수는 있지만 상대적인 성능은 동일하게 유지되어야합니다. 또한 비 표시 HDC/Graphics 컨텍스트 (비트 맵 또는 다른 오프 스크린 버퍼)로 그리는 경우 타이밍이 크게 변경 될 수 있습니다. 이 샘플 스 니펫에 문제가 있으면 알려주십시오. – meklarian

+0

왜 오프 스크린 버퍼에 그릴 때 GDI + 렌더링 성능에 영향을 줍니까? 깜박임을 피하기 위해이 작업을 수행합니다 (mem 비트 맵에 그려서 비트 블릿을 그립니다). 순간에 나는 GDI +에서 벗어날 수있는 최고의 성능을 짜내려고 노력하고 있습니다. 나는 심지어 내 드로잉 기능을 멀티 스레딩하려했지만 그 도움이 도움이된다. 나는 도울 수 없지만 Im은 smthing을 잘못한다고 생각하여 GDI + 성능을 향상시키는 데 도움이되는 정보를 매우 높이 평가합니다. – OverTheEdge

관련 문제