SciChart® the market leader in Fast WPF Charts, WPF 3D Charts, iOS Chart, Android Chart and JavaScript Chart Components
Hi,
I develop a real time app that polls online data, e.g. EEG, and sends it on a SciChart plot. I need to keep the polling thread at maximal and steady time resolution, ideally, close to 1 ms. To achieve that, I run the Scichart plot in a different STA thread (WPF window), such that the polling thread uses only IDataSeries.Append() and IDataSeries. SuspendUpdates() methods to submit data to the plot.
However, time tests show that these 2 functions in the polling thread have very irregular execution times, which may depend on different factors, e.g. on the plot size. To demonstrate it, I recorded the calling times of the following code, which submits 10 000 samples in a loop:
double StartTimeInMs = SW.ElapsedTicks / 10000;
using (TestDataSeries.SuspendUpdates())
{
TestDataSeries.Clear();
TestDataSeries.Append(X, Y);
}
TimeDelta = SW.ElapsedTicks / 10000 - StartTimeInMs;
The resulting TimeDelta (loop times) are shown on the the plot 1 in the attachment.As you can see the loop time is unstable and grows to 40 ms, when the plot window is maximized.
I came to a possible solution by invoking the sample submission code in the plot thread:
double StartTimeInMs = SW.ElapsedTicks / 10000;
Dispatcher D = (TestDataSeries.ParentSurface as SciChartSurface).Dispatcher;
D.BeginInvoke((Action)(() =>
{
using (TestDataSeries.SuspendUpdates())
{
TestDataSeries.Clear();
TestDataSeries.Append(X, Y);
}
}));
TimeDelta = SW.ElapsedTicks / 10000 - StartTimeInMs;
As you can on the attached plot 2, the loop times are much better now, although I still see occasional peaks above 5 ms.
So, my questions are:
If I use the BeginInvoke in a separate plot thread, should I use lock() to avoid potential data overflow, for instance:
Dispatcher D = (TestDataSeries.ParentSurface as SciChartSurface).Dispatcher;
D.BeginInvoke((Action)(() =>
{
if (Monitor.TryEnter(TestDataSeries.SyncRoot, new TimeSpan(0, 0, 0, 0, 0)))
try
{
TestDataSeries.Clear();
TestDataSeries.Append(X, Y);
}
finally
{
Monitor.Exit(TestDataSeries.SyncRoot);
}
}));
Thank you in advance for answers and hints!
Apart from our Performance Tips and Tricks documentation, we have a number of examples which show you best practice in using SciChart in a high performance scenario.
Take a look at the WPF Chart Realtime performance demo
In this example
Try that! It’s the best way to get highest performance out of SciChart. And don’t forget to enable DirectX ‘Visual Xccelerator Engine’ as in the Performance Tips and Tricks documentation!
Best regards
Andrew
We perform a lock() around a syncroot object which halts rendering. If rendering is occuring now, we wait until its finished (hence your variable time) then lock()
That can indeed explain the variable time, thanks !
You also reduce thread-contention if you are appending on a background thread and drawing on the UI thread
The multi-threading seems a reasonable solution but my question is slightly different. To rephrase it, given that I append data in a separate thread, how can I stabilize calling times of Append() in that thread? As you can see on the plot 2, there are still occasional 5 ms peaks in the calling times, while the majorly of them are within 1 ms. Is there a better solution than using BeginInvoke? Are there some settings that can make the calling times more stable, rather than just faster?
As per our Performance Tips and Tricks FAQ, calling DataSeries.Append(x,y) with a single point thousands of times is not optimal for performance.
What happens inside DataSeries.Append is as follows:
If you append a single x,y point then all the above has to be performed per-point. It’s very efficient, takes a very short period of time, but when you have a background thread hitting one or more DataSeries.Append() calls then you can quickly reach lock contention which can cause performance problems.
The solution from our documentation is as follows:
Batching appends using the overloaded API to append IEnumerable, IList or Arrays has a massive impact on DataSeries performance. Arrays have the biggest impact as these can be indexed using unsafe code. If you have a lot of data to append, create a small buffer (say 10-100 points) and append them in blocks.
NOTE: Why does this work? When data is appended in blocks, you get a single draw call at the end. You also reduce thread-contention if you are appending on a background thread and drawing on the UI thread. Finally, the above is simply more memory efficient as we only need to recalculate the additional memory required once, rather than per-point appended.
SciChart processes data so fast, we can process >100,000 updates to the DataSeries per second. However, if you update that fast, then > SciChart will spend a lot of time in static overhead for dataseries updates. It’s better for performance to update more data, less often.
Please let me know if this helps!
Best regards,
Andrew
Please login first to submit.