C++에서 GDI +를 사용하여 차트 컨트롤을 그립니다. 위의 2 가지 기능간에 성능 차이가 있는지 알고 싶습니다. 난 DrawLines()에 대한 코드를 작성하는 게으른 아니지만 그렇게하면 내 코드가 복잡해집니다. 따라서 가독성을 떨어 뜨리고 잠재적으로 오류와 버그를 증가시키면서 코드 실행 속도를 빠르게할지 여부를 결정해야합니다.DrawLine과 DrawLines의 성능 차이는 무엇입니까?
도움을 주시면 감사하겠습니다. Eraj.
C++에서 GDI +를 사용하여 차트 컨트롤을 그립니다. 위의 2 가지 기능간에 성능 차이가 있는지 알고 싶습니다. 난 DrawLines()에 대한 코드를 작성하는 게으른 아니지만 그렇게하면 내 코드가 복잡해집니다. 따라서 가독성을 떨어 뜨리고 잠재적으로 오류와 버그를 증가시키면서 코드 실행 속도를 빠르게할지 여부를 결정해야합니다.DrawLine과 DrawLines의 성능 차이는 무엇입니까?
도움을 주시면 감사하겠습니다. Eraj.
대부분의 그리기 작업에서이 둘 사이에 큰 차이는 없어야하지만, 필자는 그 차이점을 비교하기위한 테스트 프로젝트를 작성했습니다 (실제로는 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);
}
안녕하세요 meklarian, 귀하의 회신은 매우 유용합니다. 나는 ppl이 자신의 경험을 바탕으로 아이디어를 제출할 것을 기대하고있었습니다. Bt u는 실제로 출력을 테스트하는 코드를 작성했습니다. 고맙습니다. 귀하의 연구 결과는 내가해야 할 일을 이해하는 데 매우 도움이됩니다. 드로잉 반복의 표시에 관해서는, 내 차트 ctrl은 시리즈 당 최소 10000 포인트를 지원해야하므로 타이밍이 유효해야합니다. – OverTheEdge
문제 없으니 기꺼이 도와주세요. 여기서 샘플의 성능을 더 높일 수는 있지만 상대적인 성능은 동일하게 유지되어야합니다. 또한 비 표시 HDC/Graphics 컨텍스트 (비트 맵 또는 다른 오프 스크린 버퍼)로 그리는 경우 타이밍이 크게 변경 될 수 있습니다. 이 샘플 스 니펫에 문제가 있으면 알려주십시오. – meklarian
왜 오프 스크린 버퍼에 그릴 때 GDI + 렌더링 성능에 영향을 줍니까? 깜박임을 피하기 위해이 작업을 수행합니다 (mem 비트 맵에 그려서 비트 블릿을 그립니다). 순간에 나는 GDI +에서 벗어날 수있는 최고의 성능을 짜내려고 노력하고 있습니다. 나는 심지어 내 드로잉 기능을 멀티 스레딩하려했지만 그 도움이 도움이된다. 나는 도울 수 없지만 Im은 smthing을 잘못한다고 생각하여 GDI + 성능을 향상시키는 데 도움이되는 정보를 매우 높이 평가합니다. – OverTheEdge