

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.
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.
| 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 | WinUI 3 (.NET 10), Windows 11, mid-range GPU |
| What we measure | Lines 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.
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.
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.
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.
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 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.
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 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 pointsDer 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.
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.
| Factor | ProEssentials | Syncfusion | Telerik | DevExpress | ComponentOne |
|---|---|---|---|---|---|
| Renders 100M in real time? | ✅ Yes — natively | ❌ OOM well before 100M | ⚠️ Aggregate first | ⚠️ Sample first | ⚠️ Direct2D mode (50k published) |
| Rendering | Native Direct3D compute | WriteableBitmap | Composition visuals | Managed XAML | Direct2D (optional) |
| Data model | Zero-copy float[] pointer | Object-per-point | Object-per-point | Object-per-point | Object-per-point |
| Memory: your data | 400 MB | 400 MB | 400 MB | 400 MB | 400 MB |
| Memory: library overhead | ~0 MB | ~2,400 MB+ | ~2,400 MB+ | ~2,400 MB+ | ~2,400 MB+ |
| Total memory | ~400 MB | OOM risk | ~2,800 MB (or downsampled) | ~2,800 MB (or sampled) | ~2,800 MB |
| Special series type? | No — same control | FastLineBitmapSeries | Sampling settings | Sampling settings | Direct2D render mode |
| Full-fidelity display | Lossless GPU min/max | Bitmap raster | Downsampled subset | Sampled subset | Direct2D raster |
| Native ARM64 | Yes — native binary | Managed | Managed | Managed | Managed |
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 Model | Overhead (100M pts) | Used By |
|---|---|---|
| Zero-copy pointer | ~0 MB | ProEssentials |
| Array copy (float) | ~400 MB | — |
| Object-per-point | ~2,400 MB+ | Syncfusion, Telerik, DevExpress, ComponentOne |
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.
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.
Der vollständige Vergleich von WinUI-Funktionen, Preisen und Lizenzierung.
Read itDie 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)
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 usIhr Erfolg ist unser höchstes Ziel, indem wir Ihrem Unternehmen und Ihren Endbenutzern den einfachsten und professionellsten Nutzen bieten.
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.
Vielen Dank, dass Sie ein ProEssentials-Kunde sind, und vielen Dank, dass Sie die ProEssentials-Charting-Engine recherchieren.