

WinForms 라인 차트에 1억 개의 데이터 포인트를 어떻게 표시할까요? 답은 전적으로 어떤 차트 라이브러리를 선택하느냐에 달려 있습니다 — 그리고 거의 모든 비교가 거꾸로 다루는 한 가지 사실에 달려 있습니다. 올바른 엔진에서 WinForms는 WPF보다 느리지 않습니다. 더 빠릅니다.
이 글은 개발자가 실제로 평가하는 WinForms 차트 라이브러리 — ProEssentials, LightningChart, DevExpress, Syncfusion, ComponentOne, ScottPlot — 에서 1억 포인트를 렌더링하는 완전하고 실행 가능한 C# 코드를 제공합니다. 각 단계에서 데이터에 무슨 일이 일어나는지, 그리고 동일한 ProEssentials 엔진이 WinForms에서 WPF보다 약 5% 빠르게 렌더링하는 이유를 보여줍니다.
"plot millions of points WinForms", "fastest WinForms chart", 또는 "WinForms chart 100 million points"를 검색했다면, 이것이 코드 수준의 구체적인 답입니다 — 모든 수치를 직접 재현할 수 있는 clone-and-run GitHub 저장소와 함께.
직접 해보세요: WinForms 1억 포인트 데모는 공개 저장소입니다 — 클론하고 F5를 눌러 1억 포인트가 hDC에 렌더링되는 것을 확인하세요. 계정도, 트라이얼 등록도, 격자형 대체 데이터도 없습니다.
Building on WinUI 3? See the WinUI edition of this 100M-point comparison
Reproduce the WinForms numbers yourself: clone, build, run the WinForms 100M-point chart demo on GitHub.Compare against the WPF build: clone, build, run the WPF 100M-point chart demo on GitHub.
코드에 앞서, 모든 것을 다시 보게 만드는 아키텍처적 요점입니다. WPF의 GPU 차트는 Direct3D를 화면에 직접 그릴 수 없습니다 — WPF 컴포지터가 픽셀을 소유하므로, 라이브러리는 오프스크린 텍스처에 렌더링하고, D3DImage를 통해 WPF에 넘긴 뒤, 컴포지터가 합성하도록 해야 합니다. 모든 프레임이 텍스처 복사와 컴포지터 동기화 비용을 지불합니다.
네이티브 WinForms 컨트롤은 실제 Win32 창 핸들과 디바이스 컨텍스트(hDC)를 소유합니다. ProEssentials는 Direct3D를 그 hDC에 직접 결합합니다 — Compute Shader로 구성된 프레임이 render-to-texture 없이, 두 번째 합성 없이 창에 곧바로 표시됩니다. 대표적인 데이터셋 전반에서 이는 네이티브 WinForms 경로를 동일한 엔진이 구동하는 WPF 컨트롤보다 end-to-end로 약 5% 빠르게 만듭니다.
동일한 Compute Shader, 동일한 zero-copy 데이터 경로, 동일한 on-demand 모델. WinForms는 타협 인터페이스가 아닙니다 — WPF 컴포지터를 완전히 건너뛰기 때문에 빠른 인터페이스입니다.
모든 라이브러리는 동일한 테스트를 받습니다. 네이티브 WinForms 폼에서 1억 개의 순차적 float 값으로 단일 시리즈 라인 차트를 렌더링하는 것입니다. 소스 배열을 넘어선 메모리 오버헤드, 렌더 시간, 그리고 라이브러리가 모든 포인트를 보여주는지 아니면 다운샘플링된 근사치를 보여주는지를 측정합니다.
| Parameter | Value |
|---|---|
| Data points | 100,000,000 (100 million) |
| Data type | float (4 bytes per value) — 400 MB raw data |
| Chart type | 2-D line chart, single series, sequential X-axis |
| Pattern | Sine wave with random noise (realistic signal data) |
| Platform | WinForms (.NET 8), Windows 11, mid-range GPU |
| What we measure | Lines of code, memory overhead, render time, data fidelity |
아래에서 각 라이브러리의 완전한 C# 코드를, 각 단계에서 데이터에 무슨 일이 일어나는지 주석과 함께 보여줍니다. 무엇보다 한 가지에 주목하세요. 1억 값이 복사되는지, 변환되는지, 객체로 래핑되는지 — 그리고 차트가 창에 직접 표시되는지 아니면 interop 레이어를 거치는지입니다.
ProEssentials는 UseDataAtLocation()을 사용하여 WinForms 차트가 기존 float 배열을 직접 가리키게 합니다. 복사도, 변환도, 객체 래핑도 없습니다. Direct3D Compute Shader가 1억 개의 모든 정점을 GPU에서 구성하고 프레임을 컨트롤의 hDC에 직접 표시합니다.
// ProEssentials — Plot 100 Million Points (WinForms, ~15 lines)
// NuGet: Install-Package ProEssentials.Chart.Net.Winforms
// 1. Allocate your data — this is the ONLY copy that will ever exist
float[] yData = new float[100_000_000];
for (int i = 0; i < yData.Length; i++)
yData[i] = (float)Math.Sin(i * 0.0001) + (float)(rand.NextDouble() * 0.1);
// 2. Configure the WinForms chart control (PesgoWin)
pesgo1.PeData.Subsets = 1;
pesgo1.PeData.Points = 100_000_000;
pesgo1.PePlot.Method = SGraphPlottingMethod.Line;
pesgo1.PeConfigure.RenderEngine = RenderEngine.Direct3D;
pesgo1.PeData.ComputeShader = true;
pesgo1.PeData.Filter2D3D = true; // GPU pre-filter shader — lossless min/max
// 3. Zero-copy: point the chart at your existing array — no duplication
pesgo1.PeData.Y.UseDataAtLocation(yData, yData.Length);
// 4. Tell the Direct3D engine to rebuild vertices and colors
pesgo1.PeFunction.Force3dxVerticeRebuild = true;
pesgo1.PeFunction.Force3dxNewColors = true;
// 5. Render — presented DIRECTLY to the WinForms window hDC
// (no render-to-texture, no WPF compositor hop)
pesgo1.PeFunction.ReinitializeResetImage();핵심 라인은 UseDataAtLocation(yData, yData.Length)입니다. 이는 복사 작업이 아닙니다 — 포인터 할당입니다. 데이터가 이미 차지하는 400 MB가 존재할 유일한 복사본이며, 차트는 사실상 제로 오버헤드를 더합니다.
Force3dxVerticeRebuild와 Force3dxNewColors 플래그는 Direct3D 엔진에 정점과 색상을 GPU에서 다시 구성하도록 지시합니다. ComputeShader = true와 Filter2D3D = true를 사용하면 GPU 사전 필터가 픽셀 열당 무손실 min/max를 계산하여 어떤 스파이크도 손실되지 않으며, 그 결과는 WPF의 컴포지터 단계 없이 hDC에 표시됩니다.
~15줄의 C#. ~0 MB 메모리 오버헤드. ~15 ms 렌더 시간으로, WPF의 동일 코드보다 ~5% 빠름. GPU Compute Shader를 통한 무손실 min/max 필터링, WinForms hDC에 직접 표시.
SciChart는 흔히 가장 빠른 WPF 차트로 언급되지만, WinForms에는 어떤 코드 샘플로도 우회할 수 없는 함정이 있습니다. SciChart에는 네이티브 WinForms 컨트롤이 없습니다. 자체 문서는 WinForms가 WPF와의 통합을 통해서만 지원된다고 명시합니다 — WPF SciChartSurface를 Microsoft ElementHost 안에 호스팅하는 방식입니다.
// SciChart — Plot 100 Million Points (WinForms)
// NOTE: SciChart has NO native WinForms control.
// WinForms is supported only by hosting the WPF control in an ElementHost.
// 1. Host the WPF SciChartSurface inside a WinForms ElementHost
var host = new System.Windows.Forms.Integration.ElementHost();
host.Dock = DockStyle.Fill;
var sciChartSurface = new SciChart.Charting.Visuals.SciChartSurface();
host.Child = sciChartSurface; // WPF control living inside WinForms
this.Controls.Add(host);
// 2. From here the code is identical to the WPF path — and inherits
// WPF's render-to-texture compositor cost plus ElementHost interop
// limitations (mouse events, focus, z-order / "airspace").
var dataSeries = new XyDataSeries<double, double>(); // double[], not float[]
dataSeries.Append(xData, yData); // full internal copy
var lineSeries = new FastLineRenderableSeries {
DataSeries = dataSeries,
ResamplingMode = ResamplingMode.Auto // downsamples to ~viewport width
};
sciChartSurface.RenderableSeries.Add(lineSeries);
// There is no native WinForms SciChart control to target.이는 SciChart WinForms 앱이 WPF의 render-to-texture 컴포지터 경로와, 마우스 이벤트·포커스·Z-순서와 관련된 문서화된 ElementHost interop 제약을 물려받는다는 의미입니다. 1억 포인트 코드는 사실상 래핑된 WPF 코드이며, 네이티브 WinForms가 피하는 컴포지터 비용을 떠안습니다.
진정한 네이티브 WinForms 애플리케이션에는 일급 SciChart 경로가 없습니다. 이 분야의 대표적 성능 이름이 네이티브 WinForms 항목을 제공하지 않으며, 그래서 여기서는 코드 샘플이 아닌 interop로 표시됩니다.
LightningChart는 가장 강력한 진정한 네이티브 WinForms 경쟁자입니다. 특수 샘플 시리즈를 사용하여 DirectX 파이프라인으로 1억 포인트를 렌더링하며, AddSamples 방식의 호출을 통해 float[]를 내부 저장소로 복사합니다.
// LightningChart — Plot 100 Million Points (WinForms)
// NuGet: Install-Package LightningChart.NET (native WinForms control)
// 1. Allocate your data
float[] yData = new float[100_000_000];
for (int i = 0; i < yData.Length; i++)
yData[i] = (float)Math.Sin(i * 0.0001) + (float)(rand.NextDouble() * 0.1);
// 2. Configure the native WinForms chart for large data
lightningChart.BeginUpdate();
lightningChart.ViewXY.XAxes[0].SetRange(0, 100_000_000);
// 3. SampleDataSeries (fixed-interval) — copies float[] into internal buffer
var series = new SampleDataSeries(lightningChart.ViewXY,
lightningChart.ViewXY.XAxes[0], lightningChart.ViewXY.YAxes[0]);
series.FirstSampleTimeStamp = 0;
series.SamplingFrequency = 1;
// 4. Add all 100M samples — array copy (not zero-copy)
series.AddSamples(yData, false);
// 5. Add series; continuous DirectX render loop keeps the GPU active
lightningChart.ViewXY.SampleDataSeries.Add(series);
lightningChart.EndUpdate();(올바른 시리즈 유형으로) 1억 포인트 전부를 무손실로 렌더링하며, 이는 진정한 강점입니다. 절충점은 더 넓은 비교와 일치합니다. 연속 DirectX 렌더 루프는 아무것도 변하지 않아도 GPU를 활성 상태로 유지하며, 데이터 경로는 배열을 참조하는 대신 복사합니다.
LightningChart는 순수 역량 면에서 가장 가까운 네이티브 WinForms 경쟁자입니다. 차이는 배열 복사, 연속 루프 전력 소비, 활성화/라이선싱으로 귀결됩니다 — 포인트를 렌더링할 수 있는지 여부가 아닙니다.
DevExpress는 흥미로운 반전입니다. 빠른 대용량 데이터 뷰인 SwiftPlotSeriesView가 WinForms 전용이며 — WPF ChartControl에는 존재하지 않습니다. 따라서 WinForms는 실제로 대용량 데이터에 대한 DevExpress의 더 강력한 표면입니다.
// DevExpress — Plot 100 Million Points (WinForms)
// NuGet: DevExpress.Win.Charts
// NOTE: SwiftPlotSeriesView is WinForms-ONLY — it does not exist in WPF.
// 1. Create data model — DevExpress uses object-per-point
public class DataPoint {
public double Argument { get; set; }
public double Value { get; set; }
}
// 2. Allocate the objects (object-per-point is the binding constraint)
var data = new List<DataPoint>(100_000_000);
for (int i = 0; i < 100_000_000; i++)
data.Add(new DataPoint {
Argument = i,
Value = Math.Sin(i * 0.0001) + rand.NextDouble() * 0.1
});
// ⚠ Billions of bytes of DataPoint objects before rendering starts.
// DevExpress markets 20M+ "without preprocessing" — short of 100M.
// 3. SwiftPlotDiagram + SwiftPlotSeriesView (the WinForms fast path)
var series = new Series("Signal", ViewType.SwiftPlot);
series.DataSource = data;
series.ArgumentDataMember = "Argument";
series.ValueDataMembers.AddRange("Value");
chartControl.Series.Add(series);
((SwiftPlotDiagram)chartControl.Diagram).EnableAxisXScrolling = true;SwiftPlot은 경량화 생성 알고리즘과 포인트당 객체 데이터 모델을 사용합니다. 1억 포인트에서는 렌더링이 시작되기 전에 엄청난 수의 SeriesPoint 객체를 할당해야 하며, 실제로 이것이 렌더러보다 훨씬 먼저 바인딩 제약이 됩니다. DevExpress는 전처리 없이 2천만+ 포인트를 내세우는데, 이는 1억에 한참 못 미칩니다.
Syncfusion의 간판 "Fast Series"(FastLineSeries, FastLineBitmapSeries)는 WPF, WinUI, UWP에서만 존재합니다. WinForms에는 fast-series 경로가 전혀 없습니다 — Syncfusion 자체 지원팀이 기능 요청을 등록했으나 즉각적인 구현 계획은 없다고 밝혔습니다.
그 결과 WinForms에는 GPU 가속 없는 표준 GDI+ 렌더링만 남습니다. 1억 포인트에서는 실용적인 시나리오가 아닙니다. Syncfusion WinForms는 ~10만 포인트 미만의 비즈니스 대시보드에는 강력한 선택이지만, 대규모 과학적 수집용은 아닙니다.
ScottPlot은 지배적인 무료/오픈소스 WinForms 옵션입니다. CPU에서 System.Drawing을 거쳐 SkiaSharp로 렌더링하며, 반응성을 유지하기 위해 데이터 데시메이션 — 데이터셋을 대표적인 시각적 포인트 부분집합으로 축소 — 에 의존합니다.
// ScottPlot — Plot 100 Million Points (WinForms, free / open-source)
// NuGet: Install-Package ScottPlot.WinForms
// 1. Allocate your data
double[] yData = new double[100_000_000];
for (int i = 0; i < yData.Length; i++)
yData[i] = Math.Sin(i * 0.0001) + rand.NextDouble() * 0.1;
// 2. SignalPlot is the large-data path (assumes even X spacing)
formsPlot1.Plot.Add.Signal(yData);
// 3. ScottPlot renders on the CPU (System.Drawing -> SkiaSharp) and
// relies on DECIMATION — it draws a representative subset, not all
// 100M points. Init for 10M+ runs 100+ ms; the displayed line is
// not lossless.
formsPlot1.Refresh();1천만+ 포인트의 경우 초기화가 100+ ms 걸리며, 표시되는 결과는 무손실이 아니라 데시메이션됩니다. ScottPlot 메인테이너는 포인트 배열의 매니지드-네이티브 마샬링이 근본적인 한계라고 언급합니다. 적당한 데이터에는 뛰어난 가치를 제공하지만, 무손실 1억 엔진은 아닙니다.
각 라이브러리가 네이티브 WinForms 폼에서 1억 개 float 값을 플로팅하려 할 때 일어나는 일입니다:
| Factor | ProEssentials | SciChart | LightningChart | DevExpress | Syncfusion | ScottPlot |
|---|---|---|---|---|---|---|
| Native WinForms? | ✅ Yes | ❌ ElementHost | ✅ Yes | ✅ Yes | ✅ Yes | ✅ Yes |
| Plots 100M? | ✅ Lossless | ⚠️ via WPF, resampled | ✅ Lossless | ⚠️ object-per-point | ❌ No fast path | ⚠️ Decimated |
| Data loading | Zero-copy pointer | Copy into double[] | Array copy | Object-per-point | GDI+ iteration | Array (managed↔native) |
| Library overhead | ~0 MB | ~800 MB | ~400 MB | ~2,400 MB+ | — | ~copy |
| Render time | ~15 ms (GPU) | via WPF | Fast | Fast (lightened) | Slow (GDI+) | 100+ ms init |
| Data fidelity | 100% lossless | Resampled | 100% | Feature-reduced | Lossless but slow | Decimated |
| GPU rendering | Compute shaders | (WPF) DirectX | DirectX | GDI+/DirectX | None | CPU |
| Present path | Direct to hDC | WPF compositor | Direct | Direct | Direct | Direct |
| vs WPF (same engine) | ~5% faster | — | — | — | — | — |
WinForms에서는 두 가지 아키텍처적 사실이 복합됩니다. 첫째, ProEssentials가 UseDataAtLocation()을 호출하면 배열을 복사하는 대신 포인터를 저장합니다 — 제로 메모리 오버헤드. 둘째, 완성된 프레임이 WPF의 텍스처 복사 및 합성 오버헤드 없이 hDC에 직접 표시됩니다.
이점은 연쇄적입니다. 로딩 시간은 즉각적입니다(1억 값을 반복하는 대신 포인터 할당). 메모리는 원래의 400 MB로 평탄하게 유지됩니다. 그리고 표시 경로가 WPF보다 짧으며, 여기서 ~5%의 end-to-end 우위가 나옵니다 — 동일한 엔진에서 컴포지터를 뺀 것입니다.
복사하거나(LightningChart의 AddSamples), 변환하거나(double[] 저장), 래핑하는(포인트당 객체) 경쟁자는 값당 메모리를 배가합니다. 1억 포인트에서 이 배수들은 평탄한 400 MB와 기가바이트의 차이 — 또는 차트가 그려지기도 전의 OutOfMemoryException — 입니다.
| Data Model | Overhead (100M pts) | Used By |
|---|---|---|
| Zero-copy pointer | ~0 MB | ProEssentials |
| Array copy (float) | ~400 MB | LightningChart |
| Array copy (double) | ~800 MB | SciChart (via WPF) |
| Object-per-point | ~2,400 MB+ | DevExpress |
| CPU decimation | copy + subset | ScottPlot |
1억 float × 4바이트 = 400 MB(당신의 데이터). ProEssentials는 ~0 MB를 더하고 hDC에 표시합니다. 동일 엔진의 WPF 버전은 프레임당 텍스처 복사 + 컴포지터 동기화를 더하는데 — 이것이 바로 네이티브 WinForms 경로가 절약하는 ~5%입니다.
Telerik, ScottPlot, 그리고 DevExpress의 경량화 뷰는 모두 어떤 형태로든 데이터를 축소합니다 — 모든 포인트가 아닌 대표적 부분집합을 표시 — 대규모에서 반응성을 유지하기 위해서입니다. 추세 개요에는 흔히 시각적으로 무방합니다.
하지만 데시메이션된 차트는 단일 샘플 이벤트를 숨길 수 있습니다. ECG 부정맥, 진동 공진 스파이크, 마이크로초 단위 반도체 이상 등입니다. 부분집합이 그 샘플을 우연히 포함하지 않으면 결코 보지 못합니다. 수백만 포인트를 평균화하여 "처리"한다는 것은 당신의 데이터가 아니라 데이터의 요약을 렌더링한다는 뜻입니다.
ProEssentials는 Filter2D3D GPU Compute Shader로 다른 접근을 취합니다. 포인트를 버리는 대신 1억 값 전체에 걸쳐 픽셀 열당 올바른 min/max를 계산하므로 모든 스파이크가 보존됩니다 — hDC에서 무손실로.
네이티브 WinForms 옵션 중 ProEssentials와 LightningChart만 현실적으로 1억을 무손실 렌더링합니다. DevExpress는 포인트당 객체로 더 낮은 한계를 보이고, SciChart는 네이티브 컨트롤이 없으며, Syncfusion은 빠른 경로가 없고, ScottPlot은 데시메이션합니다. 실용적 요약은 다음과 같습니다:
ProEssentials는 zero-copy 데이터 로딩, 무손실 GPU Compute Shader 필터링, on-demand 렌더링, 직접 hDC 표시를 결합한 유일한 WinForms 라이브러리이며 — WPF의 동일 엔진보다 ~5% 빠르게, 그리고 이를 증명하는 공개 clone-and-run 저장소와 함께 그렇게 합니다.
여러 라이브러리가 WinForms에서 1억 포인트를 시도할 수 있지만, 그 지형은 WPF보다 좁습니다. 오직 ProEssentials만이 제로 메모리 오버헤드, 무손실 충실도, 직접 hDC 표시로 — 그리고 자체 WPF 빌드보다 빠르게 — 해냅니다.
SciChart는 네이티브 WinForms 컨트롤이 없습니다. Syncfusion은 WinForms 빠른 경로가 없습니다. ScottPlot은 데시메이션합니다. DevExpress의 SwiftPlot은 WinForms 전용이지만 포인트당 객체입니다. LightningChart는 배열 복사와 연속 루프 절충점을 가진 진정한 네이티브 경쟁자입니다.
WinForms 애플리케이션이 1억 포인트를 표시해야 한다면 — 센서 데이터, 신호 처리, 과학적 수집, LiDAR — hDC 결합 Compute Shader 경로는 이용 가능한 가장 빠른 무손실 옵션이자, 오늘 클론하여 재현할 수 있는 유일한 옵션입니다.
이 글의 모든 경쟁사 코드 예제와 주장은 공식 문서, 공개 GitHub 저장소, 공급업체 지원 진술에서 구성되었습니다. 직접 확인하세요:
SciChart
LightningChart
DevExpress
Syncfusion
ComponentOne, Telerik 및 ScottPlot
ProEssentials는 등록, 계정, 영업 통화 없이 NuGet에서 이용할 수 있습니다. WinForms 패키지를 설치하고, 1억 포인트 저장소를 클론하여, 이 페이지의 모든 수치를 재현하세요.
ProEssentials 팀에 문의하기 →귀사의 조직과 최종 사용자들에게 가장 쉽고 가장 전문적인 혜택을 제공함으로써 귀사께서 성공하시는 것이 당사의 최우선 목표입니다.
프로에센셜은 자체 차트 컴포넌트가 필요한 전기 공학 전문가들로부터 태어났습니다. 프로에센셜을 사용하는 탑 엔지니어링 기업들 명단에 참여히세요.
프로에센셜 고객이 되어주셔서 감사드리며, 프로에센셜 차트 제작 엔진을 연구해주셔서 감사드립니다.