Checking for GUI overload

  • Thread starter Thread starter Ty
  • Start date Start date
T

Ty

In a windows GUI-Application we visualize a datatable in a Grid. The
grid is updated by incoming data from the network.
From time to time it happens that there is too much information coming
in at once wich results in the gui becoming unresponsive for a minute
or so.

i've checked this and it's definitely because the gui-thread is not
fast enough to process the incoming messages realtime, therefore the
Windows MessageQueue is building up.

We can easily solve this by minimizing Refresh on the grid if too much
data is coming in. Right now we do exactly this by measuring the
incoming data. but we have to work with a hard value (xxx messsages/
second).

The messages are being "Post()"ed, meaning it's async. so the windows
messagequeue is building up. is it possible to get the current size of
this queue? so we could take action more dynamically. is there another
way of measuring responsiveness of a gui?

Thank you!
 
Ty said:
In a windows GUI-Application we visualize a datatable in a Grid. The
grid is updated by incoming data from the network.
From time to time it happens that there is too much information coming
in at once wich results in the gui becoming unresponsive for a minute
or so.

i've checked this and it's definitely because the gui-thread is not
fast enough to process the incoming messages realtime, therefore the
Windows MessageQueue is building up.

We can easily solve this by minimizing Refresh on the grid if too much
data is coming in. Right now we do exactly this by measuring the
incoming data. but we have to work with a hard value (xxx messsages/
second).

The messages are being "Post()"ed, meaning it's async. so the windows
messagequeue is building up. is it possible to get the current size of
this queue? so we could take action more dynamically. is there another
way of measuring responsiveness of a gui?

Not in .NET, no. But, you shouldn't need to.

IMHO, the solution to this sort of problem is to set a specific "refresh
rate" for the UI that is strictly time-based (500 ms minimum, 1000 ms or
more tends to work better). Then make sure the rest of the code is
capable of handling the throughput, including being able to update the
UI with however much data becomes available in the refresh period.

This is one of the few places where a polling strategy (use a timer,
inspect the current state of the data) can definitely work better than
an interrupt-driven one (invocation of data structure updates across
threads).

If even at 500-1000 ms, you find the UI can't keep up, then you are
simply trying to present too much information to the user at once.

There's no point in keeping the UI up-to-date if there's more
information there than a human being can process, and a single thread on
a modern PC can _definitely_ keep a properly-implemented UI up-to-date
every half second or so. So, if even after throttling the refreshes,
the UI still can't keep up with the data stream, then there's simply
more data coming in than is worthwhile to present to the user.

Without knowing more about your specific scenario, it's hard to suggest
what exactly is the right fix. Hopefully, just putting the UI update on
a timer rather than trying to marshal each and every operation across
the threads fixes the issue. But if not, you will have to look at just
what information you're trying to present to the user so quickly that
the UI thread can't even keep up.

Pete
 
FWIW, I agree with Pete's polling strategy. In the case where I worked on an
app like this the data wasn't been thrown at the UI - it was coming in from
the network and stored separately (not in a local DB, but it could have
been). The interface to the owning assembly was an enumerator, and the UI
called MoveNext when it was ready (and it wasn't a single UI item - it was a
batch of 100 or so). In C++ and COM it's an IENumxxx interface.
 
IMHO, the solution to this sort of problem is to set a specific "refresh
rate" for the UI that is strictly time-based (500 ms minimum, 1000 ms or
more tends to work better).  Then make sure the rest of the code is
capable of handling the throughput, including being able to update the
UI with however much data becomes available in the refresh period.

i'm not shure what you mean by that. is there some way to set this
refresh-rate? or are you talking about "manually" refreshing? how
would we do that?

and, of course, if these databursts happen, they present too much data
to the user. but we don't know when this happens, it's external data.
usually it just ticks in irregularly. but when the bursts happen,
there are a couple 1000 records in a second, locking the gui up.

we are using componentone truedbgrid. we noticed that if a datatable
is bound to the grid, insert-speed into the datatable drops to arround
300/s, from tenthousands a sec if not bound. if several grids show the
same datatable with different views, insert speed drops further, even
if the records are not visible (2 grids: 150row/s, 3 grids: 100row/s
etc, we have a open support ticket for this). the number of open grids
depends on the user of course, that's why it's hard to know when too
much data is coming in.

just to elaborate a bit more: the grid/data in question shows new rows
on the bottom, continually moving to last record. the crux is, that
only SuspendDatabinding() resolves this issue. SuspendLayout() doesnt
help much. if we SuspendDatabinding() the grid jumps to the top.
currently we jump back to last, if the bursts are over. it simply
doesn't look very nice. so Suspending/Resuming every 500ms or so is
unfortunatly not good option.
 
OK, i'm probably gonna rot in developer-hell for this...
i figured that one way of measuring overload of gui thread would be to
measure the time a invoke takes to execute (because of the queue
building up). based on this i made a class that continuously checks
this delay and fires an event on gui-overload and one if the delay is
low again. it actually does work very well as plugin in our
application (though it's certainly not in production. just foolin'
arround a bit...)

if the delay is higher than 50ms, it will fire OnOverload, if delay
has been low for more than 2 seconds, it will fire OnResume.

this is how i instanciate the class from gui:

Watchman watchman = new Watchman(this);

//to visualize delay
watchman.OnDelayChanged += watch_OnDelayChanged;
//taking action to easy up on display of data
watchman.OnOverload += watch_OnOverload;
//resuming full display of data
watchman.OnResume += watch_OnResume;

watchman.Start();


and here's the WatchMan (yes, it's not finished. clean stop of the
WatcherThread is missing and more):

public class Watchman
{
Thread _watchThread;
Control _invoker;
bool _overload = false;
DateTime _lowSince;

delegate void InvokeDelegate(object state);

public int Threshold { get; set; }
public int DelayResume { get; set; }

public delegate void DelayChangedDelegate(object sender, int
delayMs);
public event DelayChangedDelegate OnDelayChanged;

public event EventHandler OnOverload;
public event EventHandler OnResume;

public Watchman(Control invoker)
{
_invoker = invoker;
IntPtr ptr = _invoker.Handle; //just making shure the
invoker has a handle
Threshold = 30; //delay of control-message in ms
DelayResume = 2000; //delay of resume in ms
}

public void Start()
{
ParameterizedThreadStart ts = new ParameterizedThreadStart
(Watcher);
_watchThread = new Thread(ts);
_watchThread.Start();
}

void Checker(object state)
{
DateTime sendDate = (DateTime)state;
TimeSpan span = DateTime.Now - sendDate;
int delay = Convert.ToInt32(span.TotalMilliseconds);

OnDelayChanged(this, delay);

if (delay >= Threshold && !_overload)
{
_overload = true;
_lowSince = DateTime.MinValue;

if(OnOverload!=null)
OnOverload(this, null);
}
else if (delay < Threshold && _overload)
{
if (_lowSince == DateTime.MinValue)
_lowSince = DateTime.Now;
else
{
if (_lowSince.AddMilliseconds(DelayResume) <
DateTime.Now)
{
_overload = false;
if(OnResume!=null)
OnResume(this, null);
}
}
}
}

void Watcher(object state)
{
InvokeDelegate d = Checker;
while (true)
{
_invoker.Invoke(d, DateTime.Now);
Thread.Sleep(200);
}
}
}
 
Ty said:
OK, i'm probably gonna rot in developer-hell for this...

Probably. You seem to have added quite a bit of additional complexity
to what should not be that complex a problem. That's usually a
maintenance "no-no".
i figured that one way of measuring overload of gui thread would be to
measure the time a invoke takes to execute (because of the queue
building up).

In addition to being unnecessarily complex, you are also subject to
significant experimental error. There are all sorts of reasons a call
to Control.Invoke() might be delayed, an excess of messages to process
being only one of them.
based on this i made a class that continuously checks
this delay and fires an event on gui-overload and one if the delay is
low again. it actually does work very well as plugin in our
application (though it's certainly not in production. just foolin'
arround a bit...)

if the delay is higher than 50ms, it will fire OnOverload, if delay
has been low for more than 2 seconds, it will fire OnResume. [...]

And you do what in response to that information? And that "what" is
inappropriate in the general case because why?

The idea that there's even any benefit to presenting a couple thousand
data items to the user in less than a second is fairly ridiculous. A
human being can't even keep more than seven or eight things in
short-term memory, never mind process two thousand pieces of information
in a second.

I'm sure there is a much simpler approach that would work just as well
for your needs. It's hard to know for sure, because we're lacking some
detail in the exact nature of the problem. But it seems very likely.

Pete
 
In a windows GUI-Application we visualize a datatable in a Grid. The
grid is updated by incoming data from the network.
From time to time it happens that there is too much information coming
in at once wich results in the gui becoming unresponsive for a minute
or so.

i've checked this and it's definitely because the gui-thread is not
fast enough to process the incoming messages realtime, therefore the
Windows MessageQueue is building up.

We can easily solve this by minimizing Refresh on the grid if too much
data is coming in. Right now we do exactly this  by measuring the
incoming data. but we have to work with a hard value (xxx messsages/
second).

The messages are being "Post()"ed, meaning it's async. so the windows
messagequeue is building up. is it possible to get the current size of
this queue? so we could take action more dynamically. is there another
way of measuring responsiveness of a gui?

Thank you!

The real question is, why are you doing os much in the GUI thread?
It's hard to know if this is even necessary without know more about
your specific scenario, but GUI threads should be doing exactly one
thing: Displaying information. Are you posting every single message
to the GUI thread and then having the GUI thread be responsible for
aggregating the data into a more presentable format, and *then*
displaying the data? If so, what you should really be doing is having
the worker thread be the one aggregating / summarizing / analyzing the
data from all these messages and storing it in a format that the GUI
thread can easily and quickly dump into your display element. The GUI
thread then has no idea what the messages even look like, but at a
fixed interval it simply polls this data structure (using a lock) and
says "give me all the stuff you have so far and reset". GUI thread
ends up with an already summarized collection of data that all it has
to do is insert directly into the control.

If this still is not sufficient and the message queue is still growing
faster than you can consume it, then it implies you cannot even
process (analyze) the data faster than the data is coming in, at which
point you have more serious problems to think about, and might
consider augmenting the messages with priorities so you can easily
know which ones you can be thrown away if necessary.
 
The real question is, why are you doing os much in the GUI thread?
It's hard to know if this is even necessary without know more about
your specific scenario, but GUI threads should be doing exactly one
thing: Displaying information.  Are you posting every single message
to the GUI thread and then having the GUI thread be responsible for
aggregating the data into a more presentable format, and *then*
displaying the data?  If so, what you should really be doing is having
the worker thread be the one aggregating / summarizing / analyzing the
data from all these messages and storing it in a format that the GUI
thread can easily and quickly dump into your display element.  The GUI
thread then has no idea what the messages even look like, but at a
fixed interval it simply polls this data structure (using a lock) and
says "give me all the stuff you have so far and reset".  GUI thread
ends up with an already summarized collection of data that all it has
to do is insert directly into the control.

If this still is not sufficient and the message queue is still growing
faster than you can consume it, then it implies you cannot even
process (analyze) the data faster than the data is coming in, at which
point you have more serious problems to think about, and might
consider augmenting the messages with priorities so you can easily
know which ones you can be thrown away if necessary.

well, believe me, it's processing exactly what it has to and it's
showing what the customers have to see. of course there are points to
improve (eg charting in addition to raw data, working on that). the
application is on the financial market and one of the many things it
does is showing all (if the user wishes) incoming trades of an
exchange. he filters them on the fly (eg seing his own stuff),
everything has to be as realtime and dynamic as possible. he can open
as many forms with different or same filters as he wishes.
so at some point, there will always be a performance bottleneck,
depending on how the user is working and on the hardware he uses. our
competitors are having the exact same problems.
it's actually a request of the management to somehow indicate to the
user about how much he's taxing the gui, it's not a priority though.
that's why i was asking about getting the queue-size and played with
this watchman class, because it could indicate exactly this.
But, i can't say i like either, it was "fun" to try though.

I'm still not shure how this "polling" mechanism in our scenario is
supposed to work. do you have any examples available?

Thank you!
 
well, believe me, it's processing exactly what it has to and it's
showing what the customers have to see. of course there are points to
improve (eg charting in addition to raw data, working on that). the
application is on the financial market and one of the many things it
does is showing all (if the user wishes) incoming trades of an
exchange. he filters them on the fly (eg seing his own stuff),
everything has to be as realtime and dynamic as possible. he can open
as many forms with different or same filters as he wishes.
so at some point, there will always be a performance bottleneck,
depending on how the user is working and on the hardware he uses. our
competitors are having the exact same problems.
it's actually a request of the management to somehow indicate to the
user about how much he's taxing the gui, it's not a priority though.
that's why i was asking about getting the queue-size and played with
this watchman class, because it could indicate exactly this.
But, i can't say i like either, it was "fun" to try though.

I'm still not shure how this "polling" mechanism in our scenario is
supposed to work. do you have any examples available?

Thank you!

GUI thread has a timer that fires every 500 milliseconds (for
example). in the handler for that timer, you say something like

RawData data = dataCollector.FetchAndResetData();

foreach (DataEntry entry i data)
{
//Add the info stored in entry into the grid
}


class DataCollector
{
private object dataLock = new object();
private RawData data;

public void FetchAndResetData()
{
RawData data;
lock(dataLock)
{
data = this.data;
this.data = new RawData();
}

return data;
}


public void AcceptMessage(DataMessage message)
{
lock(dataLock)
{
this.data.totalMessages++;

//etc, process the data from this particular message,
updating fields of the RawData instance, whatever
//it might look like. In the simplest (but probably
worst case), it's just a list of DataMessage objects.
//But you want to do as much work as possible here, and
have the RawData class be a format that is as
//easy as possible for the GUI thread to digest.
}
}
}
 
Back
Top