What is not very fast is the stuff before that, most importantly TBasicPointSeries.PrepareGraphPoints (easily noticeable just by stepping through TLineSeries.Draw in the debugger)I cannot confirm this. In the line demo the debugger steps over the PrepareGraphPoints command immediately. The execution time cannot be extended by adding more data because the linedemo is setup that a click on the "Add" button adds more series, but every series has the same number of points (50,000).
In the line demo the debugger steps over the PrepareGraphPoints command immediately. The execution time cannot be extended by adding more data because the linedemo is setup that a click on the "Add" button adds more series, but every series has the same number of points (50,000).Well, yeah, the total time measured is not "1x 600kpts" but "12x 50kpts", but as everything is nicely linear that doesn't matter too much.
To see thereal effect of PrepareGraphPoints I am adding a dedicated demo which adds 500,000 data to the same single series with each button click. I clicked it 4 times to have 2 millions values in the series and set a breakpoint on the "PrepareGraphPoints" in TLineSeries.Draw. Stepping over the breakpoint in the debugger occurs instantly in the debugger, but in the line "DrawSingleLineInStack" there is a noticeable delay - this is clear, because drawing requires much more operations than just copying data into a buffer.Wait, am I getting this right. Are you saying it does not behave like this for you? Youtube Screengrab (https://www.youtube.com/watch?v=EE3mb0B30rI)
Modify the linedemo and comment out the lines in btnAddSeriesClick which set the AxisIndexX and AxisIndexY of the new series s, and you will get faster execution because now GetGraphPoint is not called any more.Yep, that gets the time split down from 80:20 in the first post to roughly 50:50 like here. But since I need multiple vertical axes with different (but aligned) transforms, that doesn't really help either.
Of course, the greatest impact on drawing speed is the fact that you want to draw hundreds of thousands of data points in the same chart. In time-critical applications, it is more appropriate to draw only the most recent values and let the others scroll out of the viewport.Hm, switching out the component at runtime is not really an option, but I could/will have to scroll the extent manually during acquisition.
Other advice for high-speed line series: Do not show data point symbols, use the default pen (pen styles other than psSolid, widths other than 1, Cosmetic other than true are guarantees for slow drawing in particular on Windows; only changing the pen color is safe).Thanks for pointing that out, I already found that on the "Fast Line Series" section in the Wiki. Doesn't do much, as drawing isn't really the issue.
Well, in the linedemo I do not see any delay at these two lines, both are executed instantly (I wonder: is my computer really that much faster than yours?) I do see a difference in the demo that I added to the previous post with 2 millions of data points. And here the line PrepareGraphPoints is executed immediately, and there is some (small) delay on "DrawSinglelineInStack"To see thereal effect of PrepareGraphPoints I am adding a dedicated demo which adds 500,000 data to the same single series with each button click. I clicked it 4 times to have 2 millions values in the series and set a breakpoint on the "PrepareGraphPoints" in TLineSeries.Draw. Stepping over the breakpoint in the debugger occurs instantly in the debugger, but in the line "DrawSingleLineInStack" there is a noticeable delay - this is clear, because drawing requires much more operations than just copying data into a buffer.Wait, am I getting this right. Are you saying it does not behave like this for you? Youtube Screengrab (https://www.youtube.com/watch?v=EE3mb0B30rI)
If I completely comment out the DrawSingleLineInStack call, processing 2Mpts still takes 980ms, compared to 1990ms with actual pixel ops (or 6.2s with heaptrc).That's interesting: I am seeing a reduction by about 50%, too, when "DrawSingleLineInStack" is commented out, I would have expected a greater effect.
What if PrepareGraphPoints only updates points it knows it needs to update (nb: I haven't implemented invalidation at all, and I'm not sure how to get notifications for a single changed point from the source). Not perfect and probably buggy as hell, but it does give a 3x speedup of the expensive part.Good.
Well, in the linedemo I do not see any delay at these two lines, both are executed instantly (I wonder: is my computer really that much faster than yours?)Maybe, but I don't think that changes the relative order of things.
Total Size : 3000000
Block Size : 50000
Waves / Block : 3
Series.AxisIndexX : 1
Series.AxisIndexY : -1
Chart.Width : 1280
Chart.Height : 720
=== Core i7-6700HQ "Fast Prepare Patch" @ 1.66GHz ===
600000 / 3000000 add=0.016 prepare=0.032 drawSLIS=0.156 draw_total=0.242
2950000 / 3000000 add=0.016 prepare=0.056 drawSLIS=0.715 draw_total=0.938
=== Core i7-6700HQ @ 1.66GHz ===
600000 / 3000000 add=0.016 prepare=0.318 drawSLIS=0.158 draw_total=0.530
2950000 / 3000000 add=0.017 prepare=1.469 drawSLIS=0.722 draw_total=2.359
=== Core i7-6700HQ @ 3.1GHz ===
600000 / 3000000 add=0.009 prepare=0.170 drawSLIS=0.084 draw_total=0.285
2950000 / 3000000 add=0.009 prepare=0.804 drawSLIS=0.390 draw_total=1.288
=== i5-4600 @ 3.2GHz ===
600000 / 3000000 add=0.008 prepare=0.159 drawSLIS=0.084 draw_total=0.275
2950000 / 3000000 add=0.008 prepare=0.729 drawSLIS=0.376 draw_total=1.196
=== Ryzen 9 3900X ===
600000 / 3000000 add=0.005 prepare=0.128 drawSLIS=0.067 draw_total=0.216
2950000 / 3000000 add=0.006 prepare=0.589 drawSLIS=0.304 draw_total=0.958
That's interesting: I am seeing a reduction by about 50%, too, when "DrawSingleLineInStack" is commented out, I would have expected a greater effect.Unless the testcase turns into a GDI pixel fill rate benchmark (which I'd say is a degenerate case for charting), the cost of PrepareGraphPoints is just so much larger than drawing. Also I just saw that the "same pixel" optimization is actually in TLineSeries.DrawSingleLineInStack/CacheLine, so it was actually you instead of MS who was clever here :D
But I moved the PrepareGraphPoints method into the protected section of TBasicPointSeries and made it "virtual". Therefore, now you can write a descendant of TLineSeries in which you replace the inherited PrepareGraphPoints by your optimization.That seems like a very good idea, a TFastLineSeries that also ensures "fast" settings both on Pen etc. and Source side seems like the way to go here. I'm going to give that a go.
=== Core i7-6700HQ "Fast Line Series Patch" @ 1.66GHz ===
600000 / 3000000 add=0.017 prepare=0.027 points=53598 breaks=3 drawSLIS=0.102 draw_total=0.151
2950000 / 3000000 add=0.017 prepare=0.051 points=243394 breaks=6 drawSLIS=0.462 draw_total=0.592
Intel i7-10700 CPU @ 2.90; Windows 11
32 bit, -O1: 2950000 / 3000000 add=0.000 prepareGP=0.543 drawSLIS=0.331 draw_total=0.950
32 bit, -O1, no debugger: 2950000 / 3000000 add=0.000 prepareGP=0.551 drawSLIS=0.323 draw_total=0.953
32 bit, -O2: 2950000 / 3000000 add=0.000 prepareGP=0.490 drawSLIS=0.319 draw_total=0.876
32 bit, -O4: 2950000 / 3000000 add=0.000 prepareGP=0.473 drawSLIS=0.327 draw_total=0.879
64 bit, -O1: 2950000 / 3000000 add=0.011 prepareGP=0.507 drawSLIS=0.122 draw_total=0.691
64 bit, -O2: 2950000 / 3000000 add=0.016 prepareGP=0.444 drawSLIS=0.127 draw_total=0.634
64 bit, -O4: 2950000 / 3000000 add=0.000 prepareGP=0.448 drawSLIS=0.108 draw_total=0.603
However, it has the pitfall that I suspected: When the user edits a data value somewhere in the "salvaged" part of the data range the conversion to graph coordinates is not updated, and the chart plots the old dataYup, I was aware ;-)