100 Millionen Punkte in WinUI plotten

Ein WinUI-Chart-Performancevergleich — ProEssentials vs. Syncfusion, Telerik, DevExpress und ComponentOne

100 million points WinUI
large dataset WinUI chart
WinUI GPU compute shader
zero copy chart data
WinUI chart performance
WinUI chart benchmark
C# WinUI charting
plot millions WinUI

100 Millionen Punkte auf WinUI plotten
und wie fünf Charting-Bibliotheken damit umgehen

Einhundert Millionen Punkte sind ein Stresstest, der Diagrammbibliotheken, die für Skalierung gebaut sind, von jenen trennt, die für Dashboards gebaut sind. Unter WinUI ist die Trennung scharf, denn die meisten WinUI-Diagramme sind verwaltete .NET-Controls, die über die XAML-Schicht zeichnen, während ProEssentials darunter eine native Direct3D- und Direct2D-C++-Engine betreibt.

Wir haben dasselbe Signal mit 100 Millionen Punkten in fünf WinUI-Bibliotheken dargestellt: ProEssentials, Syncfusion, Telerik, DevExpress und ComponentOne. Der folgende Code ist in jeder davon der kürzeste ehrliche Weg zum Diagramm. Der Unterschied liegt nicht wirklich in der Zeilenzahl, sondern darin, was mit Ihren Daten auf dem Weg zum Bildschirm geschieht.

ProEssentials liest Ihr float-Array direkt an Ort und Stelle und baut die Geometrie auf der GPU neu auf. Die vier verwalteten Suiten binden an eine Collection von Objekten, eines pro Punkt, sodass dieselben 100 Millionen Werte zu einem mehrere Gigabyte großen verwalteten Heap anschwellen, bevor überhaupt etwas gezeichnet ist.

Reproduce the WinUI numbers yourself: clone, build, run the WinUI 100M-point chart demo on GitHub.

Prefer WPF or WinForms? See the WPF 100M-point demo and the WinForms 100M-point demo.


100 million points in a WinUI chart — lossless GPU compute-shader rendering, ProEssentials
ProEssentials WinUI (GigaPrime2D) — 100,000,000 points, lossless, real-time render, native Direct3D ComputeShader
Der Test

Gleiche Daten, gleiche Maschine, fünf Bibliotheken. Wir messen die Codezeilen, den Speicher-Overhead, ob überhaupt gerendert wird und ob die Anzeige verlustfrei oder eine heruntergesampelte Näherung ist.

ParameterValue
Data points100,000,000 (100 million)
Data typefloat (4 bytes per value) — 400 MB raw data
Chart type2-D line chart, single series, sequential X-axis
PatternSine wave with random noise (realistic signal data)
PlatformWinUI 3 (.NET 10), Windows 11, mid-range GPU
What we measureLines of code, memory overhead, render time, data fidelity

Das folgende Muster wiederholt sich über alle fünf Bibliotheken hinweg: Der Code, der die Daten lädt, ist weit wichtiger als der Code, der das Diagramm konfiguriert.

ProEssentials (native Direct3D-Engine)

Das Diagramm liest Ihr vorhandenes float-Array über UseDataAtLocation, sodass niemals eine zweite Kopie entsteht. Alle 100 Millionen Werte gehen an die GPU, wo der Filter2D3D compute shader einen konservativen min/max-Vorfilterdurchlauf ausführt, bevor der finale Shader die Szene aufbaut. Jeder Punkt wird verarbeitet. Wenn die Daten dichter sind als die Anzeige, behält der Filter 50 min/max-Paare pro Pixelspalte bei, also 100 gezeichnete Punkte pro Pixel, sodass keine Spitze verworfen wird und dichte Bereiche als echte Vollfläche rendern, dasselbe Bild, das der vollständige Datensatz erzeugen würde.

// ProEssentials — Plot 100 Million Points (WinUI 3, ~15 lines)
// NuGet: Install-Package ProEssentials.Chart.Net10.WinUI
// Control: PesgoWinUI (named Pesgo1 in XAML). .NET 10, x64 and native ARM64.

// 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 chart
Pesgo1.PeData.Subsets = 1;
Pesgo1.PeData.Points  = 100_000_000;
Pesgo1.PePlot.Method  = SGraphPlottingMethod.Line;
Pesgo1.PeConfigure.RenderEngine = RenderEngine.Direct3D;
Pesgo1.PeData.ComputeShader  = true;   // build chart geometry on the GPU
Pesgo1.PeData.Filter2D3D     = true;   // lossless GPU min/max pre-filter
Pesgo1.PeData.StagingBufferY = true;   // required with ComputeShader

// 3. Zero-copy: point the chart at your existing array — no duplication
Pesgo1.PeData.Y.UseDataAtLocation(yData, yData.Length);

// 4. Rebuild vertices on the GPU and render
Pesgo1.PeFunction.Force3dxVerticeRebuild = true;
Pesgo1.PeFunction.ReinitializeResetImage();
Pesgo1.Invalidate();

Auch die Präsentation ist nativ. Die Engine rendert in ihre eigene DXGI flip-model composition swap chain und übergibt sie dem XAML-Host über ISwapChainPanelNative::SetSwapChain, sodass DWM das Diagramm direkt in Ihren Visual Tree komponiert, ohne Zwischen-Bitmap und ohne CPU-Kopie pro Frame. Sie begrenzt zudem die Present-Queue mit SetMaximumFrameLatency(1), weshalb ein Diagramm mit 100 Millionen Punkten einer Ziehbewegung weiterhin folgt, statt Frames vorauszupuffern.

Why it scales

Der einzige Speicher, den das Diagramm hinzufügt, ist ein kleiner GPU-Staging-Buffer. Ihre 400 MB Daten bleiben Ihre 400 MB, und dasselbe PesgoWinUI-Control verarbeitet 10,000 Punkte oder 100 Millionen ohne speziellen Serientyp.

Vor den anderen vieren: Was passiert, wenn Sie das wirklich ausführen

Die vier folgenden Beispiele sind der dokumentierten API des jeweiligen Anbieters treu und sie kompilieren. Was sie nicht tun werden, ist Ihnen ein Diagramm zu zeichnen. Richten Sie eines davon auf 100 Millionen Werte, dann sind die realistischen Ergebnisse eine OutOfMemoryException irgendwo in der Allokationsschleife oder eine Wartezeit, die lang genug ist, dass Sie den Debugger abbrechen, bevor er fertig ist. Wir zeigen den Code, weil er den Unterschied greifbar macht, nicht weil wir erwarten, dass ihn jemand in dieser Größenordnung ausführt. Der aufschlussreiche Teil jedes Snippets ist nicht die Diagrammkonfiguration am Ende, sondern die Schleife am Anfang, die pro Datenpunkt ein Objekt allokiert.

Das sollte man im Hinterkopf behalten, wenn man Performance-Aussagen liest. Alle vier vermarkten ihre Diagramme als hochperformant, und auf den Plattformen, für die sie tatsächlich Zahlen veröffentlichen, ist das eine faire Aussage. Der Haken ist, dass es keine WinUI-Zahlen sind.

Ein Hinweis zu diesen Zahlen. Keiner der vier Anbieter veröffentlicht einen Large-Data-Benchmark speziell für sein WinUI-Diagramm. Syncfusions Millionen-Punkte-Benchmark ist ein WPF-Benchmark. Die größten DevExpress-Werte hängen von SwiftPlotSeriesView ab, einer reinen WinForms-Series-View. Die von ComponentOne veröffentlichte Direct2D-Zahl liegt bei 50,000 Punkten in rund 5 ms auf einem .NET 6 WinForms-Build. Teleriks Empfehlung für große Datenmengen lautet, die Daten vor dem Binden zu aggregieren. Wir haben ihre WinUI-Controls nicht selbst gebenchmarkt, daher werden wir keine Obergrenzen für sie erfinden; sagen lässt sich, dass veröffentlichte Belege für die WinUI-Performance bei großen Datenmengen für keines von ihnen bislang existieren.

Zur Einordnung hier unsere eigene Grenze. Der Direct2D-Pfad von ProEssentials gerät bei etwa 3 Millionen Punkten an seine Belastungsgrenze, und das bereits mit Zero-Copy-Laden und ohne Objekt-Overhead pro Punkt. Erst der Direct3D-compute-shader-Pfad trägt ihn bis zu 100 Millionen. Ein verwaltetes XAML-Control, das pro Punkt ein Objekt allokiert, startet weit dahinter.

Syncfusion (SfCartesianChart)

Syncfusions Fast Series rastern die Linie in eine WriteableBitmap, was eine echte Optimierung gegenüber dem Standardpfad ist. Der Haken liegt im Datenmodell: Das Diagramm bindet an eine Collection von Objekten, sodass 100 Millionen Punkte 100 Millionen Allokationen bedeuten, lange bevor der Bitmap-Pfad überhaupt läuft.

// Syncfusion — Plot 100 Million Points (WinUI, SfCartesianChart)
// NuGet: Install-Package Syncfusion.Chart.WinUI

// 1. Syncfusion binds to objects — one per point
public class DataPoint { public double X { get; set; } public double Y { get; set; } }

// 2. Build the collection — 100M objects is ~2.4 GB+ with object headers
var data = new List<DataPoint>(100_000_000);
for (int i = 0; i < 100_000_000; i++)
    data.Add(new DataPoint { X = i, Y = Math.Sin(i * 0.0001) + rand.NextDouble() * 0.1 });
// This allocation alone risks OutOfMemoryException; the practical ceiling is well below 100M.

// 3. Fast bitmap series — rasterizes the line to a WriteableBitmap
var series = new FastLineBitmapSeries {
    ItemsSource  = data,
    XBindingPath = "X",
    YBindingPath = "Y"
};
chart.Series.Add(series);

Erwähnenswert ist, woher Syncfusions Millionen-Punkte-Zahl stammt: Der veröffentlichte Benchmark dazu ist ein WPF-Benchmark, kein WinUI-Benchmark. So oder so setzt das Objekt-pro-Punkt-Modell die Obergrenze, nicht der Rasterizer.

Telerik (RadCartesianChart)

Telerik zeichnet seine Serien über die Composition ContainerVisualsFactory, was gewöhnliche Diagramme flüssig hält. Wie Syncfusion bindet es an Objekte, und seine eigene Empfehlung für große Datenmengen lautet, vor der Übergabe an das Diagramm herunterzusampeln.

// Telerik — Plot 100 Million Points (WinUI, RadCartesianChart)
// NuGet: Install-Package Telerik.WinUI.Controls   (license key required)

// 1. Telerik binds to objects — one per point
public class DataPoint { public double X { get; set; } public double Y { get; set; } }

// 2. Build the collection (~2.4 GB+ at 100M objects)
var data = new ObservableCollection<DataPoint>();
for (int i = 0; i < 100_000_000; i++)
    data.Add(new DataPoint { X = i, Y = Math.Sin(i * 0.0001) + rand.NextDouble() * 0.1 });

// 3. Line series — drawn through the Composition ContainerVisualsFactory
var series = new LineSeries {
    ItemsSource     = data,
    CategoryBinding = new PropertyNameDataPointBinding { PropertyName = "X" },
    ValueBinding    = new PropertyNameDataPointBinding { PropertyName = "Y" }
};
// For large data, Telerik expects you to downsample before binding.
radChart.Series.Add(series);

Zuerst herunterzusampeln bedeutet, dass das Diagramm nie alle 100 Millionen Punkte sieht, eine sinnvolle Strategie, nur eben keine verlustfreie.

DevExpress (ChartControl)

Das DevExpress WinUI ChartControl bindet einen DataSource mit Argument- und Wert-Membern. Für große Datenmengen empfiehlt DevExpress, das Data-Sampling zu aktivieren, damit das Control eine repräsentative Teilmenge statt jedes einzelnen Punkts rendert.

// DevExpress — Plot 100 Million Points (WinUI, ChartControl)
// NuGet: DevExpress WinUI packages (private feed, credentials required)

// 1. DevExpress binds to objects — one per point
public class DataPoint { public double Argument { get; set; } public double Value { get; set; } }

// 2. Build the collection (~2.4 GB+ at 100M objects)
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 });

// 3. Line series on the ChartControl — bind DataSource + data members
var series = new LineSeries();
series.DataSource         = data;
series.ArgumentDataMember = "Argument";
series.ValueDataMember    = "Value";
chartControl.Series.Add(series);
// DevExpress recommends data sampling to keep large sets responsive.

Sampling hält die UI reaktionsschnell, doch die gleiche Objekt-pro-Punkt-Allokation greift, bevor überhaupt etwas davon passiert.

ComponentOne (FlexChart)

ComponentOne ist der einzige Wettbewerber mit einem Direct2D-Rendermodus, und das ist eine echte Optimierung gegenüber seinem Standardpfad. Es bleibt ein verwaltetes Control, das an eine Objekt-Collection bindet, sodass die Speicherbilanz der der anderen gleicht. Beim Durchsatz nennt ComponentOne für Direct2D die konkrete Zahl von 50,000 Punkten in rund 5 ms, gemessen auf einem .NET 6 WinForms-Build und nicht unter WinUI.

// ComponentOne (MESCIUS) — Plot 100 Million Points (WinUI, FlexChart)
// NuGet: Install-Package C1.WinUI.Chart

// 1. FlexChart binds to objects — one per point
public class DataPoint { public double X { get; set; } public double Y { get; set; } }

// 2. Build the collection (~2.4 GB+ at 100M objects)
var data = new List<DataPoint>(100_000_000);
for (int i = 0; i < 100_000_000; i++)
    data.Add(new DataPoint { X = i, Y = Math.Sin(i * 0.0001) + rand.NextDouble() * 0.1 });

// 3. Bind the chart, then switch on the Direct2D render mode for large data
flexChart.ItemsSource = data;
flexChart.BindingX    = "X";
flexChart.Series.Add(new Series { Binding = "Y" });
flexChart.RenderMode  = RenderMode.Direct2D;   // required to draw millions of points

Der Direct2D-Modus kommt dem nativen Ansatz am nächsten, den ein verwaltetes WinUI-Diagramm erreichen kann, und man sollte ihn kennen, wenn man ehrlich vergleicht.

Ergebnisse im direkten Vergleich

Die Speicherzeilen sind einfache Arithmetik, keine Benchmarks: 100 Millionen floats ergeben 400 MB, während 100 Millionen gebundene Objekte Gigabytes an Headern und Referenzen mit sich tragen, bevor auch nur ein einziges Pixel gezeichnet ist.

FactorProEssentialsSyncfusionTelerikDevExpressComponentOne
Renders 100M in real time?✅ Yes — natively❌ OOM well before 100M⚠️ Aggregate first⚠️ Sample first⚠️ Direct2D mode (50k published)
RenderingNative Direct3D computeWriteableBitmapComposition visualsManaged XAMLDirect2D (optional)
Data modelZero-copy float[] pointerObject-per-pointObject-per-pointObject-per-pointObject-per-point
Memory: your data400 MB400 MB400 MB400 MB400 MB
Memory: library overhead~0 MB~2,400 MB+~2,400 MB+~2,400 MB+~2,400 MB+
Total memory~400 MBOOM risk~2,800 MB (or downsampled)~2,800 MB (or sampled)~2,800 MB
Special series type?No — same controlFastLineBitmapSeriesSampling settingsSampling settingsDirect2D render mode
Full-fidelity displayLossless GPU min/maxBitmap rasterDownsampled subsetSampled subsetDirect2D raster
Native ARM64Yes — native binaryManagedManagedManagedManaged

Warum Zero-Copy entscheidend ist

Die Zahl, die in dieser Größenordnung über alles entscheidet, sind nicht die Bilder pro Sekunde, sondern wie oft Ihre Daten auf dem Weg zum Bildschirm kopiert werden.

ProEssentials kopiert sie kein einziges Mal. Es liest das float-Array, das Sie bereits allokiert haben, und baut das Bild auf der GPU neu auf. Ein verwaltetes Diagramm, das an eine Collection bindet, kopiert jeden Wert in ein Objekt mit Headern und Referenzen, und genau daher kommen die Gigabytes.

Bei ein paar Tausend Punkten fällt es niemandem auf. Bei Hundert Millionen ist diese eine Design-Entscheidung der Unterschied zwischen einer 400 MB-App und einer, der der Speicher ausgeht.

Data ModelOverhead (100M pts)Used By
Zero-copy pointer~0 MBProEssentials
Array copy (float)~400 MB
Object-per-point~2,400 MB+Syncfusion, Telerik, DevExpress, ComponentOne

The takeaway

Schnelles Rendering kann ein Datenmodell nicht retten, das pro Punkt ein Objekt allokiert. Die verwalteten Suiten greifen genau deshalb zu Downsampling und Bitmap-Fast-Paths, weil das Objektmodell 100 Millionen Punkte nicht halten kann. ProEssentials lässt das Problem gar nicht erst entstehen.

Das Fazit

Alle fünf Bibliotheken zeichnen ein ansprechendes Liniendiagramm. Nur eine zeichnet 100 Millionen Punkte verlustfrei und in Echtzeit, ohne dass Sie Ihre Daten heruntersampeln, den Serientyp wechseln oder einen mehrere Gigabyte großen Heap in Kauf nehmen müssen. Das ist der Lohn einer nativen Engine, die Ihre Daten dort liest, wo sie bereits liegen.

Wenn Ihre WinUI-App Tausende von Punkten darstellt, wählen Sie einfach die Suite, die Sie ohnehin besitzen. Wenn sie Millionen bis Hunderte von Millionen darstellt, entscheidet allein das Datenmodell, und ProEssentials ist genau dafür gebaut.

WinUI-Diagrammvergleich

Der vollständige Vergleich von WinUI-Funktionen, Preisen und Lizenzierung.

Read it
Performance

Wie die native Engine und compute shader für schnelle Diagramme sorgen.

Read it
KI-Code-Assistent

DLL-validierte Antworten ohne halluzinierte Eigenschaften.

Read it
Quellen

Die Rendering-Ansätze und Binding-APIs der Mitbewerber stammen aus deren eigener Dokumentation:

Syncfusion (WinUI SfCartesianChart)

Telerik (UI for WinUI RadChart)

DevExpress (WinUI ChartControl)

ComponentOne / MESCIUS (FlexChart für WinUI)

Haben Sie ein WinUI-Projekt mit großen Datenmengen?

Sagen Sie uns, wie viele Punkte Sie plotten müssen und wie sie sich aktualisieren. Die Entwickler, die die Engine gebaut haben, sagen Ihnen ehrlich, ob ProEssentials passt.

Contact us

Unsere Aufgabe

Ihr Erfolg ist unser höchstes Ziel, indem wir Ihrem Unternehmen und Ihren Endbenutzern den einfachsten und professionellsten Nutzen bieten.

Wir sind Ingenieure

ProEssentials wurde von professionellen Elektroingenieuren erschaffen, die ihre eigenen Charting-Komponenten benötigten. Treten Sie unserer großen Liste von Top-Engineering-Unternehmen bei, die ProEssentials einsetzen.

Danke sehr

Vielen Dank, dass Sie ein ProEssentials-Kunde sind, und vielen Dank, dass Sie die ProEssentials-Charting-Engine recherchieren.