DispatcherTimer and UI refresh limits in C# silverlight - c#

Again I apologize for a question that might be simple to all of you. I have a limited understanding of what goes behind the scenes in Silverlight.
I have a charting app (Visiblox) that I use as a rolling scope updated every 20ms, adding and removing a point. In pseudocode:
List<Point> datapoints= new List<Point>();
Series series = new Series(datapoints);
void timer_tick(){
datapoints.Add(new Point);
datapoints.RemoveAt(0);
// no need to refresh chart, it does refresh automatically
}
When running 6 series in this charting tool, it started to show a bit sluggish. Changing the tick to 10ms made no difference whatsoever, chart was updated at the same speed, so it seems that 20ms is the speed limit (UI or chart?).
I tried with CompositionTarget.Rendering and got the same results: below 20ms there was no difference in speed.
Then I accidentally enabled both and speed doubled. So I tested with multiple threads (2, 3, 4) and speed doubled, tripled and quadrupled. This has no locks yet, as I don't even know what process I need to generate a lock on, but got no data corruption nor memory leaks.
The question I have is why a sluggish chart at 20ms can not run at 10ms but is ridiculously fast when multithreaded? Is the UI refresh process being run faster? Is the chart computation doubled? Or is there a limit to how fast a single DispatcherTimer can be executed?
Thanks!
Edit: I have a background of embedded coding, so when I think of threads and timings, I immediately think of toggling a pin in hardware and hook up a scope to measure process lengths. I am new to threads in C# and there are no pins to hook up scopes. Is there a way to see thread timings graphically?

A DispatcherTimer, which fires its Tick event on the UI thread, is what's considered a low-resolution or low-accuracy timer because its Interval effectively means "tick no sooner than x since the last tick". If the UI thread is busy doing anything (processing input, refreshing the chart, etc.) then it will delay the timer's events. Furthermore, having a bunch of DispatcherTimer's ticking away on the UI thread at very low intervals will also slow down the responsiveness of your application because while the Tick event is being raised, the application can't respond to input.
So as you noted, in order to process data frequently, you should move to a background thread. But there are caveats. The fact that you aren't currently observing corruption or other bugs could be purely coincidental. If the list is being modified on a background thread at the same time the foreground thread is trying to read from it, you will eventually crash (if you're lucky) or see corrupt data.
In your example, you have a comment that says "no need to refresh chart, it does refresh automatically." This makes me wonder how does the chart know that you have changed the datapoints collection? List<T> does not raise events when it is modified. If you were using an ObservableCollection<T> I would point out that each time you remove/add a point you are potentially refreshing the chart, which could be slowing things down.
But if you are in fact using List<T> then there must be something else (perhaps another timer?) that is refreshing the chart. Maybe the chart control itself has a built-in auto-refresh mechanism?
In any event, the problem is a little bit tricky but not completely new. There are ways that you could maintain a collection on a background thread and bind to it from the UI thread. But the faster your UI refreshes, the more likely you'll be waiting for a background thread to release a lock.
One way to minimize this would be to use a LinkedList<T> instead of List<T>. Adding to the end of a LinkedList is O(1), so is removing an item. A List<T> needs to shift everything down by one when you remove an item from the beginning. By using LinkedList you can lock on it in the background thread(s) and you'll minimize the amount of time that you're holding the lock. On the UI thread you would also need to obtain the same lock and either copy the list out to an array or refresh the chart while the lock is held.
Another possible solution would be to buffer "chunks" of points on the background thread and post a batch of them to the UI thread with Dispatcher.BeginInvoke, where you could then safely update a collection.

The key here I think is to realise that Silverlight renders at a maximum frame rate of 60fps by default (customisable through your MaxFrameRate property). That means that the DispatcherTimer ticks will fire at most 60 times per second. Additionally, all the rendering work happens on the UI thread as well so the DispatcherTimer fires at the rate that the drawing is happening at best, as pointed out by the previous poster.
The result of what you're doing by adding three timers is just to fire the "add data" method 3 times per event loop rather than once, so it will look like your charts are going much faster but in fact the frame rate is roughly the same. You could get the same effect with a single DispatcherTimer and just add 3 times as much data on each Tick. You can verify this by hooking into the CompositionTarget.Rendering event and counting the frame rate there in parallel.
The ObservableCollection point made previously is a good one but in Visiblox there is a bit of magic to try and mitigate the effects of that so if you're adding data at a very fast rate the chart updates will be batched up at the rate of the render loop and unnecessary re-renders will be dropped.
Also regarding your point about being tied to the ObservableCollection implementation of IDataSeries, you are entirely free to implement the IDataSeries interface yourself, for example by backing it with a simple List. Just be aware that obviously if you do that the chart will no longer automatically update when data changes. You can force a chart update by calling Chart.Invalidate() or by changing a manually set axis range.

Related

Rapidly updated DataGridView

I use a DataGridView to monitor IO events. Each time a new IO event occurs, the DataGridView is populated with a new item containing a timestamp among other data. These updates come every 10 ms, and I want to place the new items at the top of the grid.
I only need to keep ~100 rows in the grid; the older ones should be discarded as to not consume memory. All my attempts have proven to be too slow, do you have any ideas on how to approach this?
Displaying ticking data is quite challenging task. One of the biggest step to improve performance can be reducing refresh rate to a number recognizable by human eye - 20 refreshes per second without reacting to each and every IO event.
Create or use a ring buffer to store 100 records only and reduce GC / memory.
Use Dispatcher timer and schedule a grid refresh every 50ms. At the timer tick, grab the buffer data into the preallocated collection and refresh the Grid entirely.
You can then further improve the implementation by optimizing step 2 - say reducing measures of the cells.

How to Pause C# code execution without blocking main thread?

OK so we have a program where we want to animate the controls on a WinForm and then then resume subsequent operations of the remaining block of code. Here is the sample code.
The function is on the WinForm, which is running on main thread presumably
Private void DoThisWork();
{
do some work here
animateControls()
//<NEED TO PAUSE HERE WHILE THE GUI ANIMATES AND UPDATES DISPLAYING THE ANIMATION OF THE CONTROL>
//Tried Option 1: thread.sleep. When we do this the main thread blocks and the animation is //not seen. The control is directly painted at x1,y1 and thats it, the intermediate rendering is not seen
// Tried Option 2: Application.DoEvents. This works very well except that the CPU maxes out and the animation then appears very jittery
continue doing remaining work // must execute only after animateControls() completes the animation part.
}
Now, animateControls() is simply a function that is on a timer and moves a control from point (x,y) to (x1,y1) and this takes about 3 seconds.
SuspendLayout and ResumeLayout dont force GUI update because thread.sleep caused the main thread to block so everything is virtually at a standstill.
Using a different thread to animate the GUI does not seem to help because I still need for the entire animation to complete.
Also, I cannot add anything in the animation code because it is called from multiple functions and therefore is used as a common function.
Your are going down the wrong path. Put your work to do on a seperate thread and let your UI Thread do your animation till the work thread is finished.
The BackgroundWorker class might come in handy. http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

How to measure WPF Silverlight FPS or rendering time?

I have a control (for both WPF and Silverlight), represented data as bars. (Small chart). I want write an application, that measure performance of this control. I try to add points in cycle and wait while point will be rendered.
In WPF work this:
//start measuring time
for (int i =10, i<100, i++){
chart.AddRandomPoint(i); //i - argument
System.Windows.Forms.Application.DoEvents();
}
//finish measuring time
But in Silverlight it's impossible to use System.Windows.Forms.Application.DoEvents().
The second way is using DispatcherTimer:
dicpatcherTimer_Tick(object sender, EventArgs e){
chart.AddRandomPoint(i);
}
and subscribe to CompositionTarget.Rendering event. In event handler icrement counter.
Big value of this counter shows good performance.
My question is:
It is possible add point, wait while chart will be rendered, immediately add another one point etc.?
Is the second way (using DispatcherTimer object and CompositionTarget.Rendering event) correct. Can it shows performance? (I need to compare performance before and after refactoring).
Considering that you'are asking about WPF too, I would suggest to look at
WPF Performance Toolkit, especially think that Perforator will help you a lot with this.
Good article on WPF/Silverlight performance is A Critical Deep Dive into the WPF Rendering System

"Animating" a MapPolyLine in Silverlight

I need to animate a MapPolyLine such that on a given event, the start pin zips up to the end pin. The approach I am considerg is to animate frames such that I divide the MapPolyLine into n number of segments and decrease the TimeSpan ts between each frame along the logic of my chosing (to keep things simple, let's just say ts = ts / 2 after each cycle).
I know that one cannot animate the MapPolyLine, but one can change the appearance of the line by updating the latitude and longitude of the end position. My question concerns timing. My experience with multithreading is minimal, so I did not want to take the risk of a user running into a threading based error that may be difficult to diagnose. Should I:
use a simple DispatcherTimer and tick method
use a BackgroundWorker that reports progress every-time the TimeSpan has elapsed
use a dummy animation and attach an event handler to the rendering event
solution other than the above mentioned options?
Thank you in advance for your help!
Decided to use the DispatcherTimer considering the amount of time the animation is going to take - by creating a separate animation object that holds state and its own dispatch timer, it ended up being more efficient than using separate threads because the callback would mean interrupting the main UI thread and based on the requirements it was highly unlikely that there will be more than 2-3 of these animations occuring simultaneously with 95% or greater being only 1 animation at a time.

Simultaneous updates across two display contexts in openGL

I have a C# .NET application with which I've created a custom image display control. Each image display represents its own display context and draws the image using glDrawPixels (Yes I know it would be better to use textures, I plan to in the futures but this app is already too far along and my time is limited).
I am now trying to have both images pan simultaneously. That is, when one image is moved down ten pixels, the second image moves down ten pixels. Like so:
imageOne.YPan -= 10;
imageTwo.YPan -= 10;
imageOne.Invalidate(); //This forces a redraw.
imageTwo.Invalidate(); //This forces a redraw.
Alright so here is the problem I am having. Only one of the images displays is redrawing. If I place a pause in between the two Invalidate calls and make the pause duration at least 110 milliseconds both will redraw, but not simultaneously. So it looks as if the second image is always trying to catch up to the first. Plus, a 110 millisecond pause slows down the motion too much.
I have tried placing the updating and invalidating of each image in its own thread but this did not help.
At the beginning of drawing I make the appropriate context is current, and at the end I am calling swapbuffers(). I tried adding a glFinish to the end of the draw function, but there was no change.
Could it be that its the graphics card that is the problem? I am stuck using an integrated gpu that only has openGL 1.4.
Hopefully, I have provided enough detail that the answer to my problem can be found.
Its difficult telling what's wrong with what you do since you give so little detail. Here are some pointers which may help.
- before doing something in a context, make sure you make it the current one. If you want to pan two contexts, make the first one current, pan it and then make the second one current and pan it. These is no real reason why this should not work.
- If it looks like there is a timing problem, adding glFinish() at strategic places may help weed the problem out
- As should always be done, on occasions call glError() and see that everything went well.
- I'm not sure how this is done in the framework you're talking about but you should make sure that both contexts get a swapBuffers() call for every frame.
Invalidate doesn't force an immediate redraw. It marks the window invalid, and when the message queue runs out of other messages, a paint message will be created and processed. But that won't happen until you finish processing the current message and return to the main message loop, and it may be delayed even more than that.
Generally OpenGL animation is an exception to the rule of doing all drawing inside Control.OnPaint (or in a handler for the Control.Paint event).

Categories